/* Copyright (c) 2010 David Rasch 
 */

var RECTSIZE = 9;

/* math functions */

function distance(x1, y1, x2, y2) {
    return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}

/* storage functions */

function supports_local_storage() {
  return ('localStorage' in window) && window['localStorage'] !== null;
}

/* point object */

var Point = function Point(x, y) {
    this.x = x;
    this.y = y;
};

Point.prototype.draw = function (ctx) {
    ctx.fillRect(this.x - RECTSIZE, this.y - RECTSIZE, RECTSIZE * 2 + 1, RECTSIZE * 2 + 1);
};

Point.prototype.hit = function (x, y) {
    return ((this.x >= x - RECTSIZE) && (this.x <= x + RECTSIZE) && 
	    (this.y >= y - RECTSIZE) && (this.y <= y + RECTSIZE));
};

/* affine transformations see apple's documentation */
Point.prototype.transform = function (a, b, c, d, dx, dy) {
    return new Point(this.x * a + this.y * c + dx, this.x * b + this.y * d + dy);
};

Point.prototype.rotate = function (alfa) {
    var ca = Math.cos(alfa);
    var sa = Math.sin(alfa);
    return this.transform(ca, sa, -sa, ca);
};

Point.prototype.scale = function (scale) {
    return this.transform(scale, 0, 0, scale);
};

Point.prototype.translate = function (tx, ty) {
    return this.transform(1, 0, 0, 1, tx, ty);
};


/* line object */

var Line = function Line(points, dx, dy) {
    this.points = points;
    this.dx = dx;
    this.dy = dy;
};

Line.prototype.draw = function (ctx) {
    var k;
    var p = this.points[0];
    ctx.beginPath();
    ctx.moveTo(p.x, p.y);
    for (k = 1; k < this.points.length; k++) {
        p = this.points[k];
        ctx.lineTo(p.x, p.y);
    };
    ctx.stroke();
};

Line.prototype.drawPoints = function (ctx) {
    var k;
    for (k = 1; k < this.points.length - 1; k++) {
        this.points[k].draw(ctx);
    };
};

Line.prototype.breakline = function (p1, p2, cx, cy) {
    var dx = p2.x - p1.x;
    var dy = p2.y - p1.y;
    var r = distance(0, 0, dx, dy);
    var d;
    if (r !== 0) {
        d = (cx * dy - cy * dx + dx * p2.y - dy * p2.x) / r;
        if ((Math.abs(d) < RECTSIZE) && 
	    (distance(p1.x, p1.y, cx, cy) < r) &&
	    (distance(p2.x, p2.y, cx, cy) < r)) {
            return true;
        };
    };
    return false;
};

Line.prototype.hit = function (x, y) {
    var k;
    for (k = 0; k < this.points.length; k++) {
        if (this.points[k].hit(x, y)) {
            if (k > 0 && k < this.points.length - 1) {
                return k;
            }
            else {
                return null;
            };
        }
        else {
            if (k !== 0) {
                if (this.breakline(this.points[k - 1], this.points[k], x, y)) {
                    this.points.splice(k, 0, new Point(x, y));
                    return k;
                };
            };
        };
    };
    return null;
};

/* tessellation figure object */

var TessellationFigure = function TessellationFigure() {
    this.lines = [];
    this.description = "";
};

TessellationFigure.prototype.draw = function () {
    var i;
    var canvas = $("#tessellation").get(0);
    var cline;

    if (canvas.getContext) {
        var ctx = canvas.getContext('2d');
        // create an canvas equal the size of css
        canvas.width = 320;
        canvas.height = 378;
        for (i = 0; i < this.lines.length; i++) {
            ctx.save();
            cline = this.lines[i];
            cline.draw(ctx);
            cline.drawPoints(ctx);

            ctx.translate(cline.dx, cline.dy);
            cline.draw(ctx);
            cline.drawPoints(ctx);
            ctx.restore();
        };
    };
};

