import {Points as Points2} from "three/src/objects/Points";
import {Float32BufferAttribute} from "three/src/core/BufferAttribute";
import {BufferGeometry as BufferGeometry2} from "three/src/core/BufferGeometry";
const THREE = {BufferGeometry: BufferGeometry2, Float32BufferAttribute, Points: Points2};
import {CoreString} from "../../String";
import {CoreGeometry} from "../../geometry/Geometry";
import {AttribType} from "../../geometry/Constant";
import {CoreAttributeData} from "../../geometry/AttributeData";
import {CoreAttribute} from "../../geometry/Attribute";
import {Poly as Poly2} from "../../../engine/Poly";
import {CoreType} from "../../Type";
import {ObjectUtils as ObjectUtils2} from "../../ObjectUtils";
const DEEP_ATTRIB_SEPARATOR = ":";
export class JsonDataLoader {
  constructor(options = {}) {
    this._attribute_datas_by_name = {};
    this._options = {};
    this._options.dataKeysPrefix = options.dataKeysPrefix;
    this._options.skipEntries = options.skipEntries;
    this._options.doConvert = options.doConvert || false;
    this._options.convertToNumeric = options.convertToNumeric;
  }
  load(url, success_callback, progress_callback, error_callback) {
    fetch(url).then(async (response) => {
      this._json = await response.json();
      if (this._options.dataKeysPrefix != null && this._options.dataKeysPrefix != "") {
        this._json = this.get_prefixed_json(this._json, this._options.dataKeysPrefix.split("."));
      }
      const object = this.create_object();
      success_callback(object);
    }).catch((error) => {
      Poly2.error("error", error);
      error_callback(error);
    });
  }
  get_prefixed_json(json, prefixes) {
    if (prefixes.length == 0) {
      return json;
    } else {
      const first_prefix = prefixes.shift();
      if (first_prefix) {
        return this.get_prefixed_json(json[first_prefix], prefixes);
      }
    }
    return [];
  }
  set_json(json) {
    return this._json = json;
  }
  create_object() {
    const geometry = new THREE.BufferGeometry();
    const core_geo = new CoreGeometry(geometry);
    if (this._json != null) {
      const points_count = this._json.length;
      core_geo.initPositionAttribute(points_count);
      this._find_attributes();
      const convert_to_numeric_masks = CoreString.attribNames(this._options.convertToNumeric || "");
      for (let attrib_name of Object.keys(this._attribute_datas_by_name)) {
        const geo_attrib_name = CoreAttribute.remap_name(attrib_name);
        let attrib_values = this._attribute_values_for_name(attrib_name).flat();
        const data = this._attribute_datas_by_name[attrib_name];
        const size = data.size();
        if (data.type() === AttribType.STRING) {
          if (this._options.doConvert && CoreString.matchesOneMask(attrib_name, convert_to_numeric_masks)) {
            const numerical_attrib_values = attrib_values.map((v) => {
              if (CoreType.isString(v)) {
                return parseFloat(v) || 0;
              } else {
                return v;
              }
            });
            geometry.setAttribute(geo_attrib_name, new THREE.Float32BufferAttribute(numerical_attrib_values, size));
          } else {
            const index_data = CoreAttribute.array_to_indexed_arrays(attrib_values);
            core_geo.setIndexedAttribute(geo_attrib_name, index_data["values"], index_data["indices"]);
          }
        } else {
          const numerical_attrib_values = attrib_values;
          geometry.setAttribute(geo_attrib_name, new THREE.Float32BufferAttribute(numerical_attrib_values, size));
        }
      }
    }
    return geometry;
  }
  _find_attributes() {
    let first_pt;
    const masks = CoreString.attribNames(this._options.skipEntries || "");
    if (this._json) {
      if ((first_pt = this._json[0]) != null) {
        for (let attrib_name of Object.keys(first_pt)) {
          const attrib_value = first_pt[attrib_name];
          if (this._value_has_subentries(attrib_value)) {
            for (let key of Object.keys(attrib_value)) {
              const deep_attrib_name = [attrib_name, key].join(DEEP_ATTRIB_SEPARATOR);
              const deep_attrib_value = attrib_value[attrib_name];
              if (!CoreString.matchesOneMask(deep_attrib_name, masks)) {
                this._attribute_datas_by_name[deep_attrib_name] = CoreAttributeData.from_value(deep_attrib_value);
              }
            }
          } else {
            if (!CoreString.matchesOneMask(attrib_name, masks)) {
              this._attribute_datas_by_name[attrib_name] = CoreAttributeData.from_value(attrib_value);
            }
          }
        }
      }
    }
  }
  _attribute_values_for_name(attrib_name) {
    if (this._json) {
      return this._json.map((json_element) => {
        const prefix = attrib_name.split(DEEP_ATTRIB_SEPARATOR)[0];
        const value = json_element[prefix];
        if (this._value_has_subentries(value)) {
          const deep_attrib_name = attrib_name.substring(prefix.length + 1);
          return value[deep_attrib_name] || 0;
        } else {
          return value || 0;
        }
      });
    } else {
      return [];
    }
  }
  _value_has_subentries(value) {
    return ObjectUtils2.isObject(value) && !CoreType.isArray(value);
  }
}
