import { Component, EventEmitter, Input, OnInit, Output, ViewContainerRef } from '@angular/core';
import Utils from '../../lib/filler/common/utils';
import AutoColorMap from '../../lib/filler/common/auto-color-map';
import FloodFill2 from '../../lib/filler/common/floodfill2';
import { Potrace } from '../../lib/filler/common/potrace';
import { domainConfigs, FileDomain, FileInfo, FileType, PreCheckResult } from './interfaces';
import ColoredMap from '../../lib/filler/common/coloredmap';
import { Centers } from 'src/app/lib/filler/common/interfaces';
import FillArea from 'src/app/lib/filler/common/fillarea';
import { ArtService } from './art.service';
import { firstValueFrom } from 'rxjs';


const TAG = 'UploadBoxComponent';

enum CHECK_STATUS {
  INIT = 0,
  CHECKING = 1,
  CHECK_PASS = 2,
  CHECK_NOTPASSS = 3,
}

@Component({
  selector: 'art-upload-box',
  template: `

  <input type="file" #fileInput (change)="onChange($event)" accept="{{domainConfigs[domain].accept}}" style="display:none" />

  <div class="upload-box" [ngClass]="{'bad' : isBad || checkStatus == 3, 'good' : isGood}" >
    <h5>{{domainConfigs[domain].name}}</h5>
    <!--
    <div>isReady: {{isReady}}</div>
    <div>domain: {{domain}}</div>
    -->
    <div *ngIf="isBad" class="text-danger" i18n>
      尺寸错误! 合法尺寸: 2000x2000|1500x2000|1800x2400|2400x2400|3000x3000
    </div>
    <img *ngIf="fi?.image"  (click)="fileInput.click()" [src]="fi.image.src | safeUrl" />
    <div *ngIf="fi?.blob && fi.image">
      {{fi.blob.size | dataSize}}, {{fi.blob.type}}, {{fi.image.width + 'x' + fi.image.height}}
    </div>
    <div *ngIf="checkStatus == 1" class="text-info">
      <ng-container i18n>正在检查线稿...</ng-container> <i nz-icon [nzType]="'loading'"></i>
    </div>
    <div *ngIf="checkStatus == 2" class="text-success">
      <i nz-icon [nzType]="'check'"></i> <ng-container i18n>检查通过</ng-container> 
      <span class="clickable" (click)="checkDetail()" i18n>查看详情>> </span>
    </div>
    <div *ngIf="checkStatus == 3" class="text-danger">
      <i nz-icon [nzType]="'warning'"></i> <ng-container i18n>小区块{{smallAreaCount}}个, 超过规定数量，请修改线稿</ng-container>
      <span class="text-danger clickable" (click)="checkDetail()" i18n>查看详情>> </span>
    </div>
    <!--
    <img *ngIf="mapFi?.image"   [src]="mapFi.image.src | safeUrl" />
    -->
    <div *ngIf="!fi?.blob" class="upload-text">
      <a href="javascript: void(0);" (click)="fileInput.click()" i18n>添加</a>
      <span> {{domainConfigs[domain].acceptText}} </span>
    </div>

    <div *ngIf="!fi?.blob && domain == 'rawColored'">
      <input type="radio" id="map-radio" [checked]="needRefreshMap" (click)="onRadioClick()"/>
      <span i18n>识别阴影</span>
    </div>

    <div>
    <button *ngIf="fi" class="btn m-0" (click)="clear()" >
      <i class="fa fa-trash"></i>
    </button>

  </div>
   
  `,
  styles: [`
    .upload-box{
      margin:5px;
      box-sizing : border-box;
      border:4px dashed #ccc;
      border-radius:10px;
      padding:10px;
      min-width:100px;
      min-height:100px;
      display:flex;
      flex-direction: column;
      align-items:center;
      justify-content : center;
    }
    .bad {
      border-color:#cc0000;
    }
    .good{
      border-color:#00cc00;
    }
    .upload-box img{
      width:200px;
    }
    .upload-text{
      display:flex;
      flex-direction: column;
      align-items:center;
      justify-content : center;
    }

    .clickable {
      cursor:pointer;
    }

    `

  ]
})
export class UploadBoxComponent implements OnInit {

  @Output('onChange') _onChange = new EventEmitter<FileInfo[]>();

  //@Input('map')

  @Input('domain') domain: FileDomain;
  @Input('name') name: string;
  @Input('acceptText') acceptText: string;
  @Input('accept') accept: string;

