import {TypedCopNode} from "../_Base";
import {
  UVMapping,
  CubeReflectionMapping,
  CubeRefractionMapping,
  EquirectangularReflectionMapping,
  EquirectangularRefractionMapping,
  CubeUVReflectionMapping,
  CubeUVRefractionMapping,
  ClampToEdgeWrapping,
  RepeatWrapping,
  MirroredRepeatWrapping,
  LinearEncoding,
  sRGBEncoding,
  GammaEncoding,
  RGBEEncoding,
  LogLuvEncoding,
  RGBM7Encoding,
  RGBM16Encoding,
  RGBDEncoding,
  BasicDepthPacking,
  RGBADepthPacking
} from "three/src/constants";
import {
  MAG_FILTER_DEFAULT_VALUE,
  MAG_FILTER_MENU_ENTRIES,
  MIN_FILTER_DEFAULT_VALUE,
  MIN_FILTER_MENU_ENTRIES
} from "../../../../core/cop/ConstantFilter";
const MAPPINGS = [
  {UVMapping},
  {CubeReflectionMapping},
  {CubeRefractionMapping},
  {EquirectangularReflectionMapping},
  {EquirectangularRefractionMapping},
  {CubeUVReflectionMapping},
  {CubeUVRefractionMapping}
];
const ENCODINGS = [
  {LinearEncoding},
  {sRGBEncoding},
  {GammaEncoding},
  {RGBEEncoding},
  {LogLuvEncoding},
  {RGBM7Encoding},
  {RGBM16Encoding},
  {RGBDEncoding},
  {BasicDepthPacking},
  {RGBADepthPacking}
];
const WRAPPINGS = [{ClampToEdgeWrapping}, {RepeatWrapping}, {MirroredRepeatWrapping}];
import {NodeParamsConfig, ParamConfig} from "../../utils/params/ParamsConfig";
import {CopRendererController} from "./RendererController";
export function TextureParamConfig(Base3) {
  return class Mixin extends Base3 {
    constructor() {
      super(...arguments);
      this.tencoding = ParamConfig.BOOLEAN(0);
      this.encoding = ParamConfig.INTEGER(LinearEncoding, {
        visibleIf: {tencoding: 1},
        menu: {
          entries: ENCODINGS.map((m) => {
            return {
              name: Object.keys(m)[0],
              value: Object.values(m)[0]
            };
          })
        }
      });
      this.tmapping = ParamConfig.BOOLEAN(0);
      this.mapping = ParamConfig.INTEGER(UVMapping, {
        visibleIf: {tmapping: 1},
        menu: {
          entries: MAPPINGS.map((m) => {
            return {
              name: Object.keys(m)[0],
              value: Object.values(m)[0]
            };
          })
        }
      });
      this.twrap = ParamConfig.BOOLEAN(0);
      this.wrapS = ParamConfig.INTEGER(Object.values(WRAPPINGS[0])[0], {
        visibleIf: {twrap: 1},
        menu: {
          entries: WRAPPINGS.map((m) => {
            return {
              name: Object.keys(m)[0],
              value: Object.values(m)[0]
            };
          })
        }
      });
      this.wrapT = ParamConfig.INTEGER(Object.values(WRAPPINGS[0])[0], {
        visibleIf: {twrap: 1},
        menu: {
          entries: WRAPPINGS.map((m) => {
            return {
              name: Object.keys(m)[0],
              value: Object.values(m)[0]
            };
          })
        }
      });
      this.wrapSep = ParamConfig.SEPARATOR(null, {
        visibleIf: {twrap: 1}
      });
      this.tminfilter = ParamConfig.BOOLEAN(0);
      this.minFilter = ParamConfig.INTEGER(MIN_FILTER_DEFAULT_VALUE, {
        visibleIf: {tminfilter: 1},
        menu: {
          entries: MIN_FILTER_MENU_ENTRIES
        }
      });
      this.tmagfilter = ParamConfig.BOOLEAN(0);
      this.magFilter = ParamConfig.INTEGER(MAG_FILTER_DEFAULT_VALUE, {
        visibleIf: {tmagfilter: 1},
        menu: {
          entries: MAG_FILTER_MENU_ENTRIES
        }
      });
      this.tanisotropy = ParamConfig.BOOLEAN(0);
      this.useRendererMaxAnisotropy = ParamConfig.BOOLEAN(1, {
        visibleIf: {tanisotropy: 1}
      });
      this.anisotropy = ParamConfig.INTEGER(1, {
        visibleIf: {tanisotropy: 1, useRendererMaxAnisotropy: 0},
        range: [0, 32],
        rangeLocked: [true, false]
      });
      this.useCameraRenderer = ParamConfig.BOOLEAN(0, {
        visibleIf: {tanisotropy: 1, useRendererMaxAnisotropy: 1}
      });
      this.anisotropySep = ParamConfig.SEPARATOR(null, {
        visibleIf: {tanisotropy: 1}
      });
      this.tflipY = ParamConfig.BOOLEAN(0);
      this.flipY = ParamConfig.BOOLEAN(0, {visibleIf: {tflipY: 1}});
      this.ttransform = ParamConfig.BOOLEAN(0);
      this.offset = ParamConfig.VECTOR2([0, 0], {
        visibleIf: {ttransform: 1},
        cook: false,
        callback: (node) => {
          TextureParamsController.PARAM_CALLBACK_update_offset(node);
        }
      });
      this.repeat = ParamConfig.VECTOR2([1, 1], {
        visibleIf: {ttransform: 1},
        cook: false,
        callback: (node) => {
          TextureParamsController.PARAM_CALLBACK_update_repeat(node);
        }
      });
      this.rotation = ParamConfig.FLOAT(0, {
        range: [-1, 1],
        visibleIf: {ttransform: 1},
        cook: false,
        callback: (node) => {
          TextureParamsController.PARAM_CALLBACK_update_rotation(node);
        }
      });
      this.center = ParamConfig.VECTOR2([0, 0], {
        visibleIf: {ttransform: 1},
        cook: false,
        callback: (node) => {
          TextureParamsController.PARAM_CALLBACK_update_center(node);
        }
      });
    }
  };
}
class TextureParamsConfig extends TextureParamConfig(NodeParamsConfig) {
}
const ParamsConfig2 = new TextureParamsConfig();
class TextureCopNode extends TypedCopNode {
  constructor() {
    super(...arguments);
    this.params_config = ParamsConfig2;
    this.texture_params_controller = new TextureParamsController(this);
  }
}
export class TextureParamsController {
  constructor(node) {
    this.node = node;
  }
  update(texture) {
    const pv = this.node.pv;
    if (pv.tencoding) {
      texture.encoding = pv.encoding;
    }
    if (pv.tmapping) {
      texture.mapping = pv.mapping;
    }
    if (pv.twrap) {
      texture.wrapS = pv.wrapS;
      texture.wrapT = pv.wrapT;
    }
    if (pv.tminfilter) {
      texture.minFilter = pv.minFilter;
    }
    if (pv.tminfilter) {
      texture.magFilter = pv.magFilter;
    }
    this._update_anisotropy(texture);
    texture.flipY = pv.tflipY && pv.flipY;
    this._update_texture_transform(texture);
  }
  async _update_anisotropy(texture) {
    const pv = this.node.pv;
    if (!pv.tanisotropy) {
      return;
    }
    this._renderer_controller = this._renderer_controller || new CopRendererController(this.node);
    const renderer = await this._renderer_controller.renderer();
    const max_anisotropy = renderer.capabilities.getMaxAnisotropy();
    if (pv.useRendererMaxAnisotropy) {
      texture.anisotropy = max_anisotropy;
    } else {
      texture.anisotropy = Math.min(pv.anisotropy, max_anisotropy);
    }
  }
  _update_texture_transform(texture) {
    if (!this.node.pv.ttransform) {
      return;
    }
    this._update_offset(texture, false);
    this._update_repeat(texture, false);
    this._update_rotation(texture, false);
    this._update_center(texture, false);
    texture.updateMatrix();
  }
  _update_offset(texture, update_matrix) {
    texture.offset.copy(this.node.pv.offset);
    if (update_matrix) {
      texture.updateMatrix();
    }
  }
  _update_repeat(texture, update_matrix) {
    texture.repeat.copy(this.node.pv.repeat);
    if (update_matrix) {
      texture.updateMatrix();
    }
  }
  _update_rotation(texture, update_matrix) {
    texture.rotation = this.node.pv.rotation;
    if (update_matrix) {
      texture.updateMatrix();
    }
  }
  _update_center(texture, update_matrix) {
    texture.center.copy(this.node.pv.center);
    if (update_matrix) {
      texture.updateMatrix();
    }
  }
  static PARAM_CALLBACK_update_offset(node) {
    const texture = node.container_controller.container.texture();
    node.texture_params_controller._update_offset(texture, true);
  }
  static PARAM_CALLBACK_update_repeat(node) {
    const texture = node.container_controller.container.texture();
    node.texture_params_controller._update_repeat(texture, true);
  }
  static PARAM_CALLBACK_update_rotation(node) {
    const texture = node.container_controller.container.texture();
    node.texture_params_controller._update_rotation(texture, true);
  }
  static PARAM_CALLBACK_update_center(node) {
    const texture = node.container_controller.container.texture();
    node.texture_params_controller._update_center(texture, true);
  }
}
