import FillArea from "../common/fillarea";
import { Centers } from "../common/interfaces";
import Repeater from "../common/repeater";
import Utils from "../common/utils";
import { Point } from "../core/interface";
import Layer from "../core/layer";
import Rect from "../core/rect";
import MyFloodFill from './myfloodfill';

const TAG = 'CheckEditLayer';

export default class CheckEditLayer extends Layer {
  raw: HTMLImageElement;
  page: HTMLImageElement;
  pagePixels: Uint32Array;
  map: HTMLImageElement;
  mapPixels: Uint32Array;
  centers: Centers;
  areaMap: {};
  R: number = 1; //小区块半径阈值
  N: number = 10; //最多允许的小区块个数

  smallAreas: FillArea[] = []; //小区块列表
  noCenterAreas: FillArea[] = []; //未计算出中心点的area

  noCloseLines: Rect[] = [];  //未闭合线条 

  colorCanvas: HTMLCanvasElement; // 将小区块上色的图层
  markCanvas: HTMLCanvasElement; // 将小区快圈起来的图层

  showMark: boolean = true;  // 显示标记
  lineMark: boolean = true; // 显示闭合线条提示
  showPage: boolean = true; // 显示线稿
  showMap: boolean = false; // 显示map

  sortedAreaList: FillArea[] = [];

  floodImgs: HTMLImageElement[] = [];
  _showFloodIdx: number = 100;
  set showFloodIdx(idx: number) {
    this._showFloodIdx = idx;
    this.invalidate();
  }
  get showFloodIdx() {
    return this._showFloodIdx;
  }

  result: number = 0; // 0: checking... 1-check success, 2-check failed

  override get defaultToolKey(): string { return 'pan'; }

  constructor(raw: HTMLImageElement, page: HTMLImageElement, map: HTMLImageElement, centers: Centers, R: number, N: number) {
    super(page.width, page.height);

    this.raw = raw;
    this.page = page;
    this.map = map;
    this.centers = centers;
    this.R = R || this.R;
    this.N = N || this.N;
    this.result = 0;

    this.colorCanvas = Utils.createCanvas(this.width, this.height);
    this.markCanvas = Utils.createCanvas(this.width, this.height);

    if (this.centers) {
      this.init();
    }

  }

  init() {

    this.pagePixels = new Uint32Array(Utils.getImageData(this.page).data.buffer);

    this.mapPixels = new Uint32Array(Utils.getImageData(this.map).data.buffer);
    this.createAreaMap();
    this.sortedAreaList = Object.keys(this.areaMap).map(key => this.areaMap[key])
    .sort((a, b) => {
      return a.count - b.count;
    })

    let keys = Object.keys(this.centers);
    let smalls = keys.filter(a => this.centers[a].radius <= this.R);
    let smallAreas = smalls.map(a => this.areaMap[a]);
    smallAreas = smallAreas.filter(a => a);

    this.noCenterAreas = Object.keys(this.areaMap).filter(a => !this.centers[a]).map(a => this.areaMap[a]);
    this.smallAreas = this.noCenterAreas.concat(smallAreas);
    this.smallAreas.sort((a, b) => { return a.count - b.count });

    if (this.smallAreas.length > this.N) {
      this.result = 2;
    } else {
      this.result = 1;
    }

    this.noCloseLines = MyFloodFill.lineCloseCheck(this.page);

    console.log("centers count: " + Object.keys(this.centers).length
      + ", area count: " + Object.keys(this.areaMap).length 
      + ", no center count: " + this.noCenterAreas.length
      + ", small area: " + this.smallAreas.length);

    this.initHint();

    this.invalidate();

    // // 生成floodfill各个过程的结果
    // setTimeout(() => {
    //   this.makeFloodFill();
    // }, 1000);
  }