  //处理colored的时候，需要page和map来一起计算生成colorMap
  _pageAndMap: FileInfo[];
  @Input()
  set pageAndMap(fileinfos: FileInfo[]) {
    console.log('pageAndMap:', fileinfos);
    this._pageAndMap = fileinfos;
    if (FileDomain.rawColored == this.domain || FileDomain.special == this.domain) {
      if (this.fi && this.fi.colorMap) delete this.fi.colorMap;
      this.handleColored();
    }
  }

  fi: FileInfo;


  orgFi: FileInfo; //FileDomain.raw only 原始线稿图
  mapFi: FileInfo; //FileDomain.raw only
  pageFi: FileInfo; //FileDomain.raw only
  centersFi: FileInfo;
  centers: Centers;
  areaMap: {}; //FileDomain.raw ony, 需要生成区块，作为centers
  R: number = 2; //FileDomain.raw only, 小区块半径阈值
  N: number = 30; //FileDomain.raw only,  小于Rmin的区块数阈值
  smallAreaCount: number; //计算中心点后统计出来的小区快数目
  checkStatus: CHECK_STATUS = CHECK_STATUS.INIT; //区块检查状态
  @Input() preCheck: boolean = false; //FileDomain.raw only, 是否对线稿做precheck
  @Output('onCheck') _onCheck = new EventEmitter<PreCheckResult>();
  @Output('onDetail') _onDetail = new EventEmitter<PreCheckResult>();


  workFi: FileInfo; //FileDomain.colored or FileDomain.special
  colorMapFi: FileInfo;
  colorSumFi: FileInfo;

  map2Fi: FileInfo; // generate by colored

  needRefreshMap: boolean = false;

  constructor(
    private artService: ArtService,
  ) { }

  ngOnInit() {
    firstValueFrom(this.artService.sysConfig())
      .then((ret: any) => {
        this.R = ret.Rmin;
        this.N = ret.Nmax;
      })
  }

  get domainConfigs() { return domainConfigs };

  /**
   * 清空所有信息
   */
  clear() {
    this.fi = null;
    this.mapFi = null;
    this.pageFi = null;
    this.workFi = null;
    this.map2Fi = null;
    this.needRefreshMap = false;
    this.checkStatus = CHECK_STATUS.INIT;
    this._onChange.emit([]);
  }

  onRadioClick() {
    this.needRefreshMap = !this.needRefreshMap;
  }

  get isBad() { return this.fi && this.fi.sizeGood == false }

  onChange(event: Event) {
    let files = (<HTMLInputElement>event.target).files;
    if (files.length > 0) {
      this.handleFile(files[0]);
    }
  }

  async handleFile(file: File) {
    console.log('file', file);
    this.fi = { blob: file };
    this.fi.domain = this.domain;
    this.fi.image = await this.blobToImage(this.fi.blob);
    this.fi.sizeGood = this.checkSize(this.fi.image);
    this.mapFi = null;

    if (!this.fi.sizeGood) return;

    if (FileDomain.raw == this.domain) {  //线稿上传
      await this.handleRaw();
    } else if (FileDomain.rawColored == this.domain) { //colored
      await this.handleColored();
    } else if (FileDomain.special == this.domain) { //colored
      await this.handleColored();
    } else {
      this._onChange.emit([this.fi]);
    }
  }

  /**
   * 处理page
   */
  async handleRaw() {
    this.orgFi = { blob: this.fi.blob, image: this.fi.image, domain: FileDomain.org };

    if (this.fi.blob.type != FileType.svg) {
      let pt = await Potrace.fromFile(this.fi.blob);
      this.fi.blob = pt.getBlob(1);
      this.fi.image = await this.blobToImage(this.fi.blob);
    }

    //create map
    let ff = new FloodFill2(this.fi.image);
    let mapBlob = await ff.toBlob();
    this.mapFi = { blob: mapBlob };
    this.mapFi.domain = FileDomain.map;
    this.mapFi.image = await this.blobToImage(this.mapFi.blob);
    this.mapFi.sizeGood = this.checkSize(this.mapFi.image);
    this.mapFi.artMeta = {
      areaCount: ff.getAreaCount(),
      width: this.mapFi.image.width,
      height: this.mapFi.image.height,
    };

    //create page 
    let pageBlob = await Utils.convertToPngBlob(this.fi.image);
    this.pageFi = { blob: pageBlob };
    this.pageFi.domain = FileDomain.page;
    this.pageFi.image = await this.blobToImage(this.pageFi.blob);
    this.pageFi.sizeGood = this.checkSize(this.pageFi.image);

    if (this.preCheck) {
      this.startWorker();
    }

    this._onChange.emit([this.fi, this.mapFi, this.pageFi, this.orgFi]);
  }



