import FillArea from "../common/fillarea";
import {  ColorMap, Mark } from "../common/interfaces";
import Utils from "../common/utils";
import { Point } from "../core/interface";
import Layer from "../core/layer";
import Rect from "../core/rect";
import MarkTool from "./mark-tool";

const TAG = 'MarkEditLayer';

export default class MarkEditLayer extends Layer {
  output: HTMLCanvasElement;
  outputCtx: CanvasRenderingContext2D;

  raw: HTMLImageElement;
  page: HTMLImageElement;
  map: HTMLImageElement;
  mapPixels: Uint32Array;
  colorMap: ColorMap;

  areaMap: any = [];
  areaList: any[];

  undoList: any[] = [];
  redoList: any[] = [];
  cacheList: any[] = [];


  marks: Mark[]; // 标记点

  innerMarks: InnerMark[] = [];  // 仅供内部使用

  override get defaultToolKey(): string { return 'mark-edit-mark'; }

  constructor(raw: HTMLImageElement, page: HTMLImageElement, map: HTMLImageElement, colorMap: ColorMap, marks: Mark[]) {
    super(page.width, page.height);

    this.raw = raw;
    this.page = page;
    this.map = map;
    this.colorMap = colorMap;

    this.marks = marks;

    console.log("marks:", marks);

    if (this.marks && this.marks.length > 0) {
      this.innerMarks = this.marks.map(m => { return {x: m.x, y: m.y, radius: m.radius, note: m.note, status: 0} });
    }

    this.init();


    this.addTool(new MarkTool(this));

  }

  init() {
    this.output = Utils.createCanvas(this.width, this.height);
    this.outputCtx = this.output.getContext('2d');
    this.outputCtx.fillStyle = 'white';
    this.outputCtx.fillRect(0, 0, this.width, this.height);

    this.mapPixels = new Uint32Array(Utils.getImageData(this.map).data.buffer);
    this.createAreaMap();

    this.drawToOutput();

    // undo redo support.
    this.undoList = [];
    this.redoList = [];
    this.cacheList = [];
  }

  drawToOutput() {
    console.time('drawToOutput');
    this.outputCtx.fillStyle = 'white';
    this.outputCtx.fillRect(0, 0, this.width, this.height);

    //fast create output by map
    let outData = this.outputCtx.getImageData(0, 0, this.output.width, this.output.height)
    let outPixels = new Uint32Array(outData.data.buffer);
    let areaIndex;
    for (var i = 0; i < outPixels.length; i++) {
      areaIndex = this.mapPixels[i];
      if (this.colorMap[areaIndex]) {
        outPixels[i] = this.colorMap[areaIndex].color;
      }
    }
    this.outputCtx.putImageData(outData, 0, 0);
    console.timeEnd('drawToOutput');
  }


