import { validateOrder } from "./color-order";
import { ETrace } from "./etrace2";
import FillArea from "./fillarea";
import { AreaMap, Centers, ColorMap } from "./interfaces";
import { Potrace } from "./potrace";
import Utils from "./utils";


export default class SVGGenerator {
  mapData: ImageData;
  mapPixels: Uint32Array;
  pageData: ImageData;
  pagePixels: Uint32Array;
  orgData: ImageData;
  orgPixels: Uint32Array;

  areaMap: AreaMap;

  areaCanvas: HTMLCanvasElement;
  areaCtx: CanvasRenderingContext2D;
  colorBlack: number;
  colorRed: number;

  public progress : number;
  public done : boolean = false;

  etrace: ETrace;

  blobs: {} = {};


  canvas: HTMLCanvasElement;
  ctx: CanvasRenderingContext2D;

  constructor(private map: HTMLImageElement, private page: HTMLImageElement, private org: HTMLImageElement,
    private colorMap: ColorMap, private centers: Centers, private colorOrder: any) {
    
    this.pageData = Utils.getImageData(this.page);
    this.pagePixels = new Uint32Array(this.pageData.data.buffer);
    this.mapData = Utils.getImageData(this.map);
    this.mapPixels = new Uint32Array(this.mapData.data.buffer);
    this.areaMap = SVGGenerator.createAreaMap(this.mapPixels, this.map.width);

    this.areaCanvas = Utils.createCanvas(this.map.width, this.map.height);
    this.areaCtx = this.areaCanvas.getContext('2d');

    this.colorBlack = Utils.getColorInteger("#000000");
    this.colorRed = Utils.getColorInteger("#FF0000");

    this.canvas = Utils.createCanvas(this.map.width, this.map.height);
    this.ctx = this.canvas.getContext('2d');
    this.ctx.clearRect(0, 0, this.map.width, this.map.height);

  }

  async process(flag: number = 0) : Promise<{}> {

    let color: string;
    let p, path, area, svg;
    let output = [];
    let output2 = [];
    let output3 = [];
    let output4 = [];

    output.push(`<svg id="svg" version="1.1" width="${this.map.width}" height="${this.map.height}" viewBox="0,0,${this.map.width},${this.map.height}" xmlns="http://www.w3.org/2000/svg">`);
    output.push(`\t<g id="areas">`);

    output2.push(`<svg id="svg" version="1.1" width="${this.map.width}" height="${this.map.height}" viewBox="0,0,${this.map.width},${this.map.height}" xmlns="http://www.w3.org/2000/svg">`);
    output2.push(`\t<g id="areas">`);
    
    output3.push(`<svg id="svg" version="1.1" width="${this.map.width}" height="${this.map.height}" viewBox="0,0,${this.map.width},${this.map.height}" xmlns="http://www.w3.org/2000/svg">`);
    output3.push(`\t<g id="areas">`);

    output4.push(`<svg id="svg" version="1.1" width="${this.map.width}" height="${this.map.height}" viewBox="0,0,${this.map.width},${this.map.height}" xmlns="http://www.w3.org/2000/svg">`);
    output4.push(`\t<g id="areas">`);

    let colorMap = this.colorMap
    let colorOrder = this.colorOrder;
    let colorMapKeys = Object.keys(colorMap);
    // 判断colorOrder是否已经涵盖了colorMap里出现的颜色, 如果涵盖则采用order, 否则不采用
    if (validateOrder(colorMap, colorOrder)) {  // 根据colorOrder 给colorMap做个排序, 这个顺序将与生成的svg息息相关
      colorMapKeys.sort((a, b) => {
        let aidx = colorOrder.indexOf(colorMap[a].color);
        let bidx = colorOrder.indexOf(colorMap[b].color);
        return aidx - bidx;
      });
    }

    this.etrace = new ETrace(this.mapPixels, this.map.width, this.map.height);
    

    for (let i = 0; i < colorMapKeys.length; i++) {
      area = this.areaMap[colorMapKeys[i]];
      if (!area || !this.centers || !this.centers[area.color]) continue;
      color = Utils.colorToHex(this.colorMap[area.color].cssColor);
      path = this.getAreaPath(area, color, 'curve');

      if (!path) continue; 
      
      output.push(`\t\t${path}`);
      output2.push(`\t\t${path}`);

      path = this.getAreaPath(area, color, 'line');
      output3.push(`\t\t${path}`);
      output4.push(`\t\t${path}`);

      
      // console.log(i / colorMapKeys.length, path);

      this.progress = i / colorMapKeys.length;
      await Utils.delay(1);
    }
    
    // if (flag  == 1) { // 对于未涂色的区块，也生成相应的area path， fill='none'
    //   let unColoredAreas = Object.keys(this.areaMap).filter(a => { return !this.colorMap[a]});
    //   for (let i = 0; i < unColoredAreas.length; i++) {
    //     let area = this.areaMap[unColoredAreas[i]];
    //     if (!this.centers || !this.centers[area.color]) continue;
    //     let path = this.getAreaPath2(area, 'none', 'curve');
    //     output.push(`\t\t${path}`);
    //   }
    // }

    output.push(`\t</g>`);
    output2.push(`\t</g>`);
    output3.push(`\t</g>`);
    output4.push(`\t</g>`);


    p =   new Potrace(this.org || this.page);
    path = p.getSVG(1, null, true, "#000000");

    output.push(`\t<g id="outline">`);
    output.push(`\t\t${path}`);
    output.push(`\t</g>`);

    output3.push(`\t<g id="outline">`)
    output3.push(`\t\t${path}`);
    output3.push(`\t</g>`);


    output.push(`</svg>`);
    output2.push(`</svg>`);
    output3.push(`</svg>`);
    output4.push(`</svg>`);


    svg = output.join('\n');
    this.blobs['svg1'] = new Blob([svg], {type : "image/svg+xml"});
    svg = output2.join('\n');
    this.blobs['svg2'] = new Blob([svg], {type : "image/svg+xml"});
    svg = output3.join('\n');
    this.blobs['svg3'] = new Blob([svg], {type : "image/svg+xml"});
    svg = output4.join('\n');
    this.blobs['svg4'] = new Blob([svg], {type : "image/svg+xml"});

    this.blobs['contour'] = await this.getContourBlob();
    // this.blobs['hcontour'] = await this.getHContourBlob();
    // this.blobs['vcontour'] = await this.getVContourBlob();
    

    this.done = true;

    // return new Blob([svg], {type : "image/svg+xml"});
    return this.blobs;
  }

