import {
  EdgesGeometry,
  ExtrudeGeometry,
  LineSegments,
  Mesh,
  Object3D,
  PerspectiveCamera,
  Scene,
  Shape,
  Vector2,
  Vector3
} from 'three';
import {
  deletePropertyForMaterial,
  OBJECT_BASE_CONF,
  arrayPoints23Points,
  material,
  getRenderOrderPlusByLevel,
  // getObjectsByName,
  getDollys
} from './utils';
import { ICONNECTOR, IOptions, IType, OBJECT_NAME } from './index.type';
import MyCameraControls from '../map/myCameraControls';
import { PoiMarker, PoisLayer } from '../poisLayer';
import { Base } from '../base';
// import Facility from './facility'

enum FACILITY_ICON {
  futi = require('./imgs/icon_futi.png'),
  machine = require('./imgs/icon_zhaocheji.png'),
  louti = require('./imgs/icon_louti.png'),
  zhiti = require('./imgs/icon_zhiti.png'),
  churukou = require('./imgs/icon_churukou.png')
}

export default class ParkingLayer extends Base {
  MY_NAME = 'ParkingLayer';

  private options: IOptions;
  private info: Map<string, any> = new Map();

  private floorIdSort: number[] = [];
  // 基础配置
  private basicSetting = {
    // 50米
    floorHeight: 50,
    // 1:1mm
    coordinateScale: 1
  };

  /**
   * @desc 绘制策略
   */
  private drawObjectStrategy = {
    floor: (data: any[], renderOrderPlus: number, height: number) => {
      const zHeight = this.getZHeight(height);
      const item = data && data[0];
      if (!item) return [];

      const conf = OBJECT_BASE_CONF[OBJECT_NAME.floor];
      const newConf = deletePropertyForMaterial(conf);
      const mat = new material[conf.material]({
        ...newConf
      });
      const points = arrayPoints23Points(item.outline);
      const shape = new Shape(points);
      const mesh = new Mesh();
      mesh.material = mat;
      // const updateGeometry = (depth?: number) => {
      //   depth = depth ?? zHeight
      //   // 拉伸造型
      //   const geo = new ExtrudeGeometry(
      //     // 二维轮廓
      //     shape,
      //     {
      //       // 拉伸长度
      //       depth,
      //       // 无倒角
      //       bevelEnabled: false
      //     }
      //   )
      //   // @ts-ignore
      //   mesh.material.depthTest = !(depth === 0)
      //   return generateGeometry(mesh, geo)
      // }
      // updateGeometry()
      const geo = new ExtrudeGeometry(
        // 二维轮廓
        shape,
        {
          // 拉伸长度
          depth: zHeight,
          // 无倒角
          bevelEnabled: false
        }
      );
      mesh.geometry = geo;
      mesh.name = OBJECT_NAME.floor;

      // // 自定义赋值该对象的相关，添加闭包方法 generateGeometryFn ，为了更新灵活
      // mesh.userData = { ...item, zHeight, updateGeometry }
      mesh.userData = { ...item, zHeight };
      mesh.renderOrder = conf.renderOrder! + renderOrderPlus;
      return [mesh];
    },
    park: (
      data: any[],
      renderOrderPlus: number,
      height: number,
      zIndex: number
    ) => {
      const zHeight = this.getZHeight(height);
      const item = data[0];
      if (!item) return [];
      const conf = OBJECT_BASE_CONF[OBJECT_NAME.park];
      const newConf = deletePropertyForMaterial(conf);
      const mat = new material[conf.material]({
        ...newConf
      });
      let points: Vector2[];
      const meshs: any = [];
      for (const item of data) {
        points = arrayPoints23Points(item.outline);
        const shape = new Shape(points);
        const mesh = new Mesh();
        const line = new LineSegments();

        mesh.material = mat;

        const conf2 = OBJECT_BASE_CONF[OBJECT_NAME.parkBorder];
        const newConf2 = deletePropertyForMaterial(conf2);
        const matLine = new material[conf2.material](newConf2);
        line.material = matLine;

        // const updateGeometry = (depth?: number) => {
        //   depth = depth ?? zHeight
        //   // 拉伸造型
        //   const geo = new ExtrudeGeometry(
        //     // 二维轮廓
        //     shape,
        //     {
        //       // 拉伸长度
        //       depth,
        //       // 无倒角
        //       bevelEnabled: false
        //     }
        //   )
        //   generateGeometry(line, new EdgesGeometry(geo, 20))
        //   // @ts-ignore
        //   mesh.material.depthTest = !(depth === 0)
        //   return generateGeometry(mesh, geo)
        // }
        // updateGeometry()
        const geo = new ExtrudeGeometry(
          // 二维轮廓
          shape,
          {
            // 拉伸长度
            depth: zHeight,
            // 无倒角
            bevelEnabled: false
          }
        );
        mesh.geometry = geo;
        // line.geometry = new EdgesGeometry(geo, 20)
        line.geometry = new EdgesGeometry(geo);

        mesh.name = OBJECT_NAME.park;

        // mesh.position.z += 10
        // line.position.z += 15
        line.renderOrder = conf2.renderOrder! + renderOrderPlus;
        mesh.renderOrder = conf.renderOrder! + renderOrderPlus;
        // const parkInfo = unref(currentExtraInfo).info?.park || {}
        // const extraInfo = parkInfo[item.id] || {}
        mesh.userData = {
          id: item.id,
          type: item.type,
          zHeight,
          zIndex
          // updateGeometry,
          // ...extraInfo
        };

        meshs.push(mesh, line);
      }
      return meshs;
    },
    pillar: (data: any[], renderOrderPlus: number, height: number) => {
      const item = data[0];
      if (!item) return [];
      const zHeight = this.getZHeight(height);

      const conf = OBJECT_BASE_CONF[OBJECT_NAME.pillar];
      const newConf = deletePropertyForMaterial(conf);
      const mat = new material[conf.material]({
        ...newConf
      });
      const meshs: any[] = [];
      const draw = (item: any) => {
        const mesh = new Mesh();
        const points = arrayPoints23Points(item.outline);
        const shape = new Shape(points);
        mesh.material = mat;
        // // 闭包，替换成这样的方式，方便可以动态更新一些 geometry 的属性
        // const updateGeometry = (depth?: number) => {
        //   depth = depth ?? zHeight
        //   // 拉伸造型
        //   const geo = new ExtrudeGeometry(
        //     // 二维轮廓
        //     shape,
        //     {
        //       // 拉伸长度
        //       depth,
        //       // 无倒角
        //       bevelEnabled: false
        //     }
        //   )
        //   // @ts-ignore
        //   mesh.material.depthTest = !(depth === 0)
        //   return generateGeometry(mesh, geo)
        // }
        // updateGeometry()

        const geo = new ExtrudeGeometry(
          // 二维轮廓
          shape,
          {
            // 拉伸长度
            depth: zHeight,
            // 无倒角
            bevelEnabled: false
          }
        );
        mesh.geometry = geo;

        // 自定义赋值该对象的相关，添加闭包方法 updateGeometry ，为了更新灵活
        // mesh.userData = { ...item, updateGeometry }
        mesh.userData = { ...item };
        mesh.name = OBJECT_NAME.pillar;

        mesh.renderOrder = conf.renderOrder! + renderOrderPlus;
        meshs.push(mesh);
      };

      for (const item of data) draw(item);

      return meshs;
    }
  };