  async handleColored() {
    if (this.fi && this._pageAndMap) {
      let page: HTMLImageElement;
      let map: HTMLImageElement;
      let colored: HTMLImageElement = this.fi.image;
      this._pageAndMap.forEach(fi => {
        if (FileDomain.map == fi.domain) { map = fi.image; }
        if (FileDomain.page == fi.domain) { page = fi.image; }
      })


      // 根据colored重新刷新map
      if (this.needRefreshMap) {
        console.log('creating new Map');
        //create new map
        let cmap = new ColoredMap(page, map, colored);
        let mapBlob = await cmap.toBlob();
        this.map2Fi = { blob: mapBlob };
        this.map2Fi.domain = FileDomain.map;
        this.map2Fi.image = await this.blobToImage(this.map2Fi.blob);
        this.map2Fi.sizeGood = this.checkSize(this.map2Fi.image);
        this.map2Fi.artMeta = {
          areaCount: cmap.getAreaCount(),
          width: this.map2Fi.image.width,
          height: this.map2Fi.image.height,
        };
        map = this.map2Fi.image;
      }

      console.log('creating ColorMap');
      let acm = new AutoColorMap(page, map, colored);
      this.fi.colorMap = acm.colorMap;
      this.fi.colorSum = acm.colorSum;
      console.log(TAG, 'colorMap:', this.fi.colorMap);
      console.log(TAG, 'colorMap:', this.fi.colorSum);
      this.fi.artMeta = {
        coloredAreaCount: Object.keys(this.fi.colorMap).length,
        colorCount: acm.colorSum.length,
      };

      //create workFi
      let work = await acm.getWorkBlob();
      this.workFi = { blob: work };
      this.workFi.domain = FileDomain.work;
      this.workFi.image = await this.blobToImage(this.workFi.blob);
      this.workFi.sizeGood = this.checkSize(this.workFi.image);

      //colorSum fi
      let colorSumBlob = new Blob([JSON.stringify(this.fi.colorSum)], { type: 'application/json' });
      this.colorSumFi = { blob: colorSumBlob };
      this.colorSumFi.domain = FileDomain.colorSum;
      this.colorSumFi.object = this.fi.colorSum;

      //colorMap fi
      let colorMapBlob = new Blob([JSON.stringify(this.fi.colorMap)], { type: 'application/json' });
      this.colorMapFi = { blob: colorMapBlob };
      this.colorMapFi.domain = FileDomain.colorMap;
      this.colorMapFi.object = this.fi.colorMap;


      //convert special to png
      if (FileDomain.special == this.domain && this.fi.blob.type != 'image/png') {
        this.fi.blob = await Utils.toPngBlob(this.fi.image);
      }

      if (this.map2Fi) {
        this._onChange.emit([this.fi, this.workFi, this.colorSumFi, this.colorMapFi, this.map2Fi]);
      } else {
        this._onChange.emit([this.fi, this.workFi, this.colorSumFi, this.colorMapFi]);
      }

    } else {
      console.log('Data not enough, no ColorMap');
    }

  }


  /**
   * 检查尺寸是否ok
   */
  checkSize(image: HTMLImageElement): boolean {
    if (2000 == image.width && 2000 == image.height) return true;
    if (1500 == image.width && 2000 == image.height) return true;
    if (2400 == image.width && 2400 == image.height) return true;
    if (3000 == image.width && 3000 == image.height) return true;
    if (1800 == image.width && 2400 == image.height) return true;
    if (32 == image.width && 32 == image.height) return true;
    return false;
  }


  /**
   * 将blob对象转成图片对象
   * @param blob 
   * @returns 
   */
  blobToImage(blob: Blob): Promise<HTMLImageElement> {
    return new Promise((done, reject) => {
      let reader = new FileReader();
      let image = new Image();
      image.onerror = reject;
      image.onload = () => done(image);
      reader.addEventListener('load', () => {
        image.src = reader.result as string;
      }, false);
      reader.readAsDataURL(blob);
    });
  }

  get isGood() { return this.isReady && this.fi }