TessellationFigure.prototype.drawt = function () {
    var i, j, ux, uy;
    var canvas = $("#tessellation2").get(0);
    var cline;
    var allpoints = [];
    for (i = 0; i < this.lines.length; i++) {
        cline = this.lines[i];
        for (j = 0; j < cline.points.length; j++) {
            allpoints.push(cline.points[j]);
        }
    };
    for (i = 0; i < this.lines.length; i++) {
        cline = this.lines[i];
        for (j = 0; j < cline.points.length; j++) {
            allpoints.push(cline.points[cline.points.length - j - 1].translate(cline.dx, cline.dy));
        }
    }

    if (canvas.getContext) {
        var ctx = canvas.getContext('2d');
        // create an canvas equal the size of css
        canvas.width = 320;
        canvas.height = 378;
        var evenodd = 0;
        for (ux = -40; ux < 320; ux += 30) {
            evenodd++;
            for (uy = -40; uy < 378; uy += 30) {
                evenodd++;
                ctx.save();
                ctx.translate(ux, uy);
                ctx.scale(0.2, 0.2);
                var p = allpoints[0];
                ctx.beginPath();
                ctx.moveTo(p.x, p.y);
                for (i = 1; i < allpoints.length; i++) {
                    p = allpoints[i];
                    ctx.lineTo(p.x, p.y);
                };
                if ((evenodd % 2) == 0) {
                    ctx.fillStyle = "#2469A0";
                }
                else {
                    ctx.fillStyle = "#D14510";
                };
                ctx.fill();
                ctx.restore();
            };
        };
    };
};

TessellationFigure.prototype.drawIcon = function (canvas) {
    var i;
    if (canvas.getContext) {
        var ctx = canvas.getContext("2d");
        canvas.width = 40;
        canvas.height = 40;
        ctx.scale(0.16, 0.16);
        ctx.lineWidth = 16;
        ctx.strokeStyle = "black";
        for (i = 0; i < this.lines.length; i++) {
            ctx.save();
            cline = this.lines[i];
            cline.draw(ctx);
            ctx.translate(cline.dx, cline.dy);
            cline.draw(ctx);
            ctx.restore();
        };
    };
};

/* tessellation square object */

var TessellationSquare = function TessellationSquare() {
    this.lines = [new Line([new Point(20, 20), new Point(20, 170)], 150, 0),
		  new Line([new Point(20, 170), new Point(170, 170)], 0, -150)];
};

TessellationSquare.prototype = new TessellationFigure();

/* tessellation editor object */

var TessellationEditor = function TessellationEditor() {
    this.selectedpoint = null;
    this.selectedline = null;
    this.csel = false;
    this.figure = new TessellationSquare();
};

TessellationEditor.prototype.load = function (jsontext) {
    var i;
    var j;
    var lineobject;
    var pointobject;
    var o = JSON.parse(jsontext);

    this.figure = new TessellationFigure();
    this.figure.lines = jQuery.map(o.lines, function (lineobject, i) {
        return new Line(jQuery.map(lineobject.points, function (pointobject, j) {
            return new Point(pointobject.x, pointobject.y);
        }), lineobject.dx, lineobject.dy);
    });
    this.figure.description = o.description;
    this.selectedpoint = null;
    this.selectedline = null;
    this.csel = false;
};

TessellationEditor.prototype.onmousedown = function (x, y) {
    var i;
    var cline;
    var ccline;
    var xi;
    var yi;
    if (this.selectedline === null) {
        /* stupid place */
        this.sx = $("#tessellation").position().left;
        this.sy = $("#tessellation").position().top;

        for (i = 0; i < this.figure.lines.length; i++) {
            cline = this.figure.lines[i];
            this.selectedpoint = cline.hit(x - this.sx, y - this.sy);
            if (this.selectedpoint !== null) {
                this.selectedline = i;
                this.csel = false;
                this.draw();
                break;
            }
            // replace by inverse matrix
            xi = x - this.sx - cline.dx;
            yi = y - this.sy - cline.dy;
            this.selectedpoint = cline.hit(xi, yi);
            if (this.selectedpoint !== null) {
                this.selectedline = i;
                this.csel = true;
                this.draw();
                break;
            }
        }
    }
};

TessellationEditor.prototype.onmousemove = function (x, y) {
    if (this.selectedline !== null) {
        var cx;
        var cy;
        var cline = this.figure.lines[this.selectedline];
        if (this.csel) {
            // other translations here
            cx = cline.dx + this.sx;
            cy = cline.dy + this.sy;
        }
        else {
            cx = this.sx;
            cy = this.sy;
        };
        cline.points[this.selectedpoint] = new Point(x - cx, y - cy);
        this.draw();
    }
};

