/**
 * @author Sergey Chikuyonok (gonarch@design.ru)
 * @copyright Art.Lebedev Studio (http://www.artlebedev.ru)
 */

if(!Function.prototype.inheritFrom){
	Function.prototype.inheritFrom = function(BaseClass) { // inheritance's method

		var Inheritance = function() {};

		Inheritance.prototype = BaseClass.prototype;

		this.prototype = new Inheritance();
		this.prototype.constructor = this;
		this.baseConstructor = BaseClass;
		this.superClass = BaseClass.prototype;

	}
}

if(!Object.extend){
	Object.extend = function(destination, source) {
		for(var property in source){
			destination[property] = source[property];
		}
		return destination;
	}
}

if(!Object.copy){
	Object.copy=function(obj){
		var new_obj={};
		for(var a in obj){
			new_obj[a]=obj[a];
		}

		return new_obj;
	}
}

/**
 * Simple line drawing API
 * @constructor
 * @param {String, Element} [context] Point where attach drawing canvas
 */
function SimpleCanvas(context){
	if(context){
		if(typeof(context) == 'string')
			context=document.getElementById(context);
	}
	else{
		context=document.body;
	}

	this.setContainer(context);

	var renderes=[SimpleCanvas.Renderer.Canvas, SimpleCanvas.Renderer.VML];
	for(var i=0; i<renderes.length; i++){
		if(renderes[i].isSupported()){
			this.setRenderer(new renderes[i](context));
			break;
		}
	}

	this._history={};
}

SimpleCanvas.prototype.setRenderer=function(obj){
	this._renderer=obj;
}

SimpleCanvas.prototype.getRenderer=function(){
	return this._renderer;
}

SimpleCanvas.prototype.setContainer=function(obj){
	this._container=obj;
}

SimpleCanvas.prototype.getContainer=function(){
	return this._container;
}

SimpleCanvas.prototype.drawLine=function(from, to, properties, dont_add){
	var renderer=this.getRenderer();
	if(renderer){
		renderer.drawLine(from, to, properties);
		if(!dont_add){
			this._addToHistory('line', from, to, properties);
		}
	}

}

SimpleCanvas.prototype.clear=function(){
	this._clearHistory();
	this._emptyCanvas();
}

SimpleCanvas.prototype.repaint=function(){
	var renderer=this.getRenderer();
	if(renderer)
		renderer.reflow();
	this._redrawFromHistory();
}

SimpleCanvas.prototype._emptyCanvas=function(){
	var renderer=this.getRenderer();
	if(renderer)
		renderer.clear();
}

/**
 * Add object to history for redrawing on reflow
 * @param {String} item_type History item type (only 'line' supported)
 * @param {Object} ... Object parameters needed to redraw
 */
SimpleCanvas.prototype._addToHistory=function(item_type){
	if(!this._history[item_type]){
		this._history[item_type]=[];
	}
	var params=[].slice.call(arguments, 1);

	this._history[item_type].push(params);
	//console.log(this._history);
}

SimpleCanvas.prototype._clearHistory=function(){
	this._history=[];
}

/**
 * Returns history items of specified type
 * @param {String} item_type History item type (only 'line' supported)
 * @return {Array}
 */
SimpleCanvas.prototype._getHistoryByType=function(item_type){
	if(this._history && this._history[item_type] && this._history[item_type].length)
		return this._history[item_type];
	else
		return null;
}

SimpleCanvas.prototype._redrawFromHistory=function(){
	this._emptyCanvas();
	var lines=this._getHistoryByType('line');
	if(lines){
		var line;
		for(var i=0, il=lines.length; i<il; i++){
			line=lines[0];
			this.drawLine(line[0], line[1], line[2], true);
		}
	}
}

SimpleCanvas.Renderer=function(context){
	this.setContext(context);
	this.setDefaultStrokeStyle({
		color: '#000000',
		width: 1
	});
};

SimpleCanvas.Renderer.prototype={

	/**
	 * Set context element where to draw
	 * @param {Element} context
	 */
	setContext: function(context){
		this._context=context;
	},

	/**
	 * Get context element
	 * @return {Element}
	 */
	getContext: function(){
		return this._context;
	},

	setDefaultStrokeStyle: function(style){
		this._strokeStyle=style;
	},

	getDefaultStrokeStyle: function(){
		return this._strokeStyle;
	},


	reflow: function(){
		return;
	}
}

/**
 * Draw using <canvas>
 * @extends {SimpleCanvas.Renderer}
 * @param {Element} context
 */
SimpleCanvas.Renderer.Canvas=function(context){
	SimpleCanvas.Renderer.Canvas.baseConstructor.call(this, context);
	var canvas=document.createElement('canvas');
	canvas.style.width='100%';
	canvas.style.height='100%';
	context.appendChild(canvas);
	canvas.setAttribute('width', canvas.offsetWidth);
	canvas.setAttribute('height', canvas.offsetHeight);
	this._setCanvas(canvas);
}

