import util from '../core/util';
import Geometry from '../Geometry';
import BoundingBox from '../math/BoundingBox';
import Vector3 from '../math/Vector3';

var META = {
    version: 1.0,
    type: 'Geometry',
    generator: 'util.transferable.toObject'
};

/**
 * @alias clay.util.transferable
 */
var transferableUtil = {
    /**
     * Convert geometry to a object containing transferable data
     * @param {Geometry} geometry geometry
     * @param {Boolean} shallow whether shallow copy
     * @returns {Object} { data : data, buffers : buffers }, buffers is the transferable list
     */
    toObject : function (geometry, shallow) {
        if (!geometry) {
            return null;
        }
        var data = {
            metadata : util.extend({}, META)
        };
        //transferable buffers
        var buffers = [];

        //dynamic
        data.dynamic = geometry.dynamic;

        //bounding box
        if (geometry.boundingBox) {
            data.boundingBox = {
                min : geometry.boundingBox.min.toArray(),
                max : geometry.boundingBox.max.toArray()
            };
        }

        //indices
        if (geometry.indices && geometry.indices.length > 0) {
            data.indices = copyIfNecessary(geometry.indices, shallow);
            buffers.push(data.indices.buffer);
        }

        //attributes
        data.attributes = {};
        for (var p in geometry.attributes) {
            if (geometry.attributes.hasOwnProperty(p)) {
                var attr = geometry.attributes[p];
                //ignore empty attributes
                if (attr && attr.value && attr.value.length > 0) {
                    attr = data.attributes[p] = copyAttribute(attr, shallow);
                    buffers.push(attr.value.buffer);
                }
            }
        }

        return {
            data : data,
            buffers : buffers
        };
    },

    /**
     * Reproduce a geometry from object generated by toObject
     * @param {Object} object object generated by toObject
     * @returns {Geometry} geometry
     */
    toGeometry : function (object) {
        if (!object) {
            return null;
        }
        if (object.data && object.buffers) {
            return transferableUtil.toGeometry(object.data);
        }
        if (!object.metadata || object.metadata.generator !== META.generator) {
            throw new Error('[util.transferable.toGeometry] the object is not generated by util.transferable.');
        }

        //basic options
        var options = {
            dynamic : object.dynamic,
            indices : object.indices
        };

        if (object.boundingBox) {
            var min = new Vector3().setArray(object.boundingBox.min);
            var max = new Vector3().setArray(object.boundingBox.max);
            options.boundingBox = new BoundingBox(min, max);
        }

        var geometry = new Geometry(options);

        //attributes
        for (var p in object.attributes) {
            if (object.attributes.hasOwnProperty(p)) {
                var attr = object.attributes[p];
                geometry.attributes[p] = new Geometry.Attribute(attr.name, attr.type, attr.size, attr.semantic);
                geometry.attributes[p].value = attr.value;
            }
        }

        return geometry;
    }

}

function copyAttribute(attr, shallow) {
    return {
        name : attr.name,
        type : attr.type,
        size : attr.size,
        semantic : attr.semantic,
        value : copyIfNecessary(attr.value, shallow)
    };
}

function copyIfNecessary(arr, shallow) {
    if (!shallow) {
        return new arr.constructor(arr);
    } else {
        return arr;
    }
}

export default transferableUtil;
