// $Id: turtle.js 135 2009-05-20 17:22:39Z thomas.figg $

function Turtle (canvas, turtle) {
 
    if (canvas && canvas.getContext) {
        this.wait = 100;
    
        this.c = canvas.getContext('2d');
        this.canvas = canvas

        this._top = canvas.offsetTop;
        this._left = canvas.offsetLeft;
        

        this.max_x = canvas.width;
        this.max_y = canvas.height;

        this.c.lineCap = "round";
        this.turtle = turtle;
        this.sprite = document.getElementById('sprite').getSVGDocument();
        this.sprite = this.sprite.getElementById('sprite');
        this.visible = true;
        this.undobuffer = [];
        this.redobuffer = [];
        this.setup();
    }
}

Turtle.prototype.savestate = function() {
//    if (this.undobuffer.length > 23) {
//        this.undobuffer.shift();
//    }
//    this.undobuffer.push(this.getstate());
//
//    this.redobuffer = [];
}

Turtle.prototype.getstate = function() {
//    img = this.c.getImageData(0,0,this.max_x,this.max_y);
//    return {"x":this.x,"y":this.y,"angle":this.angle,"img":img};
}
Turtle.prototype.setstate = function(state) {
//    this.x = state.x;
//    this.y = state.y;
//    this.angle = state.angle;
//    this.clean_();
//    this.c.putImageData(state.img,0,0);
//    this.update();
//
}



Turtle.prototype.undo = function() {
    if (this.undobuffer.length > 0) {
        prev = this.undobuffer.pop();
        curr = this.getstate();
        this.setstate(prev);
        this.redobuffer.push(curr);
    } else {
        // Warning
    }
}   

Turtle.prototype.redo = function() {
    if (this.redobuffer.length > 0) {
        next = this.redobuffer.pop();
        curr = this.getstate();
        this.setstate(next);
        this.undobuffer.push(curr);
    } else {
        // Warning
    }
}   


Turtle.prototype.hideturtle = function() {
    this.visible = false;
    this.update();
}

Turtle.prototype.showturtle = function() {
    this.visible = true;
    this.update();
}


Turtle.prototype.update = function() {
    if (this.visible && this.x >=0 && this.y >= 0 && this.x <= this.max_x && this.y <= this.max_y) {
        this.turtle.style.left = parseInt(this._left + this.x -10) + "px";
        this.turtle.style.top = parseInt(this._top + this.y-20) + "px";
        this.sprite.setAttribute('transform','rotate('+(this.angle)+' 10 10)');
    } else {
        this.turtle.style.left = "-10px";
        this.turtle.style.top = "-10px";
    }   
}


Turtle.prototype.start = function(){};
Turtle.prototype.stop = function(){};
Turtle.prototype.finish = function(){};

Turtle.prototype.setxy = function(x,y) {
    this.savestate();
    this.x = x;
    this.y = y;
    this.update();
}

Turtle.prototype.setx = function(x) {
    this.savestate();
    this.x = x;
    this.update();
}

Turtle.prototype.sety = function(x,y) {
    this.savestate();
    this.x = x;
    this.update();
}

Turtle.prototype.setheading = function(h) {
    this.savestate();
    this.angle = (270+h) % 360;
    this.update();
}

Turtle.prototype.penwidth = function(w) {
    this.savestate();
    this.penwidth_(w);
}

Turtle.prototype.penwidth_ = function(w) {;
    this.c.lineWidth=w;
}

Turtle.prototype.color = function (args) {
    this.savestate();
    this.color_(args)
}
Turtle.prototype.color_ = function (args) {
    this.c.strokeStyle = "rgb("+parseInt(args[0])+","+parseInt(args[1])+","+parseInt(args[2])+")";
}

Turtle.prototype.arc = function (radius, angle) {
    this.savestate();
    if (this.pen) {
        this.c.beginPath();
        this.c.arc(this.x,this.y, radius, this.radians(), ((this.angle+angle)%360)/180*Math.PI,false);
        this.c.stroke();
       
  }
}
Turtle.prototype.arc_point = function (radius, angle) {
    if (this.pen) {
        this.c.beginPath();
        this.c.arc(this.x,this.y, radius, ((this.angle+angle)%360)/180*Math.PI, ((this.angle+angle+1)%360)/180*Math.PI,false);
        this.c.stroke();
       
  }
}



Turtle.prototype.circle = function (radius) {
    this.savestate();
    if (this.pen) {
        this.c.beginPath();
        this.c.arc(this.x,this.y, radius, 0, 2*Math.PI,false);
        this.c.stroke();
       
  }
}

Turtle.prototype.forward = function (d) {
    this.savestate();
    this.crawl(d);
}

Turtle.prototype.crawl = function (d) {
    this.c.beginPath();
    
    this.c.moveTo(this.x,this.y);
    
    var newx = this.x + d * Math.cos(this.radians());
    var newy = this.y + d * Math.sin(this.radians());
   
    if (this.pen) {
        this.c.lineTo(newx,newy);
    } else {
        this.c.moveTo(newx,newy);
    }
    this.c.stroke();
  
    // this only works in opera :( this.sleep();
    
    this.x = newx;
    this.y = newy;
    this.update();
}

Turtle.prototype.backward = function (d) {
    this.forward(-d);
}


Turtle.prototype.right = function(angle) {
    this.savestate();
    this.angle = (this.angle + angle) % 360;
    this.update();
}


Turtle.prototype.left = function(angle) {
    this.savestate();
    this.right(-angle);
}


Turtle.prototype.penup = function() {
    this.savestate();
    this.pen = false;
}


Turtle.prototype.pendown = function() {
    this.savestate();
    this.pen = true;
}