SimpleCanvas.Renderer.Canvas.inheritFrom(SimpleCanvas.Renderer);

SimpleCanvas.Renderer.Canvas.isSupported=function(){
	var elem=document.createElement('canvas');
	return (elem.getContext);
}

/**
 * Set canvas element where to draw
 * @param {Object} context
 */
SimpleCanvas.Renderer.Canvas.prototype._setCanvas=function(canvas){
	this._canvas=canvas;
}

/**
 * Get canvas
 * @return {Object}
 */
SimpleCanvas.Renderer.Canvas.prototype._getCanvas=function(){
	return this._canvas.getContext('2d');
}

/**
 * Draw line
 * @param {Object} from Line start point
 * @param {Object} to Line end point
 * @param {Object} [properties] Line properties
 */
SimpleCanvas.Renderer.Canvas.prototype.drawLine=function(from, to, properties){
	var ctx=this.getContext();
	var ctx_width=ctx.offsetWidth;
	var ctx_height=ctx.offsetHeight;

	var c=this._getCanvas();
	this._setStrokeStyle(properties);
	c.beginPath();
	c.moveTo(this._makeValue(from.x, ctx_width), this._makeValue(from.y, ctx_height));
	c.lineTo(this._makeValue(to.x, ctx_width), this._makeValue(to.y, ctx_height));
	c.closePath();
	c.stroke();
}

SimpleCanvas.Renderer.Canvas.prototype._makeValue=function(val, prc100){
	if(typeof(val) == 'number'){
		return val;
	}
	else if(typeof(val) == 'string' && val.charAt(val.length - 1) == '%'){
		return Math.round(parseFloat(val)/100*prc100);
	}
}

SimpleCanvas.Renderer.Canvas.prototype.reflow=function(){
	var ctx=this.getContext();
	var ctx_width=ctx.offsetWidth;
	var ctx_height=ctx.offsetHeight;

	this._canvas.setAttribute('width', ctx_width);
	this._canvas.setAttribute('height', ctx_height);
}


SimpleCanvas.Renderer.Canvas.prototype._setStrokeStyle=function(properties){
	var style=Object.copy(this.getDefaultStrokeStyle()), c=this._getCanvas();
	if(properties)
		Object.extend(style, properties);
	c.strokeStyle=style.color;
	c.lineWidth=style.width;
}

SimpleCanvas.Renderer.Canvas.prototype.clear=function(){
	var cv=this._getCanvas();
	var ctx=this.getContext();
	cv.clearRect(0,0,ctx.offsetWidth, ctx.offsetHeight);
}

SimpleCanvas.Renderer.Canvas.isSupported=function(){
	var elem=document.createElement('canvas');
	return (elem.getContext);
}

/**
 * Draw using VML (supported by IE)
 * @extends {SimpleCanvas.Renderer}
 * @constructor
 * @param {Element} context
 */
SimpleCanvas.Renderer.VML=function(context){
	SimpleCanvas.Renderer.VML.baseConstructor.call(this, context);
	if (!document.namespaces["v"]) {
		document.namespaces.add("v", "urn:schemas-microsoft-com:vml");
      // setup default css
      var ss = document.createStyleSheet();
      ss.cssText = "v\\:* {behavior:url(#default#VML);}";
	}
}

SimpleCanvas.Renderer.VML.inheritFrom(SimpleCanvas.Renderer);

SimpleCanvas.Renderer.VML.isSupported=function(){
	return (document.body.runtimeStyle);
}

SimpleCanvas.Renderer.VML.prototype.drawLine=function(from, to, properties){
	var style=Object.copy(this.getDefaultStrokeStyle());
	if(properties)
		Object.extend(style, properties);

	var ctx=this.getContext();
	var ctx_width=ctx.offsetWidth;
	var ctx_height=ctx.offsetHeight;

	var line=document.createElement('v:line');

	line.setAttribute('from', this._makeValue(from.x, ctx_width)+', '+this._makeValue(from.y, ctx_height));
	line.setAttribute('to', this._makeValue(to.x, ctx_width)+', '+this._makeValue(to.y, ctx_height));
	line.setAttribute('strokecolor', style.color);
	line.setAttribute('strokeweight', style.width);

	ctx.appendChild(line);
}

SimpleCanvas.Renderer.VML.prototype._makeValue=function(val, prc100){
	if(typeof(val) == 'number'){
		return val+'px';
	}
	else if(typeof(val) == 'string' && val.charAt(val.length - 1) == '%'){
		return Math.round(parseFloat(val)/100*prc100)+'px';
	}
}

SimpleCanvas.Renderer.VML.prototype.clear=function(){
	var c=this.getContext();
	var ch=c.childNodes;
	for(var i=ch.length-1; i>=0; i--){
		ch[i].parentNode.removeChild(ch[i]);
	}
}