/// <reference path="ParallelCoordinatesVisualisation.ts" />
/// <reference path="PCcolumn.ts" />
/// <reference path="../FDEventHandler.ts" />
/// <reference path="../resources/three.d.ts" />
module FD {
    export class ParallelCoordinatesEventHandler extends FDEventHandler {

        private selectedMesh: THREE.Mesh;
        private mouseDownPosition: THREE.Vector2;
        private columns: ParallelCoordinatesColumn[];
        private activeColumn: ParallelCoordinatesColumn;
        private activeColumnIndex: number;
        private visualisation: FDVisualisation;
        private activeMat;
        private inactiveMat;
        private action;

        constructor(debug: boolean, fluidDiagrams: FluidDiagrams) {
            super(debug, fluidDiagrams);
            this.mouseDown = false;
            this.mouseDownPosition = new THREE.Vector2();

            this.visualisation = this.fluidDiagrams.getVisualisation();
            this.columns = (<ParallelCoordinatesVisualisation>this.visualisation).columns;
            this.activeMat = new THREE.LineBasicMaterial({ color: 0x0000ff });
            this.inactiveMat = new THREE.LineBasicMaterial({ color: 0xcccccc });
            this.activeColumn = null;
            this.selectedMesh = null;
            this.action = null;

        }



        public onMouseDown(e: Event) {

            var clickedElem = this.fluidDiagrams.objectFromMouse((<any>e).clientX, (<any>e).clientY);

            if (clickedElem instanceof THREE.Mesh) {

                clickedElem = <THREE.Mesh>clickedElem;
                this.activeColumn = this.getColumFromClickedHandle(clickedElem);
                if (this.activeColumn != null) {

                    if (this.activeColumn.getLowHandle().geometry.id == clickedElem.geometry.id
                        || this.activeColumn.getHighHandle().geometry.id == clickedElem.geometry.id) {

                        //we have a filter elment
                        //we want to activate drag and drop on filter element
                        this.action = "filter";
                    }
                    if(this.activeColumn.getLabel().geometry.id == clickedElem.geometry.id){
                        this.action = "reorder";
                    }

                    this.selectedMesh = clickedElem;
                    this.mouseDownPosition.x = (<any>e).clientX;
                    this.mouseDownPosition.y = (<any>e).clientY;
                    this.mouseDown = true;
                }

            }
            this.mouseDown = true;

        }

        public onMouseUp(e: Event) {
             //move activeColumn to grid
            if(this.activeColumn!=null){
                var x =  (<ParallelCoordinatesVisualisation>this.visualisation).colWidth * this.activeColumnIndex+(<ParallelCoordinatesVisualisation>this.visualisation).left;
                this.activeColumn.getColumnSceneGraph().position.x = x;
                for (var v in this.activeColumn.vertices) {
                    this.activeColumn.vertices[v].x = this.activeColumn.getColumnSceneGraph().position.x;
                }
                for (var node in this.fluidDiagrams.rootNodes) {
                    var n = <FDNode>this.fluidDiagrams.rootNodes[node];
                    n.getAllGeometry()[0].geometry.verticesNeedUpdate=true;
                }
            }
            this.mouseDown = false;
            this.selectedMesh = null;
            this.activeColumn = null;


            
        }

        public onMouseMove(e: Event) {
            if (this.mouseDown == true && this.selectedMesh != null && this.activeColumn != null) {
                var x = (<any>e).clientX - this.mouseDownPosition.x;
                var y = (<any>e).clientY - this.mouseDownPosition.y;

                //check if movement in range
                //highHandle above lowHandle and in range;
                if (this.action == "filter") {
                    var position = this.selectedMesh.position;
                    if (this.activeColumn.getLowHandle().geometry.id == this.selectedMesh.geometry.id)
                        y = -y;

                    if (position.y - y <= this.activeColumn.getMaxY() && position.y + y >= this.activeColumn.getMinY()) {
                        //check highHandle above LowHandle
                        if (this.activeColumn.getLowHandle().position.y + y < this.activeColumn.getHighHandle().position.y) {
                            this.selectedMesh.translateY(y);
                            this.activeColumn.setFilterBottom(this.activeColumn.getLowHandle().position.y - this.activeColumn.getLowHandle().scale.y);
                            this.activeColumn.setFilterTop(this.activeColumn.getHighHandle().position.y - this.activeColumn.getHighHandle().scale.y * 3);
                        }

                    }

                    this.filter();
                }
                if(this.action == "reorder"){
                    //debugPrinter.print("active column: "+this.activeColumn.cName,true);
                    this.reorderColumns(x);
                    //this.selectedMesh.translateX(x);
                }


                this.mouseDownPosition.x = (<any>e).clientX;
                this.mouseDownPosition.y = (<any>e).clientY;
            }
        }