Turtle.prototype.radians = function() {
    return this.angle / 180 * Math.PI;
}
Turtle.prototype.clean = function() {
    this.savestate();
    this.clean_;
}
Turtle.prototype.clean_ = function() {
    old = this.c.fillStyle
    this.c.fillStyle = "rgb(255,255,255)";
    this.c.fillRect(0,0,this.max_x,this.max_y);
    this.c.fillStyle = old;
}

Turtle.prototype.clearscreen = function() {
    this.savestate();
    this.clean_();
    this.home_();
}


Turtle.prototype.reset = function() {
    this.clean_();
    this.setup();
    this.undobuffer = [];
    this.redobuffer = [];
}

Turtle.prototype.home = function () {
    this.savestate();
    this.home_();
}

Turtle.prototype.home_ = function() {
    this.x = this.max_x/2;
    this.y = this.max_y/2;
    this.angle = 270;
    this.update();
}

Turtle.prototype.setup = function() {
    this.home_();
    this.penwidth_(1);
    this.color_([0,0,0]);
    this.pen = true;
}

Turtle.prototype.paint = function () {}

function DelayCommand (that,fun,args) {
    this.that = that;
    this.fun = fun;
    this.args = args;
}

DelayCommand.prototype.call = function (that) {
    return this.fun.apply(this.that,this.args);
}


function DelayTurtle (canvas, sprite, speed, draw_bits) {
    this.turtle = new Turtle(canvas, sprite);
    this.pipeline = null;
    this.active = false;
    this.halt = false;
    this.speed = speed
    this.drawbits = (draw_bits == false) ? false : true;
}

DelayTurtle.prototype.start = function(){this.active = true; this.halt = false; this.pipeline = new Array(); this.paint();};
DelayTurtle.prototype.finish = function(){this.active = false;};
DelayTurtle.prototype.stop =  function(){this.halt = true;};

DelayTurtle.prototype.paint = function() {
    if (!this.halt) {
        var redraw = this.active;
        if (this.pipeline.length > 0) {
            var c = 0;
            do {
                var fun = this.pipeline.shift();
                fun.call()
                redraw = true;
                c++;
            } while (this.speed <= 1 && c< 10 && this.pipeline.length >0)
        } 
        if (redraw) {
            var that = this;
            
            setTimeout(function(){that.paint.call(that)},this.speed);
        }
    }
}

DelayTurtle.prototype.addCommand = function (fun, args) {
    this.pipeline.push(new DelayCommand(this.turtle, fun, args));
}

// There must be a nicer way to do this

DelayTurtle.prototype.forward = function(d) {
    if (this.drawbits) {
        var l = Math.abs(d);
        var s = l/d; 

        this.savestate();
        for (var c = 0; c < l; c++ ) {
            this.addCommand(this.turtle.crawl,[s])
        }
    } else {
        this.addCommand(this.turtle.forward,[d]);
    }
};
DelayTurtle.prototype.backward = function(d) { this.forward(-d)};

DelayTurtle.prototype.showturtle = function() { this.addCommand(this.turtle.showturtle,arguments)};
DelayTurtle.prototype.hideturtle = function() { this.addCommand(this.turtle.hideturtle,arguments)};
DelayTurtle.prototype.right = function() { this.addCommand(this.turtle.right,arguments)};
DelayTurtle.prototype.left = function() { this.addCommand(this.turtle.left,arguments)};
DelayTurtle.prototype.reset = function() { this.addCommand(this.turtle.reset,arguments)};
DelayTurtle.prototype.clean = function() { this.addCommand(this.turtle.clean,arguments)};
DelayTurtle.prototype.clearscreen = function() { this.addCommand(this.turtle.clearscreen,arguments)};
DelayTurtle.prototype.penup = function() { this.addCommand(this.turtle.penup,arguments)};
DelayTurtle.prototype.pendown = function() { this.addCommand(this.turtle.pendown,arguments)};
DelayTurtle.prototype.penwidth = function() { this.addCommand(this.turtle.penwidth,arguments)};
DelayTurtle.prototype.color = function() { this.addCommand(this.turtle.color,arguments)};
DelayTurtle.prototype.savestate = function() { this.addCommand(this.turtle.savestate,arguments)};
DelayTurtle.prototype.redo = function() { this.addCommand(this.turtle.redo,arguments)};
DelayTurtle.prototype.undo = function() { this.addCommand(this.turtle.undo,arguments)};

DelayTurtle.prototype.arc = function(radius, angle) { 
    if (this.drawbits) {
        var end = (360+angle) % 360 ; 
        this.savestate();
        for (var c = 0; c <= end; c++ ) {
            this.addCommand(this.turtle.arc_point,[radius,c])
        }
    } else {
        this.addCommand(this.turtle.arc,[radius,angle])
    }
};
DelayTurtle.prototype.circle = function(radius) {
    if (this.drawbits) {
        this.savestate();
        for (var c = 0; c < 360; c++ ) {
            this.addCommand(this.turtle.arc_point,[radius,c])
        }
    } else {
        this.addCommand(this.turtle.circle,[radius])
    }
};

DelayTurtle.prototype.setxy = function() { this.addCommand(this.turtle.setxy,arguments)};
DelayTurtle.prototype.setx = function() { this.addCommand(this.turtle.setx,arguments)};
DelayTurtle.prototype.sety = function() { this.addCommand(this.turtle.sety,arguments)};

DelayTurtle.prototype.setheading = function() { this.addCommand(this.turtle.setheading,arguments)};

DelayTurtle.prototype.home = function() { this.addCommand(this.turtle.home,arguments)};
