module jg {
	export interface QuadraticPoint extends CommonOffset {
		cp1x:number;
		cp1y:number;
	}
	export interface BezierPoint extends QuadraticPoint {
		cp2x:number;
		cp2y:number;
	}
	export interface ArcPoint extends CommonOffset {
		x2:number;
		y2:number;
		radius:number;
	}
	//arcToもサポート対象と言いたいが、arcToはOperaのサポートが怪しいそうなので無用な混乱を避けるため見送る
	export class Line extends E {
		p:CommonOffset[];
		fill:bool;
		stroke:bool;
		closePath:bool;
		clip:bool;

		constructor(pos:CommonOffset, line?:CommonOffset, color?:string, width?:number) {
			super();
			this.x = pos.x;
			this.y = pos.y;
			this.p = new CommonOffset[];
			this.p.push({x:pos.x, y:pos.y});
			if (line) {
				this.p.push({x:line.x, y:line.y});
				this.updateSize();
			}
			if (color)
				this.setColor(color);
			if (width)
				this.setLineWidth(width);
		}

		setClip(value:bool) {
			this.clip = value;
			if (this.clip)
				this.disableTransform = true;
			else
				delete this.disableTransform;
		}

		updateSize() {
			var min = {x: this.p[0].x, y: this.p[0].y};
			var max = {x: this.p[0].x, y: this.p[0].y};
			for (var i=1; i<this.p.length; i++) {
				var x = this.p[0].x + this.p[i].x;
				var y = this.p[0].y + this.p[i].y;
				if (min.x > x)
					min.x = x;
				else if (max.x < x)
					max.x = x;
				if (min.y > y)
					min.y = y;
				else if (max.y < y)
					max.y = y;
			}
			this.width = max.x - min.x;
			this.height = max.y - min.y;
		}

		setColor(color:any) {
			this.setDrawOption("strokeStyle", color);
			return this;
		}
		getColor() {
			return this.getDrawOption("strokeStyle");
		}

		setFillColor(color:any) {
			this.setDrawOption("fillStyle", color);
			return this;
		}
		getFillColor() {
			return this.getDrawOption("fillStyle");
		}

		setLineWidth(width:number) {
			this.setDrawOption("lineWidth", width);
			return this;
		}
		getLineWidth() {
			return this.getDrawOption("lineWidth");
		}

		//butt, round, square
		setLineCap(lineCap:string) {
			this.setDrawOption("lineCap", lineCap);
			return this;
		}
		getLineCap() {
			return this.getDrawOption("lineCap");
		}

		//bevel, round, miter
		setLineJoin(lineJoin:string) {
			this.setDrawOption("lineJoin", lineJoin);
			return this;
		}
		getLineJoin() {
			return this.getDrawOption("lineJoin");
		}

		setMiterLimit(miterLimit:number) {
			this.setDrawOption("miterLimit", miterLimit);
			return this;
		}
		getMiterLimit() {
			return this.getDrawOption("miterLimit");
		}

		setFill(fill:bool, color:any, closePath?:bool, stroke?:bool) {
			this.fill = fill;
			this.setFillColor(color);
			if (closePath !== undefined)
				this.closePath = closePath;
			if (stroke !== undefined)
				this.stroke = stroke;
			return this;
		}


		addLine(line:any, y?:number) {
			if (arguments.length == 2)
				line = {x: line, y: y};

			this.p.push(line);
			this.updateSize();
			return this;
		}

		addQuadraticLine(cp:any, p?:any) {
			var qp:QuadraticPoint; 
			if (arguments.length == 4) {
				qp = {
					cp1x: arguments[0],
					cp1y: arguments[1],
					x: arguments[2],
					y: arguments[3]
				}
			} else if (arguments.length == 2) {
				qp = {
					cp1x: cp.x,
					cp1y: cp.y,
					x: p.x,
					y: p.y
				}
			} else
				qp = cp;

			this.p.push(qp);
			this.updateSize();

			return this;
		}

		addBezierLine(cp1:any, cp2?:any, p?:any) {
			var bp:BezierPoint; 
			if (arguments.length == 6) {
				bp = {
					cp1x: arguments[0],
					cp1y: arguments[1],
					cp2x: arguments[2],
					cp2y: arguments[3],
					x: arguments[4],
					y: arguments[5]
				}
			} else if (arguments.length == 3) {
				bp = {
					cp1x: cp1.x,
					cp1y: cp1.y,
					cp2x: cp2.x,
					cp2y: cp2.y,
					x: p.x,
					y: p.y
				}
			} else
				bp = cp1;

			this.p.push(bp);
			this.updateSize();

			return this;
		}

		addArc(p:any, p2:any, radius:any) {
			var ap:ArcPoint;
			if (arguments.length == 5) {
				ap = {
					x: arguments[0],
					y: arguments[1],
					x2: arguments[2],
					y2: arguments[3],
					radius: arguments[4]
				}
			} else if (arguments.length == 3) {
				ap = {
					x: p.x,
					y: p.y,
					x2: p2.x,
					y2: p2.y,
					radius: radius
				}
			} else
				ap = p;

			this.p.push(ap);
			this.updateSize();
		}

		add() {
			if (arguments.length == 1)
				return this.addLine.apply(this, arguments);
			if (arguments.length == 2)
				return this.addQuadraticLine.apply(this, arguments);
			if (arguments.length == 3)
				return this.addBezierLine.apply(this, arguments);
			throw "invalid arguments";
		}

		draw(context:CanvasRenderingContext2D) {
			context.beginPath();
			if (this.clip) {
				//bad code. 回転などをサポートするための苦肉の処置
				context.save();
				context.translate(this.x, this.y);
				if (this.options)
					this.scene.game.renderer.useDrawOption(this, context);
			}
			context.moveTo(0, 0);
			for (var i=1; i<this.p.length; i++) {
				var p = <BezierPoint>this.p[i];	//bad know how
				if (p.cp2x !== undefined) {
					context.bezierCurveTo(p.cp1x, p.cp1y, p.cp2x, p.cp2y, p.x, p.y);
				} else if (p.cp1x !== undefined) {
					context.quadraticCurveTo(p.cp1x, p.cp1y, p.x, p.y);
				} else{
					if ((<ArcPoint>this.p[i]).radius !== undefined) {
						var ap = <ArcPoint>this.p[i];
						context.arcTo(ap.x, ap.y, ap.x2, ap.y2, ap.radius);
					} else
						context.lineTo(p.x, p.y);	//本当はこれを一番速く処理したいが。
				}
			}
			if (this.closePath)
				context.closePath();

			if (this.clip) {
				context.restore();
				context.clip();
			} else if (this.fill) {
				context.fill();
				if (this.stroke)
					context.stroke();
			} else
				context.stroke();
		}
	}
}