  initHint() {
    // init colorCanvas
    let ctx = this.colorCanvas.getContext('2d');
    ctx.clearRect(0, 0, this.width, this.height);
    let imgData = ctx.getImageData(0, 0, this.colorCanvas.width, this.colorCanvas.height);
    let pixels = new Uint32Array(imgData.data.buffer);
    this.smallAreas.forEach((area: FillArea) => {
      let ai, aw, i, x, y;
      let areaPixelCount = area.width() * area.height();
      aw = area.width();
      for (ai = 0; ai < areaPixelCount; ai++) {
        x = area.left + ai % aw;
        y = area.top + Math.floor(ai / aw);
        i = y * this.width + x;
        if (this.mapPixels[i] == area.color) {
          pixels[i] = area.color;
        }
      }
    })
    ctx.putImageData(imgData, 0, 0);

    // init markCanvas
    ctx = this.markCanvas.getContext('2d');
    ctx.clearRect(0, 0, this.width, this.height);
    ctx.strokeStyle = 'orange';
    ctx.lineWidth = 2;
    
    this.smallAreas.forEach(a => {
      let rect = new Rect(a.left, a.top, a.right, a.bottom);
      ctx.strokeRect(rect.left, rect.top, rect.width(), rect.height());      
    })

    ctx.strokeStyle = 'red';
    this.noCenterAreas.forEach(a => {
      let rect = new Rect(a.left, a.top, a.right, a.bottom);
      ctx.strokeRect(rect.left, rect.top, rect.width(), rect.height());      
    })

    if (this.lineMark) {
      ctx.lineWidth = 2;
      ctx.strokeStyle = 'blue';
      this.noCloseLines.forEach(a => {
        ctx.strokeRect(a.left, a.top, a.width(), a.height());
      })
    }

  }


  update(page?: HTMLImageElement, map?: HTMLImageElement, raw?: HTMLImageElement, centers?: Centers) {
    this.page = page || this.page;
    this.map = map || this.map;
    this.raw = raw || this.raw;

    let centerChange = false;
    if (centers && this.centers != centers) {
      this.centers = centers;
      centerChange = true;
    }
    if (centerChange) {  // 中心点更新完了才重新初始化
      this.init();
    } else {
      this.result = 0;
    }
  }

  /**
   * Draw this layer.
   * @Override Layer#draw(ctx)
   */
  override draw(ctx) {
    ctx.imageSmoothingEnabled = this.smoothing;

    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, this.width, this.height);

    // for test
    if (this.showFloodIdx < this.floodImgs.length) {
      ctx.drawImage(this.floodImgs[this.showFloodIdx], 0, 0, this.page.width, this.page.height, 0, 0, this.width, this.height);
      if (this.showPage) {
        if (this.raw) {
          ctx.drawImage(this.raw, 0, 0, this.page.width, this.page.height, 0, 0, this.width, this.height);
        } else {
          ctx.drawImage(this.page, 0, 0, this.page.width, this.page.height, 0, 0, this.width, this.height);
        }
      }
      if (this.showMark) {
        ctx.drawImage(this.markCanvas, 0, 0);
      }
      return;
    }

    if (this.showMap) {
      ctx.drawImage(this.map, 0, 0, this.page.width, this.page.height, 0, 0, this.width, this.height);
    }

    if (this.showMark) {
      ctx.drawImage(this.colorCanvas, 0, 0);
    }

    if (this.showPage) {
      if (this.raw) {
        ctx.drawImage(this.raw, 0, 0, this.page.width, this.page.height, 0, 0, this.width, this.height);
      } else {
        ctx.drawImage(this.page, 0, 0, this.page.width, this.page.height, 0, 0, this.width, this.height);
      }
    }

    if (this.showMark) {
      ctx.drawImage(this.markCanvas, 0, 0);
    }