  private facilityLayer!: PoisLayer;

  private _nextTick: () => void = () => {};

  _updateSize: () => void = () => {};

  constructor({ name = '', data, poiCollision = true }: IOptions) {
    super();
    this.options = { name, data, poiCollision };
  }

  nextTick(cb?: () => void) {
    if (cb) {
      this._nextTick = cb;
      return undefined;
    } else {
      return new Promise<void>(resolve => (this._nextTick = resolve));
    }
  }

  async _init() {
    // 创建放置交通设施图标图层
    this.facilityLayer = new PoisLayer({
      name: 'facilityLayer',
      // collision: false
      collision: this.options.poiCollision
    });
    // const { scene, camera, $map, cameraControls, reDraw } = this.map
    this.facilityLayer._inject(this.map)._init();
    // 赋值 _updateSize，提供给外部调用
    this._updateSize = this.facilityLayer._updateSize.bind(this.facilityLayer);

    if (typeof this.options.data === 'function') {
      try {
        const res = await (this.options.data() as Promise<Record<string, any>>);
        const { coordinateScale, floorDatas } = res;

        // #region 准备相关数据，由于后续添加对象使用
        this.floorIdSort = floorDatas
          .map((_: any) => _.sort)
          .sort((a: number, b: number) => a - b);
        this.basicSetting.coordinateScale = coordinateScale;
        // #endregion

        this.createFloors(floorDatas);

        // 触发回调
        this._nextTick();
      } catch (error) {}
    } else {
      // 直接传递一个 json 数据
      console.log('this.options.data :>> ', this.options.data);
      // 触发回调
      this._nextTick();
    }
  }

  /**
   * @desc 获取物体需要的高度
   */
  private getZHeight(height: number) {
    return (height * 1000) / this.basicSetting.coordinateScale;
  }

  /**
   * @desc 【待优化？】对楼层数据进行加工
   */
  private createFloorBefore(item: any) {
    // 这里的 data 是新增的属性用来渲染找车机的，之前都是通过url获取的
    const { id, sort, floorName, url, jsonData: extraInfo, data } = item;
    const height: Record<string, any> = {};
    Object.entries(extraInfo).forEach(item => {
      const [type, data] = item;
      // @ts-ignore
      height[type] = data?.h || 0;
    });
    const floorInfo = {
      id,
      floorName,
      height,
      sort,
      floorData: data
    };
    data.length && this.createFloor(floorInfo);
  }