        private reorderColumns(x: number){
            this.activeColumn.getColumnSceneGraph().translateX(x);
            var colwidth = (<ParallelCoordinatesVisualisation>this.visualisation).colWidth;
            var left = (<ParallelCoordinatesVisualisation>this.visualisation).left;

            //check neighboring columns
            //not first column
            //move left
            if(this.activeColumnIndex>0){
                //debugPrinter.print("col neighbour "+this.columns[this.activeColumnIndex-1].cName,true);
                if(this.columns[this.activeColumnIndex-1].getColumnSceneGraph().position.x>this.activeColumn.getColumnSceneGraph().position.x){
                    this.columns[this.activeColumnIndex-1].getColumnSceneGraph().position.x = colwidth * this.activeColumnIndex+left;
                    //reorder columns
                    var tmpColumn = this.columns[this.activeColumnIndex-1]
                    this.columns[this.activeColumnIndex-1] = this.activeColumn;
                    this.columns[this.activeColumnIndex] = tmpColumn;

                    this.activeColumnIndex = this.activeColumnIndex-1;
                    this.redrawLines();
                    this.filter();
                }
            }

            //notLastColumn
            if(this.activeColumnIndex<this.columns.length-1){
                if(this.columns[this.activeColumnIndex+1].getColumnSceneGraph().position.x<this.activeColumn.getColumnSceneGraph().position.x){
                    this.columns[this.activeColumnIndex+1].getColumnSceneGraph().position.x = colwidth * this.activeColumnIndex+left;
                    //reorder columns
                    var tmpColumn = this.columns[this.activeColumnIndex+1]
                    this.columns[this.activeColumnIndex+1] = this.activeColumn;
                    this.columns[this.activeColumnIndex] = tmpColumn;

                    this.activeColumnIndex = this.activeColumnIndex+1;
                    this.redrawLines();
                    this.filter();
                }
            }
                        
            // redraw/update lines
           for (var v in this.activeColumn.vertices) {
            this.activeColumn.vertices[v].x = this.activeColumn.getColumnSceneGraph().position.x;
           }

            for (var node in this.fluidDiagrams.rootNodes) {
                var n = <FDNode>this.fluidDiagrams.rootNodes[node];
                n.getAllGeometry()[0].geometry.verticesNeedUpdate=true;
            }


        }

        public mouseWheel(e: Event) {
            //debugPrinter.print("MouseWheel not implemented", this.debug);
        }

        public onMouseClick(e: Event, element) {
            
            if (element != undefined) {
                this.activeColumn = this.getColumFromClickedHandle(element);
                
                if (this.activeColumn != null) {
                    if (element instanceof THREE.Mesh) {
                        element = <THREE.Mesh>element;
                        if (element.geometry.id == this.activeColumn.getSortHandle().geometry.id) {
                            //we have a sort handle clicked
                            //reverse the elements and redraw the lines
                            this.reverseActiveColumnAndRedraw();
                        }
                    }
                }
            }
        }


        public update() {
            //animation update here
        }

        private getColumFromClickedHandle(h: THREE.Mesh): ParallelCoordinatesColumn {
            var index = 0;
            for (var c in this.columns) {
                var col = <ParallelCoordinatesColumn>this.columns[c];
                if (col.getLowHandle().geometry.id == h.geometry.id || col.getHighHandle().geometry.id == h.geometry.id
                    || col.getSortHandle().geometry.id == h.geometry.id || col.getLabel().geometry.id==h.geometry.id){
                    this.activeColumnIndex = index;
                    return this.columns[c];
                }
                index++;
            }

            return null;
        }


        private filter() {


            for (var node in this.fluidDiagrams.rootNodes) {
                var n = <FDNode>this.fluidDiagrams.rootNodes[node];
                //reset to default Mat
                n.getAllGeometry()[0].material = this.activeMat;
                for (var c in this.columns) {
                    var val = this.columns[c].entries[n.getUniqueId()];
                    if (val > this.columns[c].getFilterTop() || val < this.columns[c].getFilterBottom()) {
                        n.getAllGeometry()[0].material = this.inactiveMat;
                    }
                }
            }
        }

        private reverseActiveColumnAndRedraw() {
            this.activeColumn.reverseElements();
            //reverse lables
            var tmpPos = this.activeColumn.getTopLable().position;
            this.activeColumn.getTopLable().position = this.activeColumn.getBottomLable().position;
            this.activeColumn.getBottomLable().position = tmpPos;

            this.redrawLines();

            //update filters
            this.filter();


        }

        private redrawLines(){

            for (var node in this.fluidDiagrams.rootNodes) {

                var n = <FDNode>this.fluidDiagrams.rootNodes[node];
                var geom = n.getAllGeometry();
                n.getNodeSceneGraph().remove(geom[0]);
                geom.splice(0, 1);
            }


            var i = 0;
            for (var n_ in this.fluidDiagrams.rootNodes) {
                var linegeom = new THREE.Geometry();
                var colNum = 0;
                for (var col in this.columns) {
                    var x = this.columns[col].getColumnSceneGraph().position.x;
                    var y = this.columns[col].entries[this.fluidDiagrams.rootNodes[n_].getUniqueId()]+(<ParallelCoordinatesVisualisation>this.visualisation).bottom;
                    var z = 0;
                    var vert = new THREE.Vector3(x,y,z);
                    this.columns[col].vertices[i] = vert;
                    linegeom.vertices.push(vert);
                    colNum++;
                }
                var line = new THREE.Line(linegeom, new THREE.LineBasicMaterial({ color: 0x0000ff }));
                line.geometry.dynamic=true;
                this.fluidDiagrams.rootNodes[n_].addGeometry(line);
            i++;
            }
        }

    }
}