  get isReady(): boolean {
    //page 必须有
    if (FileDomain.raw == this.domain) {
      if (!this.fi) return false;
      if (!this.mapFi) return false;
      if (!this.pageFi) return false;
      if (this.preCheck && this.checkStatus != CHECK_STATUS.CHECK_PASS) return false;
      return true;
    }

    //可选，如果选择，就必须等colorMap计算完毕
    if (FileDomain.rawColored == this.domain) {
      if (!this.fi) return true;
      if (this.fi && this.fi.colorMap && this.workFi) return true;
      return false;
    }

    if (FileDomain.special == this.domain) {
      if (!this.fi) return true;
      if (this.fi && this.fi.colorMap && this.workFi) return true;
      return false;
    }

    return true;
  }

  get isReadyButCheckFailed(): boolean {
    //page 必须有
    if (FileDomain.raw == this.domain) {
      if (!this.fi) return false;
      if (!this.mapFi) return false;
      if (!this.pageFi) return false;
      if (!this.preCheck) return false;
      if (this.checkStatus != CHECK_STATUS.CHECK_NOTPASSS) return false;
      return true;
    }

    return false;
  }

  get isChecked(): boolean {
    if (FileDomain.raw == this.domain) {
      if (!this.fi) return false;
      if (!this.mapFi) return false;
      if (!this.pageFi) return false;
      if (this.preCheck && (this.checkStatus == CHECK_STATUS.INIT || this.checkStatus == CHECK_STATUS.CHECKING)) return false;
      return true;
    }
    return true;
  }


  /**
   * 启动webworker执行计算中心点的耗时任务
   */
  startWorker() {
    if (typeof Worker === 'undefined') return;
    try {
      this.checkStatus = CHECK_STATUS.CHECKING;
      const worker = new Worker(new URL('src/app/lib/filler/common/centers.worker.ts', import.meta.url));
      console.log("create centers webworker!!!");
      worker.onmessage = (result) => {
        console.log("get worker result:" + JSON.stringify(result.data));
        this.centers = result.data;
        let centersBlob = new Blob([JSON.stringify(this.centers)], { type: 'application/json' });
        this.centersFi = { blob: centersBlob };
        this.centersFi.domain = FileDomain.centers;
        this.centersFi.object = this.centers;

        this._onChange.emit([this.fi, this.mapFi, this.pageFi, this.orgFi, this.centersFi]);
        this.check();
        worker.terminate();
      };
      worker.onerror = function (event) {
        console.error("webworker error:" + event.message);
        worker.terminate();
      };
      let pagePixels = new Uint32Array(Utils.getImageData(this.pageFi.image).data.buffer);
      let mapPixels = new Uint32Array(Utils.getImageData(this.mapFi.image).data.buffer);
      worker.postMessage({ pagePixels: pagePixels, mapPixels: mapPixels, width: this.pageFi.image.width, height: this.pageFi.image.height });
    } catch (e) {
      console.log(e.message);
    }
  }

  /**
   * Create area map from map pixels.
   */
  createAreaMap() {
    let width = this.pageFi.image.width;
    let map = this.mapFi.image;
    let mapPixels = new Uint32Array(Utils.getImageData(map).data.buffer);
    let floodArr = 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;
  }


  check() {
    let values = Object.values(this.centers);
    let smalls = values.filter(e => e.radius <= this.R);
    this.smallAreaCount = smalls.length;

    // 生成areaMap，用于与中心点比较，有可能区块过小中心点算不出来
    this.createAreaMap();
    let noCenterAreaCount = Object.keys(this.areaMap).length - Object.keys(this.centers).length;

    this.smallAreaCount += noCenterAreaCount;

    if (this.smallAreaCount > this.N) {
      this.checkStatus = CHECK_STATUS.CHECK_NOTPASSS;
    } else {
      this.checkStatus = CHECK_STATUS.CHECK_PASS;
    }

    let result: PreCheckResult = {
      raw: this.fi.image,
      page: this.pageFi.image,
      map: this.mapFi.image,
      centers: this.centers,
      areaMap: this.areaMap,
      R: this.R,
      N: this.N,
      count: this.smallAreaCount,
      result: this.checkStatus == CHECK_STATUS.CHECK_PASS ? true : false
    };
    this._onCheck.emit(result);

  }

  checkDetail() {
    let result: PreCheckResult = {
      raw: this.fi.image,
      page: this.pageFi.image,
      map: this.mapFi.image,
      centers: this.centers,
      areaMap: this.areaMap,
      R: this.R,
      N: this.N,
      count: this.smallAreaCount,
      result: this.checkStatus == CHECK_STATUS.CHECK_PASS ? true : false
    };
    this._onDetail.emit(result);
  }

}


