"use strict";

//  entityShapeVisualizer.js
//
//  Created by Thijs Wenker on 1/11/19
//
//  Copyright 2019 High Fidelity, Inc.
//
//  Distributed under the Apache License, Version 2.0.
//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//

var SHAPETYPE_TO_SHAPE = {
    "box": "Cube",
    "ellipsoid": "Sphere",
    "cylinder-y": "Cylinder",
};

var REQUESTED_ENTITY_SHAPE_PROPERTIES = [
    'type', 'shapeType', 'compoundShapeURL', 'localDimensions'
];

function getEntityShapePropertiesForType(properties) {
    switch (properties.type) {
        case "Zone":
            if (SHAPETYPE_TO_SHAPE[properties.shapeType]) {
                return {
                    type: "Shape",
                    shape: SHAPETYPE_TO_SHAPE[properties.shapeType],
                    localDimensions: properties.localDimensions
                };
            } else if (properties.shapeType === "compound") {
                return {
                    type: "Model",
                    modelURL: properties.compoundShapeURL,
                    localDimensions: properties.localDimensions
                };
            } else if (properties.shapeType === "sphere") {
                var sphereDiameter = Math.max(properties.localDimensions.x, properties.localDimensions.y,
                    properties.localDimensions.z);
                return {
                    type: "Sphere",
                    modelURL: properties.compoundShapeURL,
                    localDimensions: {x: sphereDiameter, y: sphereDiameter, z: sphereDiameter}
                };
            }
            break;
    }

    // Default properties
    return {
        type: "Shape",
        shape: "Cube",
        localDimensions: properties.localDimensions
    };
}

function deepEqual(a, b) {
    if (a === b) {
        return true;
    }

    if (typeof(a) !== "object" || typeof(b) !== "object") {
        return false;
    }

    if (Object.keys(a).length !== Object.keys(b).length) {
        return false;
    }

    for (var property in a) {
        if (!a.hasOwnProperty(property)) {
            continue;
        }
        if (!b.hasOwnProperty(property)) {
            return false;
        }
        if (!deepEqual(a[property], b[property])) {
            return false;
        }
    }
    return true;
}

/**
 * Returns an array of property names which are different in comparison.
 * @param propertiesA
 * @param propertiesB
 * @returns {Array} - array of different property names
 */
function compareEntityProperties(propertiesA, propertiesB) {
    var differentProperties = [],
        property;

    for (property in propertiesA) {
        if (!propertiesA.hasOwnProperty(property)) {
            continue;
        }
        if (!propertiesB.hasOwnProperty(property) || !deepEqual(propertiesA[property], propertiesB[property])) {
            differentProperties.push(property);
        }
    }
    for (property in propertiesB) {
        if (!propertiesB.hasOwnProperty(property)) {
            continue;
        }
        if (!propertiesA.hasOwnProperty(property)) {
            differentProperties.push(property);
        }
    }

    return differentProperties;
}

function deepCopy(v) {
    return JSON.parse(JSON.stringify(v));
}

function EntityShape(entityID) {
    this.entityID = entityID;
    var propertiesForType = getEntityShapePropertiesForType(Entities.getEntityProperties(entityID, REQUESTED_ENTITY_SHAPE_PROPERTIES));

    this.previousPropertiesForType = propertiesForType;

    this.initialize(propertiesForType);
}

EntityShape.prototype = {
    initialize: function(properties) {
        // Create new instance of JS object:
        var overlayProperties = deepCopy(properties);

        overlayProperties.localPosition = Vec3.ZERO;
        overlayProperties.localRotation = Quat.IDENTITY;
        overlayProperties.canCastShadows = false;
        overlayProperties.parentID = this.entityID;
        overlayProperties.collisionless = true;
        overlayProperties.ignorePickIntersection = true;
        this.entity = Entities.addEntity(overlayProperties, "local");
        var PROJECTED_MATERIALS = false;
        this.materialEntity = Entities.addEntity({
            type: "Material",
            localPosition: Vec3.ZERO,
            localRotation: Quat.IDENTITY,
            localDimensions: properties.localDimensions,
            parentID: this.entity,
            priority: 1,
            materialMappingMode: PROJECTED_MATERIALS ? "projected" : "uv",
            materialURL: Script.resolvePath("../assets/images/materials/GridPattern.json"),
            ignorePickIntersection: true,
        }, "local");
    },
    update: function() {
        var propertiesForType = getEntityShapePropertiesForType(Entities.getEntityProperties(this.entityID, REQUESTED_ENTITY_SHAPE_PROPERTIES));

        var difference = compareEntityProperties(propertiesForType, this.previousPropertiesForType);

        if (deepEqual(difference, ['localDimensions'])) {
            this.previousPropertiesForType = propertiesForType;
            Entities.editEntity(this.entity, {
                localDimensions: propertiesForType.localDimensions,
            });
        } else if (difference.length > 0) {
            this.previousPropertiesForType = propertiesForType;
            this.clear();
            this.initialize(propertiesForType);
        }
    },
    clear: function() {
        Entities.deleteEntity(this.materialEntity);
        Entities.deleteEntity(this.entity);
    }
};

function EntityShapeVisualizer(visualizedTypes) {
    this.acceptedEntities = [];
    this.ignoredEntities = [];
    this.entityShapes = {};

    this.visualizedTypes = visualizedTypes;
}

EntityShapeVisualizer.prototype = {
    addEntity: function(entityID) {
        if (this.entityShapes[entityID]) {
            return;
        }
        this.entityShapes[entityID] = new EntityShape(entityID);

    },
    updateEntity: function(entityID) {
        if (!this.entityShapes[entityID]) {
            return;
        }
        this.entityShapes[entityID].update();
    },
    removeEntity: function(entityID) {
        if (!this.entityShapes[entityID]) {
            return;
        }
        this.entityShapes[entityID].clear();
        delete this.entityShapes[entityID];
    },
    cleanup: function() {
        Object.keys(this.entityShapes).forEach(function(entityID) {
            this.entityShapes[entityID].clear();
        }, this);
        this.entityShapes = {};
    },
    setEntities: function(entities) {
        var qualifiedEntities = entities.filter(function(entityID) {
            if (this.acceptedEntities.indexOf(entityID) !== -1) {
                return true;
            }
            if (this.ignoredEntities.indexOf(entityID) !== -1) {
                return false;
            }
            if (this.visualizedTypes.indexOf(Entities.getEntityProperties(entityID, "type").type) !== -1) {
                this.acceptedEntities.push(entityID);
                return true;
            }
            this.ignoredEntities.push(entityID);
            return false;
        }, this);


        var newEntries = [];
        var updateEntries = [];

        var currentEntries = Object.keys(this.entityShapes);
        qualifiedEntities.forEach(function(entityID) {
            if (currentEntries.indexOf(entityID) !== -1) {
                updateEntries.push(entityID);
            } else {
                newEntries.push(entityID);
            }
        });

        var deleteEntries = currentEntries.filter(function(entityID) {
            return updateEntries.indexOf(entityID) === -1;
        });

        deleteEntries.forEach(function(entityID) {
            this.removeEntity(entityID);
        }, this);

        updateEntries.forEach(function(entityID) {
            this.updateEntity(entityID);
        }, this);

        newEntries.forEach(function(entityID) {
            this.addEntity(entityID);
        }, this);
    }
};

module.exports = EntityShapeVisualizer;