var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
/// <reference path="../FDHelpers.ts" />
/// <reference path="../FDNode.ts" />
/// <reference path="TreeNode.ts" />
/// <reference path="TreeGraph.ts" />
/// <reference path="../FDVisualisation.ts" />
/// <reference path="../FDEventHandler.ts" />
var FD;
(function (FD) {
    var HypB = (function (_super) {
        __extends(HypB, _super);
        function HypB(debug, fluidDiagram) {
            _super.call(this, debug, fluidDiagram);
            this.animating = false;
            this.stepcount = 30;
            this.currentstep = 0;
            this.mousedown = false;
            this.draged = false;
            this.useLabels = false;
            this.OFFSET = 0;
            this.meshToNodes = {};

            //this.hyperbolicLines = [];
            this.PANELSIZE = this.fluidDiagrams.width;

            this.RADIUS = this.PANELSIZE / 2 - this.OFFSET;
            this.treeanglebisection_ = 0.5 * Math.PI;
            this.treeanglewidth_ = 1.98 * Math.PI;
            this.treelayouttranslation_ = new THREE.Vector2(0, 0);
            this.length_ = 0.45;
            this.labelDistance_ = this.RADIUS * (this.length_ + 0.1);
            //this.labelDistance_ = this.length_;
        }
        HypB.prototype.visualise = function () {
            this.backgroundColor = 0xeeeeee;

            //create treeGraph
            this.rootNode_ = new FD.TreeNode(this.nodes[0], this.useLabels);
            this.meshToNodes[this.rootNode_.icon_.id.toString()] = this.rootNode_;

            //this.treegraph_ = new TreeGraph(this.rootNode_);
            //console.log(this.fluidDiagrams.rootNodes[0].getUniqueId()+" has: "+this.fluidDiagrams.rootNodes[0].getChildren().length);
            //for(var fdChild in this.fluidDiagrams.rootNodes[0].getChildren()){
            this.createTreeGraph(this.fluidDiagrams.rootNodes[0], this.rootNode_);

            //}
            this.fluidDiagrams.addSceneObject(this.rootNode_.icon_); //Add Icon to Scene
            this.fluidDiagrams.addSceneObject(this.rootNode_.label); //Add Label to Scene

            //console.log(this.allNodes.length);
            this.treegraph_ = new FD.TreeGraph(this.rootNode_);
            this.layoutHyperbolicTree();
            this.adaptLayout();
        };

        HypB.prototype.createTreeGraph = function (currentRoot, parentTreeNode) {
            for (var fdChild in currentRoot.getChildren()) {
                //console.log(currentRoot.getUniqueId()+" has: "+currentRoot.getChildren().length);
                var currentTreeNode = new FD.TreeNode(currentRoot.getChildren()[fdChild], this.useLabels);
                currentTreeNode.setParent(parentTreeNode);
                parentTreeNode.addChild(currentTreeNode);

                this.fluidDiagrams.addSceneObject(currentTreeNode.icon_); //Add Icon to Scene
                this.fluidDiagrams.addSceneObject(currentTreeNode.label); //Add Label to Scene

                //MAP MeshID to treeNode
                this.meshToNodes[currentTreeNode.icon_.id.toString()] = currentTreeNode;

                //add lines from currentTreeNode to current Root
                var line = new THREE.Geometry();
                line.vertices.push(parentTreeNode.linepos_);
                line.vertices.push(currentTreeNode.linepos_);
                line.dynamic = true;
                line.verticesNeedUpdate = true;

                //this.hyperbolicLines.push(line);
                currentTreeNode.line_ = line;

                var line_ = new THREE.Line(line, new THREE.LineBasicMaterial({ color: 0xcccccc }));
                this.fluidDiagrams.addSceneObject(line_);

                this.createTreeGraph(currentRoot.getChildren()[fdChild], currentTreeNode);
            }
        };

        HypB.prototype.layoutHyperbolicTree = function () {
            this.computeWeight(this.rootNode_);

            var wedge = new FD.HBWedge();
            wedge.alpha_ = this.treeanglebisection_ - this.treeanglewidth_ / 2.0;
            if (wedge.alpha_ < 0)
                wedge.alpha_ += 2 * Math.PI;
            wedge.omega_ = this.treeanglebisection_ + this.treeanglewidth_ / 2.0;
            if (wedge.omega_ > 2 * Math.PI)
                wedge.omega_ -= 2 * Math.PI;

            this.layoutHBNode(this.rootNode_, wedge, this.length_);

            // move tree acording to the treelayoutdir
            var translation = new FD.HBPoint(this.treelayouttranslation_.x, this.treelayouttranslation_.y);
            for (var i = 0; i < this.treegraph_.getTreeNodeCount(); i++) {
                var treenode = this.treegraph_.getTreeNode(i);
                treenode.z.translate(0, 0, translation);
            }

            for (var i = 0; i < this.treegraph_.getTreeNodeCount(); i++) {
                var treenode = this.treegraph_.getTreeNode(i);
                treenode.oldz.x = treenode.z.x;
                treenode.oldz.y = treenode.z.y;
                treenode.origz.x = treenode.z.x;
                treenode.origz.y = treenode.z.y;
                //console.log(treenode.icon_.position.x);
            }
        };

        HypB.prototype.computeWeight = function (treenode) {
            for (var i = 0; i < treenode.getChildCount(); i++) {
                var child = treenode.getChildNode(i);

                // compute weight recusively for all descendants
                this.computeWeight(child);

                // add the weight to this node
                treenode.childrenweights += child.weight;
            }

            // use the logarithmic value for further calculation
            if (treenode.childrenweights != 0.0)
                treenode.weight += Math.log(treenode.childrenweights);
        };

        HypB.prototype.layoutHBNode = function (treenode, wedge, length) {
            // layout the node
            // nothing to do for the root node
            var parent = treenode.getParentNode();
            if (parent != null) {
                var angle = wedge.getBisectionAngle();

                // We first start as if the parent was the origin.
                // We still are in the hyperbolic space.
                treenode.z.x = length * Math.cos(angle);
                treenode.z.y = length * Math.sin(angle);

                // translate the point and the wedge by the parents coordinates
                treenode.z.translate(0, 0, parent.z);
                wedge.translate(0, 0, parent.z);

                // translate the wedge by the negitive points coordinates
                wedge.translate(-treenode.z.x, -treenode.z.y);
            }

            // debug information
            // treenode.infotext_ = wedge.toString ();
            // check, if the wedge is not to big
            var wedgeangle = wedge.getAngleWidth();
            if (wedgeangle > this.treeanglewidth_) {
                var anglediff = wedgeangle - this.treeanglewidth_;
                wedge.alpha_ += anglediff / 2.0;
                if (wedge.alpha_ > 2 * Math.PI)
                    wedge.alpha_ -= 2 * Math.PI;
                wedge.omega_ -= anglediff / 2.0;
                if (wedge.omega_ < 0)
                    wedge.omega_ += 2 * Math.PI;
                wedgeangle = this.treeanglewidth_;
            }

            // layout for the child nodes and their subtrees
            var currentangle = wedge.alpha_;
            for (var i = 0; i < treenode.getChildCount(); i++) {
                var childnode = treenode.getChildNode(i);
                var childWedge = new FD.HBWedge();

                // calculate the wedge for the child
                childWedge.alpha_ = currentangle;
                currentangle += (wedgeangle * (childnode.weight / treenode.childrenweights));
                if (currentangle > 2 * Math.PI)
                    currentangle -= 2 * Math.PI;
                childWedge.omega_ = currentangle;

                // layout the child node within its wedge
                this.layoutHBNode(childnode, childWedge, length);
            }
        };

        HypB.prototype.adaptLayout = function () {
            for (var i = 0; i < this.treegraph_.getTreeNodeCount(); i++) {
                var treenode = this.treegraph_.getTreeNode(i);
                var parentnode = treenode.getParentNode();

                treenode.ppcirclelayout_.setCartesian(treenode.z.x, treenode.z.y);
                treenode.ppcirclelayout_.length_ *= this.RADIUS;

                treenode.renderpos_.x = treenode.ppcirclelayout_.getPosX() + this.RADIUS + this.OFFSET;
                treenode.renderpos_.y = treenode.ppcirclelayout_.getPosY() + this.RADIUS + this.OFFSET;
                treenode.linepos_.x = treenode.renderpos_.x;
                treenode.linepos_.y = treenode.renderpos_.y;
                treenode.linepos_.z = -4;

                //update lines
                if (treenode.line_) {
                    treenode.line_.verticesNeedUpdate = true;
                }

                //for (var line in this.hyperbolicLines) {
                //      this.hyperbolicLines[line].verticesNeedUpdate = true;
                //}
                //console.log(treenode.ppcirclelayout_.length_ + " "+dist);
                if (this.useLabels == true) {
                    if (treenode.ppcirclelayout_.length_ > this.labelDistance_) {
                        treenode.label.visible = false;
                    } else {
                        treenode.label.visible = true;
                    }
                }
            }
        };

        HypB.prototype.updateFocus = function () {
            this.focusnode_ = this.treegraph_.getFocusNode();
            if (this.focusnode_ == null)
                return false;
            this.posx_ = this.focusnode_.z.x;
            this.posy_ = this.focusnode_.z.y;
            this.stepx = this.posx_ / this.stepcount;
            this.stepy = this.posy_ / this.stepcount;

            this.animating = true;
        };

        HypB.prototype.moveCorrected = function (zs, ze) {
            var alpha = new FD.HBPoint();
            var beta = new FD.HBPoint();
            var isvalid = this.calcualteAlphBeta(zs, ze, alpha, beta);
            if (!isvalid)
                return;

            var rootnode = this.treegraph_.getRootNode();

            for (var i = 0; i < this.treegraph_.getTreeNodeCount(); i++) {
                var treenode = this.treegraph_.getTreeNode(i);
                treenode.z.x = treenode.oldz.x;
                treenode.z.y = treenode.oldz.y;
                treenode.z.specialTrans(alpha, beta);
            }
            /*
            // no rotation on radion layout
            if (treelayoutdir_ == TREELAYOUTDIR_RADIAL)
            return;
            
            // check position for directed layouts
            double epsilon = 0.05;
            HBPoint trans = null;
            if (treelayoutdir_ == TREELAYOUTDIR_RIGHT && rootnode.z.x > -epsilon)
            trans = new HBPoint (0, -rootnode.z.y);
            else if (treelayoutdir_ == TREELAYOUTDIR_LEFT && rootnode.z.x < epsilon)
            trans = new HBPoint (0, -rootnode.z.y);
            else if (treelayoutdir_ == TREELAYOUTDIR_TOP && rootnode.z.y < epsilon)
            trans = new HBPoint (0, -rootnode.z.x);
            else if (treelayoutdir_ == TREELAYOUTDIR_BOTTOM && rootnode.z.y > -epsilon)
            trans = new HBPoint (0, -rootnode.z.x);
            
            */
        };

        HypB.prototype.calcualteAlphBeta = function (zs, ze, alpha, beta) {
            var rootnode = this.treegraph_.getRootNode();
            var zo = new FD.HBPoint(-rootnode.oldz.x, -rootnode.oldz.y);
            var zs1 = new FD.HBPoint(zs.x, zs.y);
            zs1.translate(0, 0, zo);

            var t = new FD.HBPoint();
            var de = ze.d2();
            var ds = zs1.d2();
            var dd = 1.0 - de * ds;
            t.x = (ze.x * (1.0 - ds) - zs1.x * (1.0 - de)) / dd;
            t.y = (ze.y * (1.0 - ds) - zs1.y * (1.0 - de)) / dd;

            if (!t.isValid()) {
                //console.log("t not valid.");
                return false;
            }

            // alpha = 1 + conj(zo)*t
            alpha.x = 1 + (zo.x * t.x) + (zo.y * t.y);
            alpha.y = (zo.x * t.y) - (zo.y * t.x);

            // beta = zo + t
            beta.x = zo.x + t.x;
            beta.y = zo.y + t.y;

            return true;
        };

        HypB.prototype.drag = function (dragstartx, dragstarty, posx, posy) {
            var scale = this.PANELSIZE;

            //console.log(scale);
            dragstartx = dragstartx / scale - 1;
            dragstarty = dragstarty / scale - 1;
            posx = posx / scale - 1;
            posy = posy / scale - 1;

            //dragx = dragx / 250.0;
            //dragy = dragy / 250.0;
            // drag from
            var ppoint = new FD.PolarPoint2D();
            ppoint.setCartesian(dragstartx, dragstarty);

            //console.log(ppoint.length_);
            if (ppoint.length_ > 0.99)
                return;

            // drag to
            ppoint = new FD.PolarPoint2D();
            ppoint.setCartesian(posx, posy);
            if (ppoint.length_ > 0.99) {
                ppoint.length_ = 0.99;
                posx = ppoint.getPosX();
                posy = ppoint.getPosY();
            }

            var zs = new FD.HBPoint(dragstartx, dragstarty);
            var ze = new FD.HBPoint(posx, posy);

            // test
            //zs = new HBPoint (posx - dragx, posy - dragy);
            this.moveCorrected(zs, ze);

            this.adaptLayout();
            //transform ();
        };

        HypB.prototype.dragFinisched = function () {
            for (var i = 0; i < this.treegraph_.getTreeNodeCount(); i++) {
                var treenode = this.treegraph_.getTreeNode(i);
                treenode.oldz.x = treenode.z.x;
                treenode.oldz.y = treenode.z.y;
            }
        };

        /*
        
        private transform ()
        {
        if (this.treegraph_.getRootNode () == null)
        return;
        
        
        // node size information
        for (var i = 0; i < this.treegraph_.getTreeNodeCount (); i++)
        {
        var treenode = this.treegraph_.getTreeNode (i);
        
        // location
        var distance = Point2D.distance (OFFSET + RADIUS, OFFSET + RADIUS, treenode.renderpos_.x, treenode.renderpos_.y);
        var sizefactor = 1 - 0.7 * distance / (OFFSET + RADIUS);
        treenode.setIconSizeFactor (sizefactor);
        }
        
        }
        */
        //==============EVENT HANDLER====================0
        HypB.prototype.onMouseDown = function (e) {
            this.mousedown = true;
            var offsetLeft = this.fluidDiagrams.domElement.getBoundingClientRect().left;
            var offsetTop = this.fluidDiagrams.domElement.getBoundingClientRect().top;

            /*
            var eltx = (<any>e).clientX - offsetLeft;
            var elty = (<any>e).clientY - offsetTop;
            
            this.dragstartx= ((eltx / this.fluidDiagrams.domElement.width) * 2 - 1);
            this.dragstarty= (-(elty / this.fluidDiagrams.domElement.height) * 2 + 1);
            */
            this.dragstartx = e.clientX - offsetLeft;
            this.dragstarty = this.fluidDiagrams.height + offsetTop - e.clientY;
        };

        HypB.prototype.onMouseUp = function (e) {
            this.mousedown = false;

            this.dragFinisched();
        };

        HypB.prototype.onMouseMove = function (e) {
            if (this.mousedown) {
                this.draged = true;
                var offsetLeft = this.fluidDiagrams.domElement.getBoundingClientRect().left;
                var offsetTop = this.fluidDiagrams.domElement.getBoundingClientRect().top;

                /*
                var eltx = (<any>e).clientX - offsetLeft;
                var elty = (<any>e).clientY - offsetTop;
                
                var dragToX = ((eltx / this.fluidDiagrams.domElement.width) * 2 - 1);
                var dragToY =(-(elty / this.fluidDiagrams.domElement.height) * 2 + 1);
                */
                var dragToX = e.clientX - offsetLeft;
                var dragToY = this.fluidDiagrams.height - (e.clientY - offsetTop);

                this.drag(this.dragstartx, this.dragstarty, dragToX, dragToY);
                // for (var line in this.hyperbolicLines) {
                //     this.hyperbolicLines[line].verticesNeedUpdate = true;
                // }
            }
        };

        HypB.prototype.mouseWheel = function (e) {
            //debugPrinter.print("MouseWheel not implemented", this.debug);
            //this.length_-=0.1;
            //this.labelDistance_ += this.RADIUS*0.1;
            //this.adaptLayout ();
            //this.updateFocus();
        };

        HypB.prototype.onMouseClick = function (e, element) {
            if (this.draged) {
                this.draged = false;
                return;
            }

            //IF a node wa clicked element is a FDNode
            //Else it is the clicked Mesh
            if (element != null && element instanceof THREE.Mesh) {
                var focus = this.meshToNodes[element.id.toString()];
                if (focus != null) {
                    this.treegraph_.setFocusNode(focus);
                    this.updateFocus();
                }
            }
            //console.log("focusNode: "+this.treegraph_.focusnode_.name_+" meshID: "+element.id.toString());
        };

        HypB.prototype.update = function () {
            if (this.animating == true && this.currentstep <= this.stepcount) {
                var startposx = this.posx_ - this.currentstep * this.stepx;
                var startposy = this.posy_ - this.currentstep * this.stepy;
                var endposx = this.posx_ - (this.currentstep + 1) * this.stepx;
                var endposy = this.posy_ - (this.currentstep + 1) * this.stepy;
                var zstart_ = new FD.HBPoint(startposx, startposy);
                var zend_ = new FD.HBPoint(endposx, endposy);

                this.moveCorrected(zstart_, zend_);
                this.dragFinisched();
                this.adaptLayout();

                //this.transform ();
                this.currentstep++;
            }
            if (this.currentstep > this.stepcount) {
                this.posx_ = 0;
                this.posy_ = 0;
                this.stepx = 0;
                this.stepy = 0;
                this.currentstep = 0;
                this.animating = false;
            }
        };
        return HypB;
    })(FD.FDVisualisation);
    FD.HypB = HypB;
})(FD || (FD = {}));
