(function()
{

var w=950, h;
var dragged = null;
var dragXOff = 0, dragYOff = 0;
var paper;

var controls;

// non-raphael global event binding helper.
function globalEvent(name, fn)
{
    var prop = "on" + name;

    var obj = name == "load" ? window : document;
    
    var old = obj[prop]; 
    if (old)
    {
        obj[prop] = function()
        {
            old();
            fn();            
        };
    }
    else
    {
        obj[prop] = fn;
    }
}

function grabObject(ev)
{
    //console.debug("grabObject %o", ev);
    dragged = this;
    dragXOff = ev.clientX ; 
    dragYOff = ev.clientY ; 
    this.attr({"stroke-width":2, stroke: "#800"});
    return false;
}

function releaseObject(ev)
{
    //console.debug("releaseObject %o", ev);
    if (dragged)
    {
        dragged.attr({"stroke-width":1, stroke: "#ccc"});
        dragged = null;
    }
    return false;
}

function moveObject(ev)
{
    //console.debug("move %d,%d", ev.clientX, ev.clientY);
    if (dragged)
    {
        var dx = ev.clientX - dragXOff;
        var dy = ev.clientY - dragYOff;
        dragged.translate(dx,dy);

        dragXOff += dx;
        dragYOff += dy;

        updatePaths();
    }
    return false;
}

var paths = [];


function distance(x0,y0,x1,y1)
{
    var dx = x1-x0;
    var dy = y1-y0;
    return Math.sqrt( dx * dx + dy * dy);
}

// calculate angle at first point by applying the http://en.wikipedia.org/wiki/Law_of_cosines
function angleAtPoint(x0,y0,x1,y1,x2,y2)
{
    var b = distance(x0,y0,x2,y2);
    var a = distance(x1,y1,x2,y2);
    var c = distance(x1,y1,x0,y0);
    return Math.acos( ( a*a - b*b - c*c) / ( -2 * b * c ) );
}

// find the intersectio
function intersect(x0,y0,a0,x1,y1,a1)
{
    var x,y;
    
    var a2 = Math.PI - a0 - a1;
    
    // figure out length of the inner triangle side from p0 with http://en.wikipedia.org/wiki/Law_of_sines
    var l = distance(x0,y0,x1,y1) * Math.sin(a1) / Math.sin(a2)
    
    // use atan2 to get the angle in screen space, substract angle to p0
    var r = Math.atan2(y1-y0,x1-x0) - a0;
    // calculate point of intersection and return it
    return { x: x0 + l * Math.cos(r), y: y0 + l * Math.sin(r)};
}

// use the sign of the vector cross product's Z component to figure out if our points are clockwise or counter-clockwise
function clockwise(x0,y0,x1,y1,x2,y2)
{
    var a1 = x2 - x0;
    var a2 = y2 - y0;
    var b1 = x1 - x0;
    var b2 = y1 - y0;
    
    return (a1 * b2 - a2 * b1) <= 0;    
}

function updatePaths()
{
    var x0 = controls[0].attr("cx");
    var y0 = controls[0].attr("cy");
    var x1 = controls[1].attr("cx");
    var y1 = controls[1].attr("cy");
    var x2 = controls[2].attr("cx");
    var y2 = controls[2].attr("cy");
    
    var isClockwise = clockwise(x0,y0,x1,y1,x2,y2);
    if (isClockwise)
    {
        // if we're not clockwise, swap p0 and p1 to be clockwise again
        var hx,hy;
        hx = x0;
        hy = y0;
        x0 = x1;
        y0 = y1;
        x1 = hx;
        y1 = hy;
    }
    
        
    var alpha = angleAtPoint(x0,y0,x1,y1,x2,y2);
    var beta = angleAtPoint(x1,y1,x2,y2,x0,y0)
    var gamma = angleAtPoint(x2,y2,x0,y0,x1,y1);
    
    
    var p20 = intersect( x2, y2, gamma/3, x0, y0, alpha/3 );
    var p01 = intersect( x0, y0, alpha/3, x1, y1, beta/3 );
    var p12 = intersect( x1, y1, beta/3, x2, y2, gamma/3 );

//     log some angles etc. ( with JavaScript numbers, teh sum of all angles in a triangle is only kind of 180 degrees ;)    
//    var deg2rad = Math.PI / 180;
//    console.debug("alpha = %d, beta = %d, gamma = %d => sum %d" , alpha / deg2rad  , beta / deg2rad, gamma / deg2rad, (alpha + beta + gamma) / deg2rad );
//    console.debug("p20 : %o", p20 );
//    console.debug("p01 : %o", p01 );
//    console.debug("p12 : %o", p12 );
    
    var col = isClockwise ? "#00c" : "#a00";
    
    var newPaths = [
        {path:"M" + x0 + "," + y0 + "L" + x1 + "," + y1},
        {path:"M" + x1 + "," + y1 + "L" + x2 + "," + y2},
        {path:"M" + x2 + "," + y2 + "L" + x0 + "," + y0},
        {path:"M" + x0 + "," + y0 + "L" + p20.x + "," + p20.y, stroke: "#999"},
        {path:"M" + x2 + "," + y2 + "L" + p20.x + "," + p20.y, stroke: "#999"},
        {path:"M" + x0 + "," + y0 + "L" + p01.x + "," + p01.y, stroke: "#999"},
        {path:"M" + x1 + "," + y1 + "L" + p01.x + "," + p01.y, stroke: "#999"},
        {path:"M" + x1 + "," + y1 + "L" + p12.x + "," + p12.y, stroke: "#999"},
        {path:"M" + x2 + "," + y2 + "L" + p12.x + "," + p12.y, stroke: "#999"},

        {path:"M" + p20.x + "," + p20.y + "L" + p01.x + "," + p01.y, stroke: col, "stroke-width": 2},
        {path:"M" + p01.x + "," + p01.y + "L" + p12.x + "," + p12.y, stroke: col, "stroke-width": 2},
        {path:"M" + p12.x + "," + p12.y + "L" + p20.x + "," + p20.y, stroke: col, "stroke-width": 2}
        ];

    //console.debug("newPaths = %o", newPaths);
    
    for (var i = 0; i < newPaths.length ; i++)
    {
        var path = paths[i];
        if (!path)
        {
            path = paths[i] = paper.path();
        }
        path.attr(newPaths[i]);
    }    
}

function createControls()
{
    var centerY = h/2;

    var s = (w < h ? w : h) / 2 * 0.95;

    var sin = Math.sin, cos = Math.cos;

    var a = Math.PI * 2 / 3;
    
    var x0 = w/2 , y0 = centerY + s;  
    var x1 = w/2 + sin(a) * s, y1 = centerY + cos(a) * s;  
    var x2 = w/2 + sin(-a) * s, y2 = centerY + cos(-a) * s;  

    //console.log("size = %d, centerY = %d", s, centerY);
    //console.log("x0 = %d, y0 = %d", x0, y0);
    //console.log("x1 = %d, y1 = %d", x1, y1);
    //console.log("x2 = %d, y2 = %d", x2, y2);

    var controlSize = 8;
    var controlAttrs = { fill: "#eee", stroke: "#ccc"};
    return [paper.circle(x0,y0,controlSize).attr(controlAttrs), paper.circle(x1,y1,controlSize).attr(controlAttrs), paper.circle(x2,y2,controlSize).attr(controlAttrs)];
}

globalEvent("load", function()
{
    h = window.innerHeight;
    //console.debug("h = %d", h);  
    paper = Raphael("holder", w, h);
    paper.text(w/2,20, "drag controls to change triangles")
    controls = createControls();
//    controls[0].attr( { fill:"#800" } );
//    controls[1].attr( { fill:"#080" } );
//    controls[2].attr( { fill:"#008" } );
    for (var i = controls.length-1; i >= 0; i--)
    {
        controls[i].mousedown(grabObject);
    }

    updatePaths();

    globalEvent("mouseup", releaseObject);
    globalEvent("mousemove", moveObject);
}); 
})();