  /**
   * @desc 创建所有楼层入口
   */
  private createFloors(data: Array<Record<string, any>>) {
    for (const item of data) {
      this.createFloorBefore(item);
    }
    const { reDraw, options } = this.map;
    reDraw();

    if (options.viewMode === '2D') {
      this.map.fitToBox2d();
    } else if (options.viewMode === '3D') {
      this.map.fitToBox3d();
    }

    // // 自适应
    // const _getTarget = () => {
    //   const allLevelObjs = getObjectsByName(OBJECT_NAME.level, this.map.scene)
    //   // 最高楼层
    //   return allLevelObjs[allLevelObjs.length - 1]
    // }

    // const target = _getTarget()
    // const { fitDistance, dollys } = getDollys(target, this.map.cameraControls)
    // console.log('dollys :>> ', fitDistance, dollys)
    // this.map.cameraControls.dollyTo(fitDistance, true)
  }

  /**
   * @desc 创建单层
   */
  private createFloor(floorInfo: Record<string, any>) {
    // const floorIdSort = computed(
    //   () => (store.state as AllStates).mapModule.floorIdSort
    // )
    // const extraInfoMapArr = computed(
    //   () => (store.state as AllStates).mapModule.extraInfoMapArr
    // )
    const { floorHeight, coordinateScale } = this.basicSetting;
    // const { jsonData: mapData, height, id, sort, floorName: name } = floorInfo
    const { floorData, height, id, sort, floorName: name } = floorInfo;
    // const curLevelInfo =
    //   unref(extraInfoMapArr).length &&
    //   unref(extraInfoMapArr).find((f: any) => f.id === id)
    // currentExtraInfo.value = curLevelInfo
    const zIndex = this.floorIdSort.indexOf(sort);
    const renderOrderPlus = getRenderOrderPlusByLevel(zIndex);
    const zHeight = (zIndex * (floorHeight * 1000)) / coordinateScale;

    const dataFloor = floorData.filter(
      (_: any) => _.type === OBJECT_NAME.floor
    );
    const meshFloors = this.drawObjectStrategy.floor(
      dataFloor,
      renderOrderPlus,
      height[OBJECT_NAME.floor]
    );

    const dataPark = floorData.filter((_: any) => _.type === OBJECT_NAME.park);
    const meshParks = this.drawObjectStrategy.park(
      dataPark,
      renderOrderPlus,
      height[OBJECT_NAME.park],
      zIndex
    );

    const dataPillar = floorData.filter(
      (_: any) => _.type === OBJECT_NAME.pillar
    );
    const meshPillars = this.drawObjectStrategy.pillar(
      dataPillar,
      renderOrderPlus,
      height[OBJECT_NAME.pillar]
    );

    const floorGroup = new Object3D();
    const conf = OBJECT_BASE_CONF[OBJECT_NAME.floor];

    meshFloors.length && floorGroup.add(...meshFloors);
    meshParks.length && floorGroup.add(...meshParks);
    meshPillars.length && floorGroup.add(...meshPillars);

    // 绘制通行设施
    this.drawFacility(floorInfo, zIndex, zHeight);

    floorGroup.name = OBJECT_NAME.level;
    floorGroup.renderOrder = conf.renderOrder! + renderOrderPlus;
    floorGroup.position.z = zHeight;
    floorGroup.userData = {
      id,
      name,
      zIndex,
      zHeight,
      sort
    };

    // global.scene.add(floorObj)
    this.map.scene.add(floorGroup);

    // global.positoinZs.push(zHeight)

    // store.commit('mapModule/ADD_FLOOR_LIST', {
    //   name,
    //   zIndex
    // })

    // // 创建标签
    // const parkHeight =
    //   (height[OBJECT_NAME.park] * 1000) / global.basicSetting.coordinateScale
    // // drawNameSprites(floorObj, parkHeight, zIndex);

    // reDraw()
  }

  /**
   * @desc 绘制通行设施
   */
  private drawFacility = (floorInfo: any, zIndex: number, zHeight: number) => {
    const tempArr: any[] = [];
    const data = floorInfo.floorData.filter(
      (_: any) => _.type === 'machine' || _.type.includes('connector')
    );

    for (const { type, outline } of data) {
      const name =
        type === 'machine' ? type : ICONNECTOR[Number(type.split('_')[1])];

      // 因为楼层的原因，所以 renderOrder 要计算正确，才能显示完美
      const renderOrderMachine =
        OBJECT_BASE_CONF[(type === 'machine' ? type : 'connector') as IType]
          .renderOrder! + getRenderOrderPlusByLevel(zIndex);

      const position = new Vector3(+outline[0], +outline[1], zHeight);
      tempArr.push(
        new PoiMarker({
          name,
          icon: FACILITY_ICON[name],
          // scale: 1 / 16,
          renderOrder: renderOrderMachine,
          position
        })
      );
    }
    // 添加到图层
    this.facilityLayer.add(...tempArr);

    console.log('this.scene :>> ', this.map.scene);
  };
}
