/// <reference path="../FDNode.ts" />
/// <reference path="ConeTreeNode.ts" />
/// <reference path="../resources/jquery.d.ts" />
/// <reference path="../debugPrinter.ts" />
/// <reference path="../resources/three.d.ts" />
/// <reference path="../FDGeomObject.ts" />
/// <reference path="../FDHelpers.ts" />
/// <reference path="../FDVisualisation.ts" />
module FD {
    export class ConeTreeVisualisation extends FDVisualisation {

        private segments: number;
        private rings: number;
        private maxDepth: number;
        private depthPerLayer: number;
        public shadows: THREE.Mesh[];
        public labels: THREE.Sprite[];

        constructor(debug: Boolean, fluidDiagram: any) {
            super(debug, fluidDiagram);
            //EXTRACT THIS
            this.segments = 7;
            this.rings = 7;
            this.maxDepth = 0;
            this.depthPerLayer = -80;
            this.labels = [];
            this.shadows = [];

        }


        public visualise() {
            this.backgroundColor = 0xeeeeee;
            var currentNode: FDNode;

            currentNode = this.nodes[0];//we expect only one root in a cone-tree
            //debugPrinter.print(currentNode.getUniqueId(), this.debug);

            //need to cast ALL nodes to ConeTreeNodes
            //Cast not visible in JS
            var currentConeTreeNode = <ConeTreeNode>currentNode;
            //get space available for children
            currentConeTreeNode.radius = this.getRadius(currentConeTreeNode, 0);


            this.position(currentConeTreeNode, 1);


            //lets add scene elements that are not in visualisation themselves
            //floor
            var floor = new THREE.PlaneGeometry(currentConeTreeNode.radius * 5, currentConeTreeNode.radius * 5);
            var floorMat = new THREE.MeshBasicMaterial({ color: 0x81DAF5 });
            floorMat.side = THREE.DoubleSide;
            var floorMesh = new THREE.Mesh(floor, floorMat);

            var translate = this.depthPerLayer * this.maxDepth - (this.depthPerLayer) - 20;
            transformationMatrix = new THREE.Matrix4().identity();
            transformationMatrix.translate(new THREE.Vector3(0, 0, -translate));
            floorMesh.applyMatrix(transformationMatrix);

            this.fluidDiagrams.addSceneObject(floorMesh);

            //debugPrinter.print("depth:" + this.maxDepth + " depthPerLayer:" + this.depthPerLayer + " translate:" + translate, true);
            var transformationMatrix = new THREE.Matrix4().identity();
            transformationMatrix.rotateX(1.57079633);
            floorMesh.applyMatrix(transformationMatrix);
            //debugPrinter.print("possitioned children", this.debug);


        }
        //reffference: carriere-ivis1995-conetree-self.pdf
        private getRadius(currentNode: ConeTreeNode, depth: number): number {
            //calculate Circumference of n-1 (Currwent Node is n-1 where n=deph of child nodes)
            var currentNodeChildren = currentNode.getChildren();
            currentNode.circumference = 0;
            depth++;
        var _this = this;
            var rememberedRadius = 0;
            if (currentNodeChildren.length > 0) {

                for (var i = 0; i < currentNodeChildren.length; i++) {

                    var radius = _this.getRadius(<ConeTreeNode>currentNodeChildren[i], depth);
                    if (radius >= rememberedRadius) {
                        rememberedRadius = radius;
                    }
                    currentNode.circumference += radius;
                }
            }

            currentNode.circumference *= 2;

            //calculate my Radus
            if (currentNodeChildren.length === 0) {
                currentNode.radius = 20;
            } else {
                //add radius of largest child to own radius PERHAPS NOT NEEDED??? REMOVED
                //Slight overlapping .....
                //as defined in paper
                currentNode.radius = currentNode.circumference / (2 * Math.PI);
            }

            if (this.maxDepth < depth) {

                this.maxDepth = depth;
            }
            return currentNode.radius;
        }

