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

	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.addLine.apply(this, arguments);
		if (arguments.length == 3)
			return this.addLine.apply(this, arguments);
		throw "invalid arguments";
	}

	draw(area:Area, context:CanvasRenderingContext2D) {
		context.beginPath();
		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.fill) {
			context.fill();
			if (this.stroke)
				context.stroke();
		} else
			context.stroke();
	}
}