(function($) {
// use this
$.fn.signatureRender = function(points) {
  canvas_id = this[0].id;
  signature.setCanvas(canvas_id);
  signature.importData(points);
  signature.drawSign();
}

/* implementation */

Signature = function() {
  this.setCanvas = function(canvas_id) {
      if (canvas_id != this.canvas_id) {
          this.canvas_id = canvas_id;
          this.drawing = new RecordableDrawing(canvas_id);
      }
  }

  this.importData = function (input_data) {
    var data = this.transformData(input_data);
    this.drawing.removeAllRecordings();
    this.drawing.startRecording();
    var recording = this.drawing.currentRecording;
    var self = this;
    data.forEach(function(item, i, data) {
      points = self.convertPoints(item.points);
      if(i==0) {
        recording.currActionSet = new ActionsSet(item.t, points);
        recording.actionsSet = recording.currActionSet;
      }
      else {
        var tmpActionSet = recording.currActionSet;
        recording.currActionSet = new ActionsSet(item.t, points);
        tmpActionSet.next = recording.currActionSet;
      }
    });
  }

  this.convertPoints = function (points) {
    buffer = Array();
    points.forEach(function(item) {
      point = new Point(item.x, item.y, item.type);
      buffer.push(point);
    });
    return buffer;
  }

  this.transformData = function (data) {
    var transformed_data = Array();
		// detect data format
    if (data.hasOwnProperty('actions')) {
			while(data.next != null) {
				var points = Array();
				data.actions.forEach(function(item) {
					points.push({x: item.x, y: item.y, type: item.type});
				});
				transformed_data.push({points: points, t: data.interval});
				data = data.next;
			}
      return transformed_data.sort(function(a, b) {return a.t - b.t});
    }
		else if (data[0].hasOwnProperty['points']) {
			return data.sort(function(a, b) {return a.t - b.t});
		}
    else {
      data = data.sort(function(a, b) {return a.t - b.t});
      data.forEach(function(item, i) {
      	if (typeof(item.type) == 'undefined') {
	        type = (i==0? 0: 1);  // move first, then draw
      	} else {
      		type = item.type;
      	}
        transformed_data.push({
          points: [{x: item.x, y: item.y, type: type}],
          t: item.t
        });
      });
      return transformed_data;
    }
  }

  this.drawSign = function() {
    this.drawing.playRecording();
  }
}


RecordableDrawing = function (canvasId)
{
	var self = this;
	this.canvas = null;
	this.width = this.height = 0;
	this.actions = new Array();
	this.ctx = null;
	this.currentRecording = null; //instance of Recording
	this.recordings = new Array(); //array of Recording objects
	this.bgColor = "rgb(255,255,255)";
	var currentLineWidth = 5;
	var drawingColor = "rgb(0,0,0)";

	this.startRecording = function()
	{
		self.currentRecording = new Recording(this);
		self.recordings = new Array();
		self.recordings.push(self.currentRecording);
		self.currentRecording.start();
	}

	this.playRecording = function()
	{
		if (self.recordings.length == 0) {
			console.log("No recording loaded to play");
			return;
		}

		self.clearCanvas();

		for (var rec = 0; rec < self.recordings.length; rec++)
		{
			self.recordings[rec].playRecording(self.drawActions);
		}
	}

	this.clearCanvas = function()
	{
		self.ctx.fillStyle = self.bgColor;
		self.ctx.fillRect(0,0,self.canvas.width,self.canvas.height);
	}

	this.removeAllRecordings = function()
	{
        if (self.recordings) {
            self.recordings.forEach(function(item) {
                item.stopRecording();
            });
        }
		self.recordings = new Array()
		self.currentRecording = null;
	}

	this.drawAction = function (actionArg, addToArray)
	{
		var x = actionArg.x;
		var y = actionArg.y;

		switch (actionArg.type)
		{
		case 0: //moveto
			self.ctx.beginPath();
			self.ctx.moveTo(x, y);
			self.ctx.strokeStyle = self.drawingColor;
			self.ctx.lineWidth = self.currentLineWidth;
			break;
		case 1: //lineto
			self.ctx.lineTo(x,y);
			self.ctx.stroke();
			break;
		}
		if (addToArray)
			self.actions.push(actionArg);
	}

	__init = function()
	{
		self.canvas = $("#" + canvasId);
		if (self.canvas.length == 0)
		{
			return;
		}
		self.canvas = self.canvas.get(0);
		self.width = $(self.canvas).width();
		self.height = $(self.canvas).height();
		self.ctx = self.canvas.getContext("2d");

		self.clearCanvas();
	}

	__init();
}

Recording = function (drawingArg)
{
	var self = this;
	this.drawing = drawingArg;
	this.timeSlots = new Object(); //Map with key as time slot and value as array of Point objects

	this.buffer = new Array(); //array of Point objects
	this.timeInterval = 100; //10 miliseconds
	this.currTime = 0;
	this.started = false;
	this.intervalId = null;
	this.currTimeSlot = 0;
	this.actionsSet = null;
	this.currActionSet = null;
	this.recStartTime = null;
    this.scheduledDraws = Array();

	this.start = function()
	{
		self.currTime = 0;
		self.currTimeSlot = -1;
		self.actionsSet = null;

		self.recStartTime = (new Date()).getTime();
		self.started = true;
	}

	this.stop = function()
	{
		if (self.intervalId != null)
		{
			window.clearInterval(self.intervalId);
			self.intervalId = null;
		}
		self.started = false;
	}

	this.addAction = function(actionArg)
	{
		if (!self.started)
			return;
		self.buffer.push(actionArg);
	}

	this.playRecording = function(callbackFunctionArg)
	{
		if (self.actionsSet == null)
			return;

		self.scheduleDraw(self.actionsSet,self.actionsSet.interval,callbackFunctionArg, true);
	}

	this.scheduleDraw = function (actionSetArg, interval, callbackFunctionArg, isFirst)
	{
		scheduled_event = window.setTimeout(function(){
			var intervalDiff = -1;
			var isLast = true;
			if (actionSetArg.next != null)
			{
				isLast = false;
				intervalDiff = actionSetArg.next.interval - actionSetArg.interval;
			}
			if (intervalDiff >= 0)
				self.scheduleDraw(actionSetArg.next, intervalDiff, callbackFunctionArg, false);

			self.drawActions(actionSetArg.actions, isFirst, isLast);
		},interval);
        self.scheduledDraws.push(scheduled_event);
	}

    this.stopRecording = function () {
        this.scheduledDraws.forEach(function(item) {
            clearTimeout(item);
        });
        this.scheduledDraws.length = 0;
    }

	this.drawActions = function (actionArray, isFirst, isLast)
	{
		for (var i = 0; i < actionArray.length; i++)
			self.drawing.drawAction(actionArray[i],false);
	}
}

Action = function()
{
	var self = this;
	this.actionType; // 1 - Point, other action types could be added later
	this.x = 0;
	this.y = 0;
	this.isMovable = false;
	this.index = 0;

	if (arguments.length > 0)
	{
		self.actionType = arguments[0];
	}
	if (arguments.length > 2)
	{
		self.x = arguments[1];
		self.y = arguments[2];
	}
}

Point = function (argX,argY,typeArg)
{
	var self = this;
	this.type = typeArg; //0 - moveto, 1 - lineto

	Action.call(this,1,argX,argY);
}

Point.prototype = new Action();

ActionsSet = function (interalArg, actionsArrayArg)
{
	var self = this;

	this.actions = actionsArrayArg;
	this.interval = interalArg;
	this.next = null;
}

signature = new Signature();

    $.fn.signatureTooltip = function(points) {
      canvas_id = this[0].id;
      tooltip_id = "tooltip_" + canvas_id;
      var signatureTooltip = new SignatureTooltip();
      signatureTooltip.setCanvas(canvas_id);
      signatureTooltip.setTooltip(tooltip_id);
      signatureTooltip.setPoints(points);
      signatureTooltip.tooltip();
    }

    SignatureTooltip = function() {
        this.setCanvas = function(canvas_id) {
            if (canvas_id != this.canvas_id) {
                this.canvas_id = canvas_id;
            }
        }
        
        this.setTooltip = function(tooltip_id) {
            if (tooltip_id != this.tooltip_id) {
                this.tooltip_id = tooltip_id;
            }
        }
        
        this.setPoints = function(points) {
            if (points != this.points) {
                this.points = points;
            }
        }

        this.getNearPoint = function(x, y) {
            var d = 10;
            var firstPoint = false;
            var nearestPoint = {distance: Infinity, interval: -1};
            for(var key in this.points) {
                var point = this.points[key];
                if (!firstPoint) {
                    firstPoint = point;
                }
                if (point.x - d <= x && point.x + d >= x
                        && point.y - d <= y && point.y + d >= y) {
                    var distance =  Math.sqrt(Math.pow(point.x - x, 2) + Math.pow(point.y - y, 2));
                    if (nearestPoint.distance > distance) {
                         nearestPoint.distance = distance;
                         nearestPoint.interval = point.t - firstPoint.t;
                    }
                }
            }

            return nearestPoint;
        }
        this.tooltip = function() {
            var self = this;
            $("#" + self.canvas_id).mousemove(function(event) {
                var nearestPoint = self.getNearPoint(event.offsetX, event.offsetY);
                if (nearestPoint.interval >= 0) {
                    var strInterval = (nearestPoint.interval / 1000) + " сек.";
                    if ($("#" + self.tooltip_id).length == 0) {
                        $(document.body).append('<div id="' + self.tooltip_id + '" class="signature_tooltip">' + strInterval + '</div>');
                    }
                    $("#" + self.tooltip_id).html(strInterval);
                    $("#" + self.tooltip_id).css({top: event.pageY + 20, left: event.pageX + 20, position:'absolute'});
                    $("#" + self.tooltip_id).show();
                }
            });
            $("#" + self.canvas_id).mouseout(function() {
                $j("#" + self.tooltip_id).hide("slow");
            });
        }
    }

} (jQuery));