    if (this.blinkCanvas) {
      ctx.drawImage(this.blinkCanvas, 0, 0);
    }

  }

  
  /**
   * Create area map from map pixels.
   */
   createAreaMap() {
    let width = this.width;
    let height = this.height;
    let floodArr = this.mapPixels;
    let areaMap = {};
    let i, x, y, color;
    for (i = 0; i < floodArr.length; i++) {
      color = floodArr[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);
      }
    }
    this.areaMap = areaMap;
  }

  toggleMark() {
    this.showMark = !this.showMark;
    this.invalidate();
  }

  toggleLineMark() {
    this.lineMark = !this.lineMark;
    this.initHint();
    this.invalidate();
  }

  togglePage() {
    this.showPage = !this.showPage;
    this.invalidate();
  }

  toggleMap() {
    this.showMap = !this.showMap;
    this.invalidate();
  }

  toggleParse() {
    if (this.showFloodIdx == 100) {
      this.showPage = false;
      this.showFloodIdx = 0;
    } else {
      this.showFloodIdx = 100;
      this.showPage = true;
    }
  }

  getWorkBlob(): Promise<Blob> {
    let canvas = Utils.createCanvas(this.width, this.height);
    let ctx = canvas.getContext('2d');
    ctx.drawImage(this.colorCanvas, 0, 0);
    if (this.raw) {
      ctx.drawImage(this.raw, 0, 0, this.page.width, this.page.height, 0, 0, this.width, this.height);
    } else {
      ctx.drawImage(this.page, 0, 0, this.page.width, this.page.height, 0, 0, this.width, this.height);
    }
    ctx.drawImage(this.markCanvas, 0, 0);
    return new Promise((done, reject) => { canvas.toBlob(b => { done(b) }) });
  }
  

  blinkCanvas: HTMLCanvasElement;
  repeater: Repeater;
  blinkByAreas(areas: FillArea[]) {
    //Stop the previous running repeater.
    if (this.repeater) this.repeater.cancel();

    //没有考虑scale
    let _blinkCanvas = Utils.createCanvas(this.width, this.height);
    let ctx = _blinkCanvas.getContext('2d');
    ctx.clearRect(0, 0, this.width, this.height);

    let imgData = ctx.getImageData(0, 0, _blinkCanvas.width, _blinkCanvas.height);
    let pixels = new Uint32Array(imgData.data.buffer);
    //console.log(TAG, 'pixels=', pixels);

    areas.forEach((area: FillArea) => {
      let ai, aw, i, x, y;
      let areaPixelCount = area.width() * area.height();
      aw = area.width();
      for (ai = 0; ai < areaPixelCount; ai++) {
        x = area.left + ai % aw;
        y = area.top + Math.floor(ai / aw);
        i = y * this.width + x;
        if (this.mapPixels[i] == area.color) {
          pixels[i] = 0xffffffff;
        }
      }
    })
    ctx.putImageData(imgData, 0, 0);

    let rect = new Rect(
      Math.min.call(null, ...areas.map(a => a.left)),
      Math.min.call(null, ...areas.map(a => a.top)),
      Math.max.call(null, ...areas.map(a => a.right)),
      Math.max.call(null, ...areas.map(a => a.bottom))
    );

    ctx.strokeStyle = '#ffffff';
    ctx.lineWidth = 2;
    ctx.strokeRect(rect.left, rect.top, rect.width(), rect.height());

    this.blinkCanvas = _blinkCanvas;
    this.invalidate();

    this.repeater = new Repeater(() => {
      this.blinkCanvas = this.blinkCanvas ? null : _blinkCanvas;
      this.invalidate();
    }, () => {
      this.blinkCanvas = null;
      this.repeater = null;
      this.invalidate();
    }, 5, 300).start();

    return rect;
  }


  /**
   * 生成floodfill各个阶段处理的结果图（调试用）
   */
  async makeFloodFill() {
    for (let i = 0; i <= 7; i++) {
      let img = await MyFloodFill.createMapImage(this.raw, i);
      this.floodImgs.push(img);
    }
  }

}