  private getAreaPath(area: FillArea, color: string = 'none', mode: string = 'curve'): string {
    let svg = '';

    svg = this.etrace.getSVG(area.color, null, true, color, mode);

    if (svg.includes(`d=""`)) {
      return null;
    }

    //center
    let center =  this.centers[area.color];
    let id = `${area.color}`;
    let c = `${center.x},${center.y},${center.radius.toFixed(5)}`;
    svg = svg.replace('path', `path id="${id}" c="${c}"`);

    return svg;
  }


  private canvasToBlob(canvas: HTMLCanvasElement): Promise<Blob> {
    return new Promise((done, reject) => {
      canvas.toBlob((blob) => {
        done(blob);
      }, 'image/png');
    })
  }

  /**
   * Create area map from map pixels.
   */
  public static createAreaMap(pixels: Uint32Array, width: number): AreaMap {
    let areaMap = {};
    let i: number, x: number, y: number, color: number;
    for (i = 0; i < pixels.length; i++) {
      color = pixels[i];
      if (color == 0) continue;
      x = i % width;
      y = Math.floor(i / width);
      if (areaMap[color]) {
        areaMap[color].addPoint(x, y);
      } else {
        areaMap[color] = new FillArea(color, x, y);
      }
    }
    return areaMap;
  }


  private randomColor() {
    var color = new Uint8Array([0, 0, 0, 255]);
    for (var i = 0; i < 3; i++) {
      color[i] = Math.floor(Math.random() * 256);
    }
    var u32 = new Uint32Array(color.buffer);
    return u32[0];
  }



  async getContourBlob() {
    let w = this.map.width + 1;  // 注意下，因etrace会对map扩展+1， 所以这里+1
    let h = this.map.height + 1;
    let intersectMap = this.etrace.intersectMap;
    this.ctx.clearRect(0, 0, w, h);
    let imageData = this.ctx.getImageData(0, 0, w, h);
    let pixels = new Uint32Array(imageData.data.buffer);
    let keys = Object.keys(intersectMap);
    for (let key of keys) {
      let contours = intersectMap[key];
      let color = this.randomColor();
      for (let p of contours) {
        let sp = p.split('-'); 
        // let x = Math.floor(+sp[0]), y = Math.floor(+sp[1]);
        let x = Math.round(+sp[0]), y = Math.round(+sp[1]);
        let idx = y * w + x;
        pixels[idx] = color;
      }
    }

    let poSet = this.etrace.poSet;
    for (let idx of poSet) {
      pixels[idx] = this.colorBlack;
    }
    this.ctx.putImageData(imageData, 0, 0);
    let blob = await this.canvasToBlob(this.canvas);
    return blob;
  }


  async getHContourBlob() {
    this.ctx.clearRect(0, 0, this.map.width, this.map.height);
    let imageData = this.ctx.getImageData(0, 0, this.map.width, this.map.height);
    let pixels = new Uint32Array(imageData.data.buffer);

    for (let index of this.etrace.hContour) {
      pixels[index] = this.colorBlack;
    }

    this.ctx.putImageData(imageData, 0, 0);
    let blob = await this.canvasToBlob(this.canvas);
    return blob;
  }

  async getVContourBlob() {
    this.ctx.clearRect(0, 0, this.map.width, this.map.height);
    let imageData = this.ctx.getImageData(0, 0, this.map.width, this.map.height);
    let pixels = new Uint32Array(imageData.data.buffer);

    for (let index of this.etrace.vContour) {
      pixels[index] = this.colorBlack;
    }

    this.ctx.putImageData(imageData, 0, 0);
    let blob = await this.canvasToBlob(this.canvas);
    return blob;
  }

}