TessellationEditor.prototype.onmouseup = function (x, y) {
    this.selectedline = null;
    this.selectedpoint = null;
};

TessellationEditor.prototype.draw = function () {
    this.figure.draw();
};

TessellationEditor.prototype.drawt = function () {
    this.figure.drawt();
};

var TessellationView = function TessellationView(figure) {
    this.figure = figure;
};

TessellationView.prototype.draw = function () {
    this.figure.drawt();
};

function createList() {
    var i;
    var ret = '';
    var k;
    var ul = $("<ul>");
    if (supports_local_storage()) {
        for (i = 0; i < localStorage.length; i++) {
            k = localStorage.key(i);
            ul.append('<li><canvas width="40" height="40"></canvas><h3><a href="#editor">' + k + '</a></h3></li>');
        };
    }
    else {
	ul.append('<li><h3><a href="#">HTML5 Local Storage not supported</a></h3></li>');
    }
    $("#home ul").replaceWith(ul);
    // hack jquerymobile styling
    //$("#home ul").listview({"inset":true});
$("#home").page();
try {
	$("#home ul").listview("refresh");
} catch (err) {
    $("#home ul").listview();
}
    $("#home ul li").each(function () {
        k = $(this).find("a").text();
        var jsontext = localStorage.getItem(k);
        try {
            t1.load(jsontext);
        }
        catch (err) {
            localStorage.removeItem(k);
        };
        var iconcanvas = $(this).find("canvas").get(0);
        t1.figure.drawIcon(iconcanvas);

    });

    $("#home ul li a").bind('click tap', function () {
        k = $(this).text();
        if (supports_local_storage()) {
            var jsontext = localStorage.getItem(k)
            t1.load(jsontext);
        }
    });
 
};

var t1 = new TessellationEditor();
$(document).ready(function () {
    // first screen doesn't trigger pageshow
    createList();
    //t1.draw(); // needed for size?
    if ($.support.touch) {
        // use different events for touch enabled browser
        $("#tessellation").bind("touchstart", function (e) {
            e.preventDefault();
            e = e.originalEvent.targetTouches[0];
            t1.onmousedown(e.pageX, e.pageY);
        });

        $("#tessellation").bind('touchmove', function (e) {
            e.preventDefault();
            e = e.originalEvent.targetTouches[0];
            t1.onmousemove(e.pageX, e.pageY);
        });

        $("#tessellation").bind('touchend', function (e) {
            t1.onmouseup();
        });
    }
    else {
        $("#tessellation").bind("mousedown", function (e) {
            t1.onmousedown(e.pageX, e.pageY);
        });

        $("#tessellation").bind('mousemove', function (e) {
            t1.onmousemove(e.pageX, e.pageY);
        });

        $("#tessellation").bind('mouseup', function (e) {
            t1.onmouseup();
        });
    };

    /* editor */
    $("#editor").bind("pagehide", function () {
	// save item
        if (supports_local_storage()) {
            var jsontext = JSON.stringify(t1.figure);
            localStorage.setItem(t1.figure.description, jsontext);
        };
    });

    /* add form */
    
    $("#add form").submit(function () {
      $el = $('#add form');
      var description = $('#description',$el).val();
      if (description.length==0) {
	  alert('Description required');
	  $('#description',$el).focus();
	  return false;
      };
      if (localStorage.getItem(description)) {
	if (!confirm('Overwrite existing?')) {
	  $('#description',$el).focus();
	  return false;
	}
      }  
      t1.figure = new TessellationSquare();
      t1.figure.description = description;
      var jsontext = JSON.stringify(t1.figure);
      localStorage.setItem(description, jsontext);
      $.mobile.changePage("#home");
      return false;
    });

    /* list */

    		     
    $("#clearbutton").click(function () {
        if (supports_local_storage()) {
	  localStorage.clear();
	};
    });

    $("#add").bind("pageshow", function() {
       $("#add form").get(0).reset();
    });
    $("#home").bind("pageshow",function() {
	createList();
    });
    $("#editor").bind("pageshow", function() {
        t1.draw();
    });
    $("#viewer").bind("pageshow", function () {
        t1.drawt();
    });

});

