package
{
    import away3d.objects.WirePlane;
    import away3d.core.material.ISegmentMaterial;
    import away3d.core.scene.ObjectContainer3D;
    import away3d.core.utils.Init;
    import away3d.core.material.WireColorMaterial;
    import away3d.core.mesh.Segment;
    import away3d.core.mesh.Vertex;
    import away3d.core.material.WireframeMaterial;
    import away3d.core.scene.Object3D;
    import away3dPlus.objects.WireCurve;
    import flash.display.BitmapData;
    import away3d.core.sprite.Sprite2D;
    import away3dPlus.objects.Axes;
    import away3d.core.math.Number3D;
    import caurina.transitions.Tweener;
    
    /** Wire plane */ 
    public class Tangle extends ObjectContainer3D
    {
        public var tscene:TangleScene;
        private var wireGuage:Number = 3;

        private var grid:WirePlane;
        private var gridMaterial:ISegmentMaterial
        //private var axes:Axes;
        private var rope0:Rope;
        private var rope1:Rope;
        private var ropeGuage:Number = 20;
        
        private var segmentsW:int = 1;
        private var segmentsH:int = 1;
        private var width:Number = 400;
        private var height:Number = 400;
        private var unit:Number = 200;
        
           private var NE:RopeEnd;
           private var SE:RopeEnd;
           private var NW:RopeEnd;
           private var SW:RopeEnd;
           private var growAxes:Array;
           private var growIndex:int = 0;
           
           private var _p:int = 0;
           private var _q:int = 1;
           private var cfrac:String = "0";
           
        private var origin:Vertex;
    
        public function Tangle(material:ISegmentMaterial, tscene:TangleScene, target:Object3D, init:Object = null)
        {
            name = "Tangle";
            this.tscene = tscene;
            
            init = Init.parse(init);
                       
            width = init.getNumber("width", 400, {min:0});
            height = init.getNumber("height", 400, {min:0});

               //var tunit:Number = 2*unit;
               growAxes = [
                   Number3D.RIGHT, 
                   Number3D.FORWARD, 
                   Number3D.LEFT,
                   Number3D.BACKWARD
               ];
               
               zero();
               
           }
                      
           public function get growAxis():Number3D {
               return growAxes[growIndex] as Number3D;
           }
           
           public function turn():void {
               var t:RopeEnd = NE;
               NE = NW;
               NW = SW;
               SW = SE
               SE = t;
               growIndex = (growIndex + 1) % growAxes.length;
               
               var s:int = p;
               p = -q;
               q = s;
               if(q < 0) {
                   p = -p;
                   q = -q;
               }
               
               cfrac = "-1/" +cfrac;
           }
           
           public function twist():void {
               extendBeforeTwist(+1);
               p += q;
               var t:RopeEnd = NE;
               NE = SE;
               SE = t;
               
               cfrac = "(1 + " + cfrac + ")";
           }
           
           public function untwist():void {
            extendBeforeTwist(-1);
               p -= q;
            var t:RopeEnd = NE;
               NE = SE;
               SE = t;             
               cfrac = "(-1 + " + cfrac + ")";
           }
           
           public function get p():int {
               return _p;
           }
           public function set p(n:int):void {
               _p = n;
               if(tscene.stage != null)
                   tscene.p.text = ""+n;
           }
           
           public function get q():int {
               return _q;
           }
           public function set q(n:int):void {
               _q = n;
               if(tscene.stage != null)
                   tscene.q.text = ""+n;
           }
           
           /**
            * extend the tangle with Zero
            */
           public function zero():void {
               rotationY = 0;
               if(rope0 != null) {
                   removeChild(rope0);
               }
               if(rope1 != null) {
                   removeChild(rope1);
               }
               
               addChild(rope0 = new Rope({topRope:true, thickness:ropeGuage, start:new Number3D(-unit, 0, unit), end:new Number3D(unit, 0, unit)}));
            addChild(rope1 = new Rope({topRope:false, thickness:ropeGuage, start:new Number3D(-unit, 0, -unit), end:new Number3D(unit, 0, -unit)}));
              growIndex = 0;
               NW = rope0.ropeStart;
               NE = rope0.ropeEnd;
               SW = rope1.ropeStart;
               SE = rope1.ropeEnd;
               
               p = 0;
               q = 1;
               cfrac = "0";
               
               //tscene.startTween();
               //Tweener.addTween(this, {rotationY:0, time:2, transition:"easeOutCubic", onComplete:tscene.endTween});

               tscene.resetView();
               
               //tscene.invalidate();
           }
           
           public function extendBeforeTwist(sense:int):void {
            //rope0 = NE.rope;
               //rope1 = SE.rope;
               var edge:Number3D = Number3D.sub(NE.position, SE.position);
               var growth:Number = edge.modulo;
               var growthVector:Number3D = Number3D.scale(growAxis, growth);
               
               //trace("rope0="+rope0 + " NE.position="+NE.position + " growthVector="+growthVector);
               //trace("rope1="+rope1 + " SE.position="+SE.position + " growthVector="+growthVector);
            
            var pos:Number3D = Number3D.add(NE.position, growthVector);
            var v:Vertex = new Vertex(pos.x, pos.y, pos.z);
            if(cfrac != "0")
                NE.extendTo(v, NE.rope.segments);
            trace("NE -> " + pos);
            
               //trace("rope1="+rope1 + " SE.position="+SE.position + " growthVector="+growthVector);
            pos = Number3D.add(SE.position, growthVector);
            v = new Vertex(pos.x, pos.y, pos.z);
            if(cfrac != "0")
                SE.extendTo(v, SE.rope.segments);
            trace("SE -> " + pos);
            
            //
            // Insert Twist
            //
            var upAxis:Number3D = Number3D.cross(edge, growthVector);
            upAxis.normalize(1/growth);
            if(sense > 0) {
                twistEnd(NE, Number3D.scale(upAxis,-1), edge);
                twistEnd(SE, upAxis, Number3D.scale(edge, -1));
            }
            else {
                twistEnd(NE, upAxis, edge);
                twistEnd(SE, Number3D.scale(upAxis,-1), Number3D.scale(edge, -1));                
            }
            
              //tscene.zoomOutView(null);
               tscene.invalidate();
           }
           
           public function twistEnd(end:RopeEnd, upAxis:Number3D, edge:Number3D):void {
               var rope:Rope = end.rope;
               var x0:int = end.index;
               var inc:int = (x0 == 0) ? 1 : -1;
               
               for (var i:int = 0; i < rope.segments+1; i++) {
                   var x:int = Math.round(x0 + i*inc);
                   var v:Vertex = rope.wire.vertex(x);
                   trace(x + " : " + v.position + " : " + (i - rope.segments)/(rope.segments));
                   var lambda:Number = (i-rope.segments)/rope.segments;
                   var edgeMove:Number3D = Number3D.scale(edge, lambda);
                   var upMove:Number3D = Number3D.scale(upAxis, Math.sin(Math.PI*i/rope.segments)/3);
                   var combined:Number3D = Number3D.add(edgeMove, upMove);
                   var newPos:Number3D = Number3D.add(v.position, combined); 
                   trace(x + " : " + v.position + " :-> " + newPos + " lambda="+(i - rope.segments)/(rope.segments));
 
                   //v.position = newPos;                  
                   tscene.startTween();
                   Tweener.addTween(v, {x:newPos.x, y:newPos.y, z:newPos.z, time:1, transition:"easeInOutCubic", onComplete:tscene.endTween});
               }
               end.touch();
           }
                      
           /**
            * Update tangle prior to a render
            */
           public function preRender():void {
               // Recalculate rope widths from zoom factor
               var thickness:Number = ropeGuage/tscene.zoomFactor;
               rope0.thickness = thickness;
               rope1.thickness = thickness;
           }
     }
}