import FillArea from "./fillarea";
import { AreaMap, Centers } from "./interfaces";

/**
 * 找到填色页所有区域的最大内切圆中心点以及半径
 */
 export default class WorkCenterFinder {
  pagePixels: Uint32Array;
  mapPixels: Uint32Array;
  areaMap: AreaMap;
  width: number;
  height: number;

  /**
   * webworker 不支持操作dom, 改写一下
   * @param pagePixels
   * @param mapPixels
   * @param width
   * @param height
   */
  constructor(pagePixels: Uint32Array, mapPixels: Uint32Array, width: number, height: number) {
    this.pagePixels = pagePixels;
    this.mapPixels = mapPixels;
    this.areaMap = {};
    this.width = width;
    this.height = height;
  }

  calculate() {
    //裁剪
    for (let i = 0; i < this.pagePixels.length; i++) {
      if (this.pagePixels[i]) this.mapPixels[i] = 0;
    }
    this.createAreaMap();
    let centers = this.findAll();
    return centers;
  }

  findAll() {
    console.time('findCenters');
    let areas = Object.keys(this.areaMap).map(key => {
      return this.areaMap[key];
    })

    let centers: Centers = {};
    areas.forEach(area => {
      //skip large area.
      //if (area.left == 0) return null;
      let center = this.findAreaCenter(area);
      if (isNaN(center.x)) {
        console.log('xxxxxxxxxxxxxxxxxxxx', center, area)
      } else {
        centers[area.color] = center;
      }
    })

    console.timeEnd('findCenters');
    return centers;
  }

  /**
   * Finder center of area.
   * @param {FillArea} area 
   */
  findAreaCenter(area) {

    console.log('area', area);

    let ai, ax, ay, aw, ah, i, x, y, gx, gy, radius;
    aw = area.width();
    ah = area.height();

    let areaPixels = new Uint32Array(aw * ah);

    for (ai = 0; ai < areaPixels.length; ai++) {
      ax = ai % aw;
      ay = (ai - ax) / aw;
      x = area.left + ax;
      y = area.top + ay;
      i = y * this.width + x;
      if (this.mapPixels[i] == area.color) {
        areaPixels[ai] = this.mapPixels[i];
      } else {
        areaPixels[ai] = 0;
      }
    }


    //find contours
    let flag, pixel;
    let contours = [];
    for (ax = 0; ax < aw; ax++) {
      flag = 0;
      for (ay = 0; ay < ah; ay++) {
        ai = ay * aw + ax;
        pixel = areaPixels[ai];
        if (!flag && pixel) {
          flag = 1;
          contours.push(ai);
        } else if (flag && !pixel) {
          flag = 0;
          contours.push(ai - aw);
        } else if (flag && pixel) continue;
        else if (!flag && !pixel) continue;
      }
      if (flag) {
        //last point
        contours.push(ai);
      }
    }

    for (ay = 0; ay < ah; ay++) {
      flag = 0;
      for (ax = 0; ax < aw; ax++) {
        ai = ay * aw + ax;
        pixel = areaPixels[ai];
        if (!flag && pixel) {
          flag = 1;
          contours.push(ai);
        } else if (flag && !pixel) {
          flag = 0;
          contours.push(ai - 1);
        } else if (flag && pixel) continue;
        else if (!flag && !pixel) continue;
      }
      if (flag) {
        //last point
        contours.push(ai);
      }
    }



    let contoursHash = {}
    let contoursNew = [];
    for (i = 0; i < contours.length; i++) {
      ai = contours[i];
      if (contoursHash[ai]) continue;
      else {
        contoursNew.push(ai);
        contoursHash[ai] = true;
      }
    }
    console.log('contours size:', contours.length, contoursNew.length);
    contoursHash = null;
    contours = contoursNew;

    let contoursX = [];
    let contoursY = [];
    for (i = 0; i < contours.length; i++) {
      ai = contours[i];
      x = ai % aw;
      y = (ai - x) / aw;
      contoursX[i] = x;
      contoursY[i] = y;
    }


    //console.log('contoursXY', contoursX, contoursY);

    let minRadius, maxRadius, maxAi, dist, x1, y1, dx, dy, maxX, maxY;
    maxRadius = 0;
    for (ai = 0; ai < areaPixels.length; ai++) {
      pixel = areaPixels[ai];
      minRadius = Number.MAX_VALUE;
      x1 = ai % aw;
      y1 = (ai - x1) / aw;
      if (pixel) {
        for (i = 0; i < contours.length; i++) {
          dx = x1 - contoursX[i];
          dy = y1 - contoursY[i];
          dist = dx * dx + dy * dy;
          if (dist < minRadius) minRadius = dist;
          //性能优化
          if (minRadius < maxRadius) break;
        }
        if (minRadius > maxRadius) {
          maxRadius = minRadius;
          maxAi = ai;
        }
      }
    }

    // 得到最大内切圆的位置和半径
    maxX = maxAi % aw;
    maxY = (maxAi - maxX) / aw;
    let x0 = aw / 2;   // 几何中心点坐标x
    let y0 = ah / 2;   // 几何中心点坐标y
    let maxDist = (maxX - x0) * (maxX - x0) + (maxY - y0) * (maxY - y0);  // 最大内切圆心到几何中心点的距离

    console.log('1------ maxAi: ', maxAi, maxX, maxY, maxRadius);

    if (maxDist == 0) { // 说明找到的最大内切圆就正在区域矩形中心，可以不用往下进行了
      console.log("find the center exactly, no need to adjust");

      radius = Math.sqrt(maxRadius);
      gx = maxX + area.left;
      gy = maxY + area.top;
  
      return {
        x: gx,
        y: gy,
        radius
      }
    }

    if (maxRadius <= 0 || isNaN(maxX) || isNaN(maxY)) { // 异常情况，不要继续下去了，直接返回
      console.log("find center something wrong!!!");
      
      radius = Math.sqrt(maxRadius);
      gx = maxX + area.left;
      gy = maxY + area.top;
  
      return {
        x: gx,
        y: gy,
        radius
      }
    }

    // 重新根据score = ( radius / max_radius ) * 0.8 +  (1 - dist / max_dist) * 0.2 再次做矫正
    let finalX, finalY, finalAi, finalRadius, maxScore, score;
    maxScore = 0;
    for (ai = 0; ai < areaPixels.length; ai++) {
      pixel = areaPixels[ai];
      minRadius = Number.MAX_VALUE;
      x1 = ai % aw;
      y1 = (ai - x1) / aw;
      if (pixel) {
        for (i = 0; i < contours.length; i++) {
          dx = x1 - contoursX[i];
          dy = y1 - contoursY[i];
          dist = dx * dx + dy * dy;
          if (dist < minRadius) minRadius = dist;
          // //性能优化
          // if (minRadius < maxRadius) break;
        }
        dist = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0); // 该点到几何中心点的距离
        score = (minRadius / maxRadius) * 0.8 + (1 - dist /  maxDist) * 0.2;
        if (score > maxScore) {
          maxScore = score;
          finalRadius = minRadius;
          finalAi = ai;
        }
      }
    }

    finalX = finalAi % aw;
    finalY = (finalAi - finalX) / aw;
    radius = Math.sqrt(finalRadius);
    console.log('2------ finalAi: ', finalAi, finalX, finalY, finalRadius);

    gx = finalX + area.left;
    gy = finalY + area.top;

    return {
      x: gx,
      y: gy,
      radius
    }

  }

  /**
   * Create area map
   */
  createAreaMap() {
    let width = this.width;
    let height = this.height;
    let pixels = this.mapPixels;
    let areaMap = this.areaMap;
    let i, x, y, color;
    for (i = 0; i < pixels.length; i++) {
      color = pixels[i];
      if (!color) continue;
      x = i % width;
      y = (i - x) / width;
      if (areaMap[color]) {
        areaMap[color].addPoint(x, y);
      } else {
        areaMap[color] = new FillArea(color, x, y);
      }
    }
    this.areaMap = areaMap;
  }

}





