/// <reference path="resources/three.d.ts" />
/// <reference path="FDGeomObject.ts" />
/// <reference path="debugPrinter.ts" />




module FD {
    export class FDNode {

        private nodeSceneGraph: THREE.Object3D;
        private geometryObjects: THREE.Mesh[];
        private children: FDNode[];
        private siblings: FDNode[];
        public uniqueId: string;
        private parent: FDNode;
        private data: Object;

        /**
        * Creates a new instance of FDnode. Creates am empty object to be added to the scene graph. And possitions it at its origin 
        * @class FDNode
        * @classdesc Container for Nodes
        */
        constructor() {
            this.nodeSceneGraph = new THREE.Object3D();
            this.nodeSceneGraph.position = new THREE.Vector3(0, 0, 0);
            this.children = [];
            this.siblings = [];
            this.geometryObjects = [];
        }

        /**
        * Sets additional data to the node.
        * @memberof FDNode
        * @function setData
        * @param {Object} data
        */
        public setData(data: Object) {
            this.data = data;
        }
        /**
        * Returns the data object
        * @memberof FDNode
        * @function getData
        */
        public getData() {
            return this.data;
        }

        /**
        * Add a child node. Adds the node to the correct possition in the scenegraph
        * @memberof FDNode
        * @function addChild
        * @param {FDNode} child
        */
        public addChild(child: FDNode) {
            this.children.push(child);
            this.nodeSceneGraph.add(child.getNodeSceneGraph());

        }
        /**
        * Add a sibling node. 
        * @memberof FDNode
        * @function addSibling
        * @param {FDNode} sibling
        */
        public addSibling(sibling: FDNode) {
            this.siblings.push(sibling);
        }


        /**
        * Set the unique identifier for this node
        * @memberof FDNode
        * @function setUniqueId
        * @param {String} uid
        */
        public setUniqueId(uid: string) {
            this.uniqueId = uid;
        }

        /**
        * returns the unique id
        * @memberof FDNode
        * @function getUniqueId
        * @returns {String} uid
        */
        public getUniqueId() {
            return this.uniqueId;
        }

        /**
        * returns an array of its children
        * @memberof FDNode
        * @function getChildren
        * @returns {FDNode[]} children
        */
        public getChildren() {
            return this.children;
        }

        /**
        * Sets the parent of this node. Does NOT update the scene graph
        * @memberof FDNode
        * @function setParent
        * @param {FDNode} parent
        */
        public setParent(parent: FDNode) {
            this.parent = parent;
        }

        /**
        * Get the parent node
        * @memberof FDNode
        * @function getParent
        * @returns {FDNode} parent
        */
        public getParent() {
            return this.parent;
        }

        /**
        * Applies the matrix to this entry in the scene graph
        * @memberof FDNode
        * @function transform
        * @param {THREE.Matrix4} matrix
        */
        public transform(matrix: THREE.Matrix4) {
            this.nodeSceneGraph.applyMatrix(matrix);
        }

        /**
        * Returns the node in the scenegraph
        * @memberof FDNode
        * @function getNodeSceneGraph
        * @returns {THREE.Object3D} scene graph node
        */
        public getNodeSceneGraph(): THREE.Object3D {
            return this.nodeSceneGraph;
        }

        /**
        * Returns all the ids of all meshes in this node of the scene graph
        * @memberof FDNode
        * @function getMeshIds
        * @returns Array of mesh Ids
        */
        public getMeshIds() {
            var meshIds = [];
            for (var m in this.geometryObjects) {
                meshIds.push(this.geometryObjects[m].geometry.id);
            }
            return meshIds;
        }

        /**
        * Rotates the scene graph node around the axis
        * @memberof FDNode
        * @function rotate
        * @param {number} angle in rad
        * @param {THREE.Vector3} axis
        */
        public rotate(angle: number, axis: THREE.Vector3) {
            var matrix = new THREE.Matrix4().makeRotationAxis(axis, angle);

            this.nodeSceneGraph.updateMatrixWorld(true);
            this.nodeSceneGraph.applyMatrix(matrix);
        }

        /**
        * Rotates the meshes of this node individually.
        * @memberof FDNode
        * @function rotateLocal
        * @param {number} angle in rad
        * @param {THREE.Vector3} axis
        */
        public rotateLocal(angle: number, axis: THREE.Vector3) {
            var matrix = new THREE.Matrix4().makeRotationAxis(axis, angle);
            this.nodeSceneGraph.updateMatrixWorld(true);
            for (var c in this.nodeSceneGraph.children) {
                this.nodeSceneGraph.children[c].applyMatrix(matrix);
            }
        }

        /**
        * Returns an array of meshes in this node
        * @memberof FDNode
        * @function getAllGeometry
        * @returns {THREE.Mesh[]} geometry
        */
        public getAllGeometry(): THREE.Mesh[] {
            return this.geometryObjects;
        }

        /**
        * Add a mesh to the node
        * @memberof FDNode
        * @function addGeometry
        * @param {THREE.Mesh} geomObject
        */
        public addGeometry(geomObject: any) {

            this.geometryObjects.push(geomObject);
            this.nodeSceneGraph.add(geomObject);

        }

        /**
        * Sets the possition of the node relative to its position in the scenegraph.
        * @memberof FDNode
        * @function setPosition
        * @param {THREE.Vector3} position
        */
        public setPosition(position: THREE.Vector3) {
            this.nodeSceneGraph.position = position;
        }

        /**
        * Returns current position of the node relative to its position in the scene graph
        * @memberof FDNode
        * @function getPosition
        * @returns {THREE.Vector3} position
        */
        public getPosition() {
            return this.nodeSceneGraph.position;
        }

        /**
       * Returns current position of the node in world space
       * @memberof FDNode
       * @function getWorldCoordinates
       * @returns {THREE.Vector3} position
       */
        public getWorldCoordinates(): THREE.Vector3 {


            var vec = new THREE.Vector3();
            vec.getPositionFromMatrix((<any>this.nodeSceneGraph).matrixWorld);
            return vec;
        }
    }
}
