import { Disposable } from "../base/Scene";
import { Area, AreaGroup, AreaGroups, createReadFramebuffer, createTexture, Data, Settings, TexImage } from "./common";
import { createColored } from "./createColored";

export class FillerResource {
  constructor(
    public config: AreaGroups,
    public page: TexImage,
    public map: TexImage,
    public numberImage: TexImage,
    public taskList?: number[],
    public special?: TexImage,
    public bg?: TexImage,
  ) {
    this.fillAreaInfo(config)
  }

  fillAreaInfo(groups: AreaGroups) {
    for (var i = 0; i < groups.length; i++) {
      var group = groups[i]
      var label = `${i + 1}`
      for (var j = 0; j < group.areas.length; j++) {
        var area = group.areas[j]
        area.center.label = label
        area.center.offset = 0
        area.center.fontHeight = 0
        area.colored = false
      }
    }
  }


}


export class FillerConfig {
  constructor(
    public settings: Settings,
    readonly maxScale: number = 10,
    readonly visibleFontSize: number = 30,
    public hintColorDark: number = 0xffdddddd,
    public hintColorLight: number = 0xffaaaaaa,
  ) { }
}


export interface FillerDataListener {
  onGroupChange(group: AreaGroup | null): void
}


export interface FillerCallback {
  onFillSuccess(): void
  onFillFailed(): void
  onSwitchGroup(): void
  onFinish(): void
}



export class FillerData implements Disposable {


  readonly width: number
  readonly height: number
  readonly mapTexure: WebGLTexture

  readonly colored: WebGLTexture
  readonly data: Data

  readonly fb: WebGLFramebuffer

  private listeners: Array<FillerDataListener> = []
  private _currentGroup: AreaGroup | null = null
  public get currentGroup(): AreaGroup | null { return this._currentGroup }
  public set currentGroup(group: AreaGroup) {
    this._currentGroup = group
    this.listeners.forEach(l => l.onGroupChange(group))
  }

  // 当前group的下标
  public get currentGroupIndex(): number {
    if (!this.currentGroup) return -1;
    return this.data.areaGroups.findIndex((group) => group == this.currentGroup);
  }

  // 统计在当前group之前有多少个已经完成的
  public get doneBeforeCount(): number {
    let count = 0;
    for (let i = 0; i < this.currentGroupIndex; i++) {
      if (this.data.areaGroups[i].isAllColored) count++;
    }
    return count;
  }

  constructor(
    public config: FillerConfig,
    resource: FillerResource,
    private gl: WebGL2RenderingContext,
    public taskList: number[] = [],
    public callback: FillerCallback | null = null
  ) {
    this.width = resource.map.width
    this.height = resource.map.height
    this.mapTexure = createTexture(gl, resource.map, gl.NEAREST)
    this.data = new Data(resource.config)

    this.colored = resource.special ? createTexture(gl, resource.special, gl.LINEAR) : createColored(gl, this.mapTexure, this.data, this.width, this.height)
    this.fb = createReadFramebuffer(gl, this.mapTexure)

    for (let task of taskList) {
      let area = this.data.areaHash.get(task);
      if (area) {
        area.colored = true;
      }
    }
    // this.currentGroup = this.data.areaGroups[1]
    let firstUnfinishedGroup = this.data.areaGroups.find(g => !g.isAllColored);
    if (firstUnfinishedGroup) this.currentGroup = firstUnfinishedGroup;
  }



  addListener(listener: FillerDataListener) {
    this.listeners.push(listener)
  }


  setCurrentGroup(n: number) {
    this.currentGroup = this.data.areaGroups[n]
  }

  setColored(area: Area, color: number, x: number, y: number) {
    area.colored = true
    this.taskList.push(area.id)
  }

  /**
   * 切换到下一个未完成的group，返回null表示当前已没有未完成的group，即整个填色已完成
   */
  switchToNextGroup() {
    if (!this.currentGroup) return null;

    let n = this.data.areaGroups.findIndex((group) => group == this.currentGroup);
    // search next unfinished group
    let found = false;
    for (let i = 0; i < this.data.areaGroups.length; i++) {
      let group = this.data.areaGroups[n];
      if (group.isAllColored) {
        n = (++n) % this.data.areaGroups.length;
      } else {
        found = true;
        break;
      }
    }
    if (found) {
      this.currentGroup = this.data.areaGroups[n];
      return this.currentGroup;
    }
    return null;
  }



  getArea(x: number, y: number, radius: number, areaHash: Map<number, Area>): Area | undefined {


    x = Math.round(x)
    y = Math.round(y)

    let left = x - radius;
    let top = y - radius;
    let width = radius * 2 + 1;
    let height = radius * 2 + 1;
    let count = width * height
    let data = new Uint8Array(count * 4) // TODO memory optimize
    let cx = radius
    let cy = radius


    let gl = this.gl;
    let fb = this.fb;
    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
    gl.readPixels(left, top, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data)
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);

    let u32 = new Uint32Array(data.buffer)


    let areaSearchSparse: Map<number, number> = new Map()
    var px = 0
    var py = 0
    var dx = 0
    var dy = 0
    var dist = 0.0
    var weight = 0.0
    var id = 0
    var pixels = 0
    for (var i = 0; i < u32.length; i++) {
      id = u32[i]
      // 剪枝，忽略超出边界的像素
      if (id == 0) continue
      px = i % width
      py = Math.floor(i / width)
      dx = px - cx
      dy = py - cy
      dist = Math.sqrt(dx * dx + dy * dy)

      // 超出半径以外
      if (dist > radius) continue

      pixels++

      // 距离越远，权重越低, 确保中心点具有最高优先级
      weight = 1 / Math.exp(dist)
      var w = areaSearchSparse.get(id)
      if (w == null) {
        areaSearchSparse.set(id, weight)
      } else {
        areaSearchSparse.set(id, weight + w)
      }
    }



    let maxWeight = 0
    let area: Area | undefined = undefined
    areaSearchSparse.forEach((weight, id) => {
      console.log(`id=${id}, count=${weight}`)

      var a = areaHash.get(id)
      if (a != null && !a.colored && weight > maxWeight) {
        area = a
        maxWeight = weight
      }
    })

    console.log(`Area hit:`, area)

    return area
  }




  dispose() {
    this.gl.deleteFramebuffer(this.fb)
    this.gl.deleteTexture(this.colored)
    this.gl.deleteTexture(this.mapTexure)
    this.listeners = []

  }






}