        private position(currentNode: ConeTreeNode, depth) {

            var tmpMat = new THREE.MeshBasicMaterial();

            tmpMat = <any>new THREE.MeshBasicMaterial({ color: 0x5555cc });

            tmpMat.side = THREE.DoubleSide;
            tmpMat.transparent = true;

            var currentNodeChildren = currentNode.getChildren();
            var phi = 0;



            var sphere = new THREE.SphereGeometry(7, this.segments, this.rings);
            var mesh = new THREE.Mesh(sphere, tmpMat);
            currentNode.addGeometry(mesh);


            var textMesh = makeSprite(currentNode.getUniqueId(), 12);
            textMesh.position.x = 0;
            textMesh.position.y = 20;
            textMesh.position.z = 0;
            this.labels.push(textMesh);
            currentNode.getNodeSceneGraph().add(textMesh);
            //currentNode.addGeometry(textMesh);

            if (currentNode.getChildren().length > 0) {
                //add cone to node
                var coneMat = <any>new THREE.MeshBasicMaterial({ transparent: true, color: 0x81DAF5 });
                coneMat.opacity = 0.8;
                coneMat.side = THREE.DoubleSide;
                var cone = new THREE.CylinderGeometry(0, currentNode.radius, -this.depthPerLayer, currentNode.getChildren().length, 1, true);
                var coneMesh = new THREE.Mesh(cone, coneMat);
                currentNode.addGeometry(coneMesh);
                coneMesh.translateY(this.depthPerLayer / 2);

                //add "Shadow"
                var shadow = new THREE.CircleGeometry(currentNode.radius, currentNode.getChildren().length);
                var shadowMat = <any>new THREE.MeshBasicMaterial({ transparent: true, color: 0x333333 });
                shadowMat.side = THREE.DoubleSide;
                shadowMat.opacity = 0.2;
                var shadowMesh = new THREE.Mesh(shadow, shadowMat);
                currentNode.addGeometry(shadowMesh);
                this.shadows.push(shadowMesh);
                //add depth/100 so that they do not intersect
                var translate = (this.maxDepth - depth) * this.depthPerLayer - 19 - (depth / 100);
                var transformationMatrix = new THREE.Matrix4().identity();
                transformationMatrix.rotateX(1.57079633);
                transformationMatrix.translate(new THREE.Vector3(0, 0, -translate));
                shadowMesh.applyMatrix(transformationMatrix);
            }

            for (var i = 0; i < currentNodeChildren.length; i++) {
                var child = <ConeTreeNode>currentNodeChildren[i];

                //calculate arc length:
                if (i === 0) {//first element 
                    child.arcLength = child.radius;
                } else {
                    child.arcLength = (<ConeTreeNode>currentNodeChildren[i - 1]).radius + child.radius;
                }
                //calculate angle
                //incrementaly adding the radius to rotate around the parent once
                child.angle = child.arcLength / currentNode.radius;
                phi += child.angle;

                child.getNodeSceneGraph().translate(currentNode.radius, new THREE.Vector3(1, 0, 0));
                child.getNodeSceneGraph().translate(this.depthPerLayer, new THREE.Vector3(0, 1, 0));

                var newPos = rotateAroundPoint(child.getPosition(), new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 1, 0), phi);
                child.getNodeSceneGraph().position = newPos;

                //ADD LINKS to currentNode SceneGraph
                var linegeom = new THREE.Geometry();
                linegeom.vertices.push(new THREE.Vector3(0, 0, 0));
                linegeom.vertices.push(child.getPosition());
                var line = new THREE.Line(linegeom, new THREE.LineBasicMaterial({ color: 0x0000ff }));
                currentNode.addGeometry(<any>line);

            }


            depth++;

            for (var j = 0; j < currentNodeChildren.length; j++) {
                this.position(<ConeTreeNode>currentNodeChildren[j], depth);
            }


        }

    }
}
