/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 */
/* eslint-disable */
import {
  AdditiveBlending,
  AnimationClip,
  BackSide,
  Color,
  CustomBlending,
  DoubleSide,
  Face3,
  FaceColors,
  FileLoader,
  Geometry,
  Loader,
  LoaderUtils,
  MaterialLoader,
  Math as _Math,
  MirroredRepeatWrapping,
  MultiplyBlending,
  NoBlending,
  NormalBlending,
  RepeatWrapping,
  SubtractiveBlending,
  TextureLoader,
  Vector2,
  Vector3,
  Vector4,
  VertexColors,
} from 'three';

var LegacyJSONLoader = (function () {
  function LegacyJSONLoader(manager) {
    if (typeof manager === 'boolean') {
      console.warn('THREE.JSONLoader: showStatus parameter has been removed from constructor.');
      manager = undefined;
    }

    Loader.call(this, manager);

    this.withCredentials = false;
  }

  LegacyJSONLoader.prototype = Object.assign(Object.create(Loader.prototype), {
    constructor: LegacyJSONLoader,

    load: function (url, onLoad, onProgress, onError) {
      var scope = this;

      var path = this.path === '' ? LoaderUtils.extractUrlBase(url) : this.path;

      var loader = new FileLoader(this.manager);
      loader.setPath(this.path);
      loader.setWithCredentials(this.withCredentials);
      loader.load(
        url,
        function (text) {
          var json = JSON.parse(text);
          var metadata = json.metadata;

          if (metadata !== undefined) {
            var type = metadata.type;

            if (type !== undefined) {
              if (type.toLowerCase() === 'object') {
                console.error('THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.');
                return;
              }
            }
          }

          var object = scope.parse(json, path);
          onLoad(object.geometry, object.materials);
        },
        onProgress,
        onError
      );
    },

    parse: (function () {
      var _BlendingMode = {
        NoBlending: NoBlending,
        NormalBlending: NormalBlending,
        AdditiveBlending: AdditiveBlending,
        SubtractiveBlending: SubtractiveBlending,
        MultiplyBlending: MultiplyBlending,
        CustomBlending: CustomBlending,
      };

      var _color = new Color();
      var _textureLoader = new TextureLoader();
      var _materialLoader = new MaterialLoader();

      function initMaterials(materials, texturePath, crossOrigin, manager) {
        var array = [];

        for (var i = 0; i < materials.length; ++i) {
          array[i] = createMaterial(materials[i], texturePath, crossOrigin, manager);
        }

        return array;
      }

      function createMaterial(m, texturePath, crossOrigin, manager) {
        // convert from old material format

        var textures = {};

        //

        var json = {
          uuid: _Math.generateUUID(),
          type: 'MeshLambertMaterial',
        };

        for (var name in m) {
          var value = m[name];

          switch (name) {
            case 'DbgColor':
            case 'DbgIndex':
            case 'opticalDensity':
            case 'illumination':
              break;
            case 'DbgName':
              json.name = value;
              break;
            case 'blending':
              json.blending = _BlendingMode[value];
              break;
            case 'colorAmbient':
            case 'mapAmbient':
              console.warn('THREE.LegacyJSONLoader.createMaterial:', name, 'is no longer supported.');
              break;
            case 'colorDiffuse':
              json.color = _color.fromArray(value).getHex();
              break;
            case 'colorSpecular':
              json.specular = _color.fromArray(value).getHex();
              break;
            case 'colorEmissive':
              json.emissive = _color.fromArray(value).getHex();
              break;
            case 'specularCoef':
              json.shininess = value;
              break;
            case 'shading':
              if (value.toLowerCase() === 'basic') json.type = 'MeshBasicMaterial';
              if (value.toLowerCase() === 'phong') json.type = 'MeshPhongMaterial';
              if (value.toLowerCase() === 'standard') json.type = 'MeshStandardMaterial';
              break;
            case 'mapDiffuse':
              json.map = loadTexture(
                value,
                m.mapDiffuseRepeat,
                m.mapDiffuseOffset,
                m.mapDiffuseWrap,
                m.mapDiffuseAnisotropy,
                textures,
                texturePath,
                crossOrigin,
                manager
              );
              break;
            case 'mapDiffuseRepeat':
            case 'mapDiffuseOffset':
            case 'mapDiffuseWrap':
            case 'mapDiffuseAnisotropy':
              break;
            case 'mapEmissive':
              json.emissiveMap = loadTexture(
                value,
                m.mapEmissiveRepeat,
                m.mapEmissiveOffset,
                m.mapEmissiveWrap,
                m.mapEmissiveAnisotropy,
                textures,
                texturePath,
                crossOrigin,
                manager
              );
              break;
            case 'mapEmissiveRepeat':
            case 'mapEmissiveOffset':
            case 'mapEmissiveWrap':
            case 'mapEmissiveAnisotropy':
              break;
            case 'mapLight':
              json.lightMap = loadTexture(
                value,
                m.mapLightRepeat,
                m.mapLightOffset,
                m.mapLightWrap,
                m.mapLightAnisotropy,
                textures,
                texturePath,
                crossOrigin,
                manager
              );
              break;
            case 'mapLightRepeat':
            case 'mapLightOffset':
            case 'mapLightWrap':
            case 'mapLightAnisotropy':
              break;
            case 'mapAO':
              json.aoMap = loadTexture(
                value,
                m.mapAORepeat,
                m.mapAOOffset,
                m.mapAOWrap,
                m.mapAOAnisotropy,
                textures,
                texturePath,
                crossOrigin,
                manager
              );
              break;
            case 'mapAORepeat':
            case 'mapAOOffset':
            case 'mapAOWrap':
            case 'mapAOAnisotropy':
              break;
            case 'mapBump':
              json.bumpMap = loadTexture(
                value,
                m.mapBumpRepeat,
                m.mapBumpOffset,
                m.mapBumpWrap,
                m.mapBumpAnisotropy,
                textures,
                texturePath,
                crossOrigin,
                manager
              );
              break;
            case 'mapBumpScale':
              json.bumpScale = value;
              break;
            case 'mapBumpRepeat':
            case 'mapBumpOffset':
            case 'mapBumpWrap':
            case 'mapBumpAnisotropy':
              break;
            case 'mapNormal':
              json.normalMap = loadTexture(
                value,
                m.mapNormalRepeat,
                m.mapNormalOffset,
                m.mapNormalWrap,
                m.mapNormalAnisotropy,
                textures,
                texturePath,
                crossOrigin,
                manager
              );
              break;
            case 'mapNormalFactor':
              json.normalScale = value;
              break;
            case 'mapNormalRepeat':
            case 'mapNormalOffset':
            case 'mapNormalWrap':
            case 'mapNormalAnisotropy':
              break;
            case 'mapSpecular':
              json.specularMap = loadTexture(
                value,
                m.mapSpecularRepeat,
                m.mapSpecularOffset,
                m.mapSpecularWrap,
                m.mapSpecularAnisotropy,
                textures,
                texturePath,
                crossOrigin,
                manager
              );
              break;
            case 'mapSpecularRepeat':
            case 'mapSpecularOffset':
            case 'mapSpecularWrap':
            case 'mapSpecularAnisotropy':
              break;
            case 'mapMetalness':
              json.metalnessMap = loadTexture(
                value,
                m.mapMetalnessRepeat,
                m.mapMetalnessOffset,
                m.mapMetalnessWrap,
                m.mapMetalnessAnisotropy,
                textures,
                texturePath,
                crossOrigin,
                manager
              );
              break;
            case 'mapMetalnessRepeat':
            case 'mapMetalnessOffset':
            case 'mapMetalnessWrap':
            case 'mapMetalnessAnisotropy':
              break;
            case 'mapRoughness':
              json.roughnessMap = loadTexture(
                value,
                m.mapRoughnessRepeat,
                m.mapRoughnessOffset,
                m.mapRoughnessWrap,
                m.mapRoughnessAnisotropy,
                textures,
                texturePath,
                crossOrigin,
                manager
              );
              break;
            case 'mapRoughnessRepeat':
            case 'mapRoughnessOffset':
            case 'mapRoughnessWrap':
            case 'mapRoughnessAnisotropy':
              break;
            case 'mapAlpha':
              json.alphaMap = loadTexture(
                value,
                m.mapAlphaRepeat,
                m.mapAlphaOffset,
                m.mapAlphaWrap,
                m.mapAlphaAnisotropy,
                textures,
                texturePath,
                crossOrigin,
                manager
              );
              break;
            case 'mapAlphaRepeat':
            case 'mapAlphaOffset':
            case 'mapAlphaWrap':
            case 'mapAlphaAnisotropy':
              break;
            case 'flipSided':
              json.side = BackSide;
              break;
            case 'doubleSided':
              json.side = DoubleSide;
              break;
            case 'transparency':
              console.warn('THREE.LegacyJSONLoader.createMaterial: transparency has been renamed to opacity');
              json.opacity = value;
              break;
            case 'depthTest':
            case 'depthWrite':
            case 'colorWrite':
            case 'opacity':
            case 'reflectivity':
            case 'transparent':
            case 'visible':
            case 'wireframe':
              json[name] = value;
              break;
            case 'vertexColors':
              if (value === true) json.vertexColors = VertexColors;
              if (value === 'face') json.vertexColors = FaceColors;
              break;
            default:
              console.error('THREE.LegacyJSONLoader.createMaterial: Unsupported', name, value);
              break;
          }
        }

        if (json.type === 'MeshBasicMaterial') delete json.emissive;
        if (json.type !== 'MeshPhongMaterial') delete json.specular;

        if (json.opacity < 1) json.transparent = true;

        _materialLoader.setTextures(textures);

        return _materialLoader.parse(json);
      }

      function loadTexture(path, repeat, offset, wrap, anisotropy, textures, texturePath, crossOrigin, manager) {
        var fullPath = texturePath + path;
        var loader = manager.getHandler(fullPath);

        var texture;

        if (loader !== null) {
          texture = loader.load(fullPath);
        } else {
          _textureLoader.setCrossOrigin(crossOrigin);
          texture = _textureLoader.load(fullPath);
        }

        if (repeat !== undefined) {
          texture.repeat.fromArray(repeat);

          if (repeat[0] !== 1) texture.wrapS = RepeatWrapping;
          if (repeat[1] !== 1) texture.wrapT = RepeatWrapping;
        }

        if (offset !== undefined) {
          texture.offset.fromArray(offset);
        }

        if (wrap !== undefined) {
          if (wrap[0] === 'repeat') texture.wrapS = RepeatWrapping;
          if (wrap[0] === 'mirror') texture.wrapS = MirroredRepeatWrapping;

          if (wrap[1] === 'repeat') texture.wrapT = RepeatWrapping;
          if (wrap[1] === 'mirror') texture.wrapT = MirroredRepeatWrapping;
        }

        if (anisotropy !== undefined) {
          texture.anisotropy = anisotropy;
        }

        var uuid = _Math.generateUUID();

        textures[uuid] = texture;

        return uuid;
      }

      function parseModel(json, geometry) {
        function isBitSet(value, position) {
          return value & (1 << position);
        }

        var i,
          j,
          fi,
          offset,
          zLength,
          colorIndex,
          normalIndex,
          uvIndex,
          materialIndex,
          type,
          isQuad,
          hasMaterial,
          hasFaceVertexUv,
          hasFaceNormal,
          hasFaceVertexNormal,
          hasFaceColor,
          hasFaceVertexColor,
          vertex,
          face,
          faceA,
          faceB,
          hex,
          normal,
          uvLayer,
          uv,
          u,
          v,
          faces = json.faces,
          vertices = json.vertices,
          normals = json.normals,
          colors = json.colors,
          scale = json.scale,
          nUvLayers = 0;

        if (json.uvs !== undefined) {
          // disregard empty arrays

          for (i = 0; i < json.uvs.length; i++) {
            if (json.uvs[i].length) nUvLayers++;
          }

          for (i = 0; i < nUvLayers; i++) {
            geometry.faceVertexUvs[i] = [];
          }
        }

        offset = 0;
        zLength = vertices.length;

        while (offset < zLength) {
          vertex = new Vector3();

          vertex.x = vertices[offset++] * scale;
          vertex.y = vertices[offset++] * scale;
          vertex.z = vertices[offset++] * scale;

          geometry.vertices.push(vertex);
        }

        offset = 0;
        zLength = faces.length;

        while (offset < zLength) {
          type = faces[offset++];

          isQuad = isBitSet(type, 0);
          hasMaterial = isBitSet(type, 1);
          hasFaceVertexUv = isBitSet(type, 3);
          hasFaceNormal = isBitSet(type, 4);
          hasFaceVertexNormal = isBitSet(type, 5);
          hasFaceColor = isBitSet(type, 6);
          hasFaceVertexColor = isBitSet(type, 7);

          // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);

          if (isQuad) {
            faceA = new Face3();
            faceA.a = faces[offset];
            faceA.b = faces[offset + 1];
            faceA.c = faces[offset + 3];

            faceB = new Face3();
            faceB.a = faces[offset + 1];
            faceB.b = faces[offset + 2];
            faceB.c = faces[offset + 3];

            offset += 4;

            if (hasMaterial) {
              materialIndex = faces[offset++];
              faceA.materialIndex = materialIndex;
              faceB.materialIndex = materialIndex;
            }

            // to get face <=> uv index correspondence

            fi = geometry.faces.length;

            if (hasFaceVertexUv) {
              for (i = 0; i < nUvLayers; i++) {
                uvLayer = json.uvs[i];

                geometry.faceVertexUvs[i][fi] = [];
                geometry.faceVertexUvs[i][fi + 1] = [];

                for (j = 0; j < 4; j++) {
                  uvIndex = faces[offset++];

                  u = uvLayer[uvIndex * 2];
                  v = uvLayer[uvIndex * 2 + 1];

                  uv = new Vector2(u, v);

                  if (j !== 2) geometry.faceVertexUvs[i][fi].push(uv);
                  if (j !== 0) geometry.faceVertexUvs[i][fi + 1].push(uv);
                }
              }
            }

            if (hasFaceNormal) {
              normalIndex = faces[offset++] * 3;

              faceA.normal.set(normals[normalIndex++], normals[normalIndex++], normals[normalIndex]);

              faceB.normal.copy(faceA.normal);
            }

            if (hasFaceVertexNormal) {
              for (i = 0; i < 4; i++) {
                normalIndex = faces[offset++] * 3;

                normal = new Vector3(normals[normalIndex++], normals[normalIndex++], normals[normalIndex]);

                if (i !== 2) faceA.vertexNormals.push(normal);
                if (i !== 0) faceB.vertexNormals.push(normal);
              }
            }

            if (hasFaceColor) {
              colorIndex = faces[offset++];
              hex = colors[colorIndex];

              faceA.color.setHex(hex);
              faceB.color.setHex(hex);
            }

            if (hasFaceVertexColor) {
              for (i = 0; i < 4; i++) {
                colorIndex = faces[offset++];
                hex = colors[colorIndex];

                if (i !== 2) faceA.vertexColors.push(new Color(hex));
                if (i !== 0) faceB.vertexColors.push(new Color(hex));
              }
            }

            geometry.faces.push(faceA);
            geometry.faces.push(faceB);
          } else {
            face = new Face3();
            face.a = faces[offset++];
            face.b = faces[offset++];
            face.c = faces[offset++];

            if (hasMaterial) {
              materialIndex = faces[offset++];
              face.materialIndex = materialIndex;
            }

            // to get face <=> uv index correspondence

            fi = geometry.faces.length;

            if (hasFaceVertexUv) {
              for (i = 0; i < nUvLayers; i++) {
                uvLayer = json.uvs[i];

                geometry.faceVertexUvs[i][fi] = [];

                for (j = 0; j < 3; j++) {
                  uvIndex = faces[offset++];

                  u = uvLayer[uvIndex * 2];
                  v = uvLayer[uvIndex * 2 + 1];

                  uv = new Vector2(u, v);

                  geometry.faceVertexUvs[i][fi].push(uv);
                }
              }
            }

            if (hasFaceNormal) {
              normalIndex = faces[offset++] * 3;

              face.normal.set(normals[normalIndex++], normals[normalIndex++], normals[normalIndex]);
            }

            if (hasFaceVertexNormal) {
              for (i = 0; i < 3; i++) {
                normalIndex = faces[offset++] * 3;

                normal = new Vector3(normals[normalIndex++], normals[normalIndex++], normals[normalIndex]);

                face.vertexNormals.push(normal);
              }
            }

            if (hasFaceColor) {
              colorIndex = faces[offset++];
              face.color.setHex(colors[colorIndex]);
            }

            if (hasFaceVertexColor) {
              for (i = 0; i < 3; i++) {
                colorIndex = faces[offset++];
                face.vertexColors.push(new Color(colors[colorIndex]));
              }
            }

            geometry.faces.push(face);
          }
        }
      }

      function parseSkin(json, geometry) {
        var influencesPerVertex = json.influencesPerVertex !== undefined ? json.influencesPerVertex : 2;

        if (json.skinWeights) {
          for (var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex) {
            var x = json.skinWeights[i];
            var y = influencesPerVertex > 1 ? json.skinWeights[i + 1] : 0;
            var z = influencesPerVertex > 2 ? json.skinWeights[i + 2] : 0;
            var w = influencesPerVertex > 3 ? json.skinWeights[i + 3] : 0;

            geometry.skinWeights.push(new Vector4(x, y, z, w));
          }
        }

        if (json.skinIndices) {
          for (var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex) {
            var a = json.skinIndices[i];
            var b = influencesPerVertex > 1 ? json.skinIndices[i + 1] : 0;
            var c = influencesPerVertex > 2 ? json.skinIndices[i + 2] : 0;
            var d = influencesPerVertex > 3 ? json.skinIndices[i + 3] : 0;

            geometry.skinIndices.push(new Vector4(a, b, c, d));
          }
        }

        geometry.bones = json.bones;

        if (
          geometry.bones &&
          geometry.bones.length > 0 &&
          (geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length)
        ) {
          console.warn(
            'When skinning, number of vertices (' +
              geometry.vertices.length +
              '), skinIndices (' +
              geometry.skinIndices.length +
              '), and skinWeights (' +
              geometry.skinWeights.length +
              ') should match.'
          );
        }
      }

      function parseMorphing(json, geometry) {
        var scale = json.scale;

        if (json.morphTargets !== undefined) {
          for (var i = 0, l = json.morphTargets.length; i < l; i++) {
            geometry.morphTargets[i] = {};
            geometry.morphTargets[i].name = json.morphTargets[i].name;
            geometry.morphTargets[i].vertices = [];

            var dstVertices = geometry.morphTargets[i].vertices;
            var srcVertices = json.morphTargets[i].vertices;

            for (var v = 0, vl = srcVertices.length; v < vl; v += 3) {
              var vertex = new Vector3();
              vertex.x = srcVertices[v] * scale;
              vertex.y = srcVertices[v + 1] * scale;
              vertex.z = srcVertices[v + 2] * scale;

              dstVertices.push(vertex);
            }
          }
        }

        if (json.morphColors !== undefined && json.morphColors.length > 0) {
          console.warn('THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.');

          var faces = geometry.faces;
          var morphColors = json.morphColors[0].colors;

          for (var i = 0, l = faces.length; i < l; i++) {
            faces[i].color.fromArray(morphColors, i * 3);
          }
        }
      }

      function parseAnimations(json, geometry) {
        var outputAnimations = [];

        // parse old style Bone/Hierarchy animations
        var animations = [];

        if (json.animation !== undefined) {
          animations.push(json.animation);
        }

        if (json.animations !== undefined) {
          if (json.animations.length) {
            animations = animations.concat(json.animations);
          } else {
            animations.push(json.animations);
          }
        }

        for (var i = 0; i < animations.length; i++) {
          var clip = AnimationClip.parseAnimation(animations[i], geometry.bones);
          if (clip) outputAnimations.push(clip);
        }

        // parse implicit morph animations
        if (geometry.morphTargets) {
          // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary.
          var morphAnimationClips = AnimationClip.CreateClipsFromMorphTargetSequences(geometry.morphTargets, 10);
          outputAnimations = outputAnimations.concat(morphAnimationClips);
        }

        if (outputAnimations.length > 0) geometry.animations = outputAnimations;
      }

      return function parse(json, path) {
        if (json.data !== undefined) {
          // Geometry 4.0 spec
          json = json.data;
        }

        if (json.scale !== undefined) {
          json.scale = 1.0 / json.scale;
        } else {
          json.scale = 1.0;
        }

        var geometry = new Geometry();

        parseModel(json, geometry);
        parseSkin(json, geometry);
        parseMorphing(json, geometry);
        parseAnimations(json, geometry);

        geometry.computeFaceNormals();
        geometry.computeBoundingSphere();

        if (json.materials === undefined || json.materials.length === 0) {
          return {geometry: geometry};
        } else {
          var materials = initMaterials(json.materials, this.resourcePath || path, this.crossOrigin, this.manager);

          return {geometry: geometry, materials: materials};
        }
      };
    })(),
  });

  return LegacyJSONLoader;
})();

export {LegacyJSONLoader};