  /**
   * Create area map from map pixels.
   */
  createAreaMap() {
    let width = this.width;
    let height = this.height;
    let floodArr = this.mapPixels;
    let areaMap = {};
    let i: number, x: number, y: number, color: number;
    for (i = 0; i < floodArr.length; i++) {
      color = floodArr[i];
      if (!color) continue;
      x = i % width;
      //y = parseInt(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;
    this.areaList = Object.keys(areaMap).map(key => {
      return areaMap[key];
    });
  }

  update(page?: HTMLImageElement, raw?: HTMLImageElement, map?: HTMLImageElement, colorMap?: ColorMap) {
    this.raw = raw || this.raw;
    this.page = page || this.page;
    this.map = map || this.map;
    
    this.invalidate();
  }


  updateing = false;
  editUpdate() {
    this.marks = this.innerMarks.filter(m => m.radius > 0)
      .map(m => {
      return {x: m.x, y: m.y, radius: m.radius, note: m.note};
    });
    // 控制下触发频率
    if (this.updateing) return;
    this.updateing = true;
    setTimeout(() => {
      this.trigger('edit-update');
      this.updateing = false;
    }, 1000);
  }


  get curMarkIdx(): number {
    return this.getSelectedMarkIdx();
  }
  set curMarkIdx(idx: number) {
    if (idx >= 0 && idx < this.innerMarks.length) {
      this.selectMarkIdx(idx);
    }
  }

  // 获取标注文字
  getMarkNote(idx: number) {
    if (idx >= 0 && idx < this.innerMarks.length) {
      let mark = this.innerMarks[idx];
      return mark.note;
    }
    return null;
  }

  // 设置标注文字
  setMarkNote(idx: number, note: string) {
    if (idx >= 0 && idx < this.innerMarks.length) {
      this.innerMarks[idx].note = note;
      this.marks[idx].note = note;
      this.trigger('edit-update');
    }
  }

  /**
   * 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);

    ctx.drawImage(this.output, 0, 0, this.output.width, this.output.height, 0, 0, this.width, this.height);

    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);
    }

    this.drawMarks(ctx);

  }


  // 生成标注效果图
  getMarkBlob(): Promise<Blob> {
    let scale = 2;
    let canvas = Utils.createCanvas(this.width / scale, this.height / scale);  // 压缩下，变为原来的一半大小即可，节省空间
    let ctx = canvas.getContext('2d');

    ctx.drawImage(this.output, 0, 0, this.output.width, this.output.height, 0, 0, canvas.width, canvas.height);

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

    this.drawMarksOut(ctx, scale);
    return new Promise((done, reject) => { canvas.toBlob(b => { done(b) }) });
  }
  
  
  // 创建圈圈标记
  createMark(x: number, y: number, radius: number): InnerMark {
    let mark = {x, y, radius, note: '', status: 0};
    this.innerMarks.push(mark);
    this.selectMark(mark);
    this.invalidate();
    this.editUpdate();
    return mark;
  }

  // 移动圈圈
  moveMark(mark: InnerMark, x: number, y: number) {
    mark.x = x;
    mark.y = y;
    this.editUpdate();
    this.invalidate();
  }

  // 调整圈圈大小
  resizeMark(mark: InnerMark, radius: number) {
    mark.radius = radius;
    this.editUpdate();
    this.invalidate();
  }

  // 选中某个圈圈
  selectMark(mark: InnerMark) {
    for (let m of this.innerMarks) {
      if (m == mark) {
        m.status = 1;
      } else {
        m.status = 0;
      }
    }
    this.invalidate();
  }

  // 选中指定下标的圈圈
  selectMarkIdx(idx: number) {
    if (idx >= 0 && idx < this.innerMarks.length) {
      let mark = this.innerMarks[idx];
      this.selectMark(mark);
    }
  }

  // 全部置为非选中
  unselectMarks() {
    this.innerMarks.forEach(m => {
      m.status = 0;
    })
    this.invalidate();
  }

  // 获取当前选中
  getSelectedMark(): InnerMark {
    return this.innerMarks.find(m => m.status == 1)
  }

  // 获取当前选中圈圈下标
  getSelectedMarkIdx(): number {
    return this.innerMarks.findIndex(m => m.status == 1)
  }

  // 删除指定
  deleteSelectedMark() {
    let idx = this.innerMarks.findIndex(m => m.status == 1);
    if (idx >= 0) {
      this.innerMarks.splice(idx, 1);
      this.editUpdate();
      this.invalidate();
    }
  }

  // 判断某点是否落在某个圈圈标记内
  checkInMarks(x: number, y: number): InnerMark {
    let dx, dy;
    for (let mark of this.innerMarks) {
      dx = mark.x - x;
      dy = mark.y - y;
      if (mark.radius * mark.radius >= dx * dx + dy * dy) { // in circle
        return mark;
      }
    }
    return null;
  }

  // 判断某点落在哪个象限（正方形选框之内和mark圈圈之外的4个角，右上/左上/左下/右下分别对应1/2/3/4象限，对应方位东北/西北/西南/东南）
  checkQuadrant(mark: InnerMark, x: number, y: number): number {
    let dx = mark.x - x
    let dy = mark.y - y;
    if (mark.radius * mark.radius >= dx * dx + dy * dy) { // in circle
      return 0;
    }
    let rect = new Rect(mark.x - mark.radius, mark.y - mark.radius, mark.x + mark.radius, mark.y + mark.radius);
    if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) { // out of square
      return 0;
    }
    if (x > mark.x && y < mark.y) return 1;
    if (x < mark.x && y < mark.y) return 2;
    if (x < mark.x && y > mark.y) return 3;
    if (x > mark.x && y > mark.y) return 4;

    return 0;
  }

  /**
   * 绘制标记圈圈
   * @param ctx 
   */
  drawMarks(ctx: CanvasRenderingContext2D) {
    ctx.save();

    ctx.strokeStyle = '#00cc00';
    ctx.lineWidth = 8;
    this.innerMarks.forEach(m => {
      ctx.beginPath();
      ctx.arc(m.x, m.y, m.radius, 0, 2 * Math.PI);
      ctx.stroke();
      if (m.status == 1) { // 选中状态，画个外框
        let rect = new Rect(m.x - m.radius, m.y - m.radius, m.x + m.radius, m.y + m.radius);
        ctx.save();
        ctx.strokeStyle = 'yellow';
        ctx.lineWidth = 2;
        ctx.setLineDash([4, 2]);
        ctx.strokeRect(rect.left, rect.top, rect.width(), rect.height());
        ctx.restore();
      }
    });
    ctx.restore();
  }

  /**
   * 绘制标记圈圈, 用于导出
   * @param ctx 
   * @param scale 压缩比例
   */
   drawMarksOut(ctx: CanvasRenderingContext2D, scale = 1) {
    ctx.save();

    ctx.font = `25px sans-serif`;
    ctx.fillStyle = 'red';
    ctx.strokeStyle = '#00cc00';
    ctx.lineWidth = 8 / scale;
    this.innerMarks.forEach(m => {
      ctx.beginPath();
      ctx.arc(m.x / scale, m.y / scale, m.radius / scale, 0, 2 * Math.PI);
      let pos = this.getBestTextPos(ctx, m, scale);
      ctx.fillText(m.note, pos.x, pos.y);
      ctx.stroke();
    });
    ctx.restore();
  }


  // 获取最佳文字排版位置
  getBestTextPos(ctx: CanvasRenderingContext2D, mark: InnerMark, scale = 1): Point{
    let text = ctx.measureText(mark.note); // TextMetrics object
    let width = text.width;
    let x = Math.max(mark.x / scale - width / 2, 0);
    if (x + width > ctx.canvas.width) { // 文字超出了
      x = Math.max(ctx.canvas.width - width, 0);
    }
    let y = Math.min(mark.y / scale + 10, this.width / scale);
    return {x, y};
  }

}

export interface InnerMark {
  x: number;
  y: number;
  radius: number;
  note: string; // 文字说明
  status: number; //0-未选中，1-选中
}


