///<reference path="all.ts"/>
module jg {
	/**
	 * 二次ベジェ曲線の座標
	 */
	export interface QuadraticPoint extends CommonOffset {
		/** 制御点のX座標 */
		cp1x: number;
		/** 制御点のY座標 */
		cp1y: number;
	}
	/**
	 * 三次ベジェ曲線の座標
	 */
	export interface BezierPoint extends QuadraticPoint {
		/** 二つ目の制御点のX座標 */
		cp2x: number;
		/** 二つ目の制御点のY座標 */
		cp2y: number;
	}
	/**
	 * 円弧の座標
	 */
	export interface ArcPoint extends CommonOffset {
		/** 終点X座標 */
		x2: number;
		/** 終点Y座標 */
		y2: number;
		/** 半径 */
		radius: number;
	}

	/**
	 * 線を描画するクラス。
	 * 一般的な直線から、複雑な図形描画も出来る。
	 */
	export class Line extends E {
		/** 各種線 */
		p: CommonOffset[];
		/** 塗りつぶすかどうか */
		fill: boolean;
		/** 直線を描くかどうか */
		stroke: boolean;
		/** パスを閉じるかどうか */
		closePath: boolean;
		/** 描画をせず、クリッピングとして使うかどうか */
		clip: boolean;

		/**
		 * コンストラクタ
		 * @param pos 基準点
		 * @param line 直線を引く場合、最初の直線座標を入れるこ
		 * @param color 線の色
		 * @param width 線の幅
		 */
		constructor(pos:CommonOffset, line?:CommonOffset, color?:string, width?:number) {
			super();
			this.x = pos.x;
			this.y = pos.y;
			this.p = [];
			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);
		}

		/**
		 * クリッピングモードにする、または解除する
		 * クリッピングモードにした場合、このクラスは直接描画されず、このクラスの後に描画される結果に対してクリッピング処理を行う。
		 * @param value trueでクリッピングモード、falseで通常
		 */
		setClip(value:boolean) {
			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;
		}

		/**
		 * 線の色を指定する
		 * @param color 線の色。CSSカラー形式の文字列や、グラデーションやパターンなど
		 */
		setColor(color:any) {
			this.setDrawOption("strokeStyle", color);
			return this;
		}
		/**
		 * 線の色を取得する
		 */
		getColor() {
			return this.getDrawOption("strokeStyle");
		}

		/**
		 * 塗りつぶし色を指定する
		 */
		setFillColor(color:any) {
			this.setDrawOption("fillStyle", color);
			return this;
		}
		/**
		 * 塗りつぶし色を取得する
		 * @param color 線の色。CSSカラー形式の文字列や、グラデーションやパターンなど
		 */
		getFillColor() {
			return this.getDrawOption("fillStyle");
		}

		/**
		 * 線の幅を指定する
		 * @param width 線の幅
		 */
		setLineWidth(width:number) {
			this.setDrawOption("lineWidth", width);
			return this;
		}
		/**
		 * 線の幅を取得する
		 */
		getLineWidth() {
			return this.getDrawOption("lineWidth");
		}

		/**
		 * 線の末端のスタイルをbutt, round, squareいずれかの文字列で指定する
		 * @param lineCap 線の末端のスタイル
		 */
		setLineCap(lineCap:string) {
			this.setDrawOption("lineCap", lineCap);
			return this;
		}
		/**
		 * 線の末端のスタイルを取得する
		 */
		getLineCap() {
			return this.getDrawOption("lineCap");
		}

		//bevel, round, miter
		/**
		 * 線の結合方式をbevel, round, miterいずれかの文字列で指定する
		 * @param lineJoin 線の結合形式
		 */
		setLineJoin(lineJoin:string) {
			this.setDrawOption("lineJoin", lineJoin);
			return this;
		}
		/**
		 * 線の結合形式を取得する
		 */
		getLineJoin() {
			return this.getDrawOption("lineJoin");
		}

		/**
		 * マイター限界比率を指定する
		 * @param miterLimit マイター限界比率
		 */
		setMiterLimit(miterLimit:number) {
			this.setDrawOption("miterLimit", miterLimit);
			return this;
		}
		/**
		 * マイター限界比率を取得する
		 */
		getMiterLimit() {
			return this.getDrawOption("miterLimit");
		}

		/**
		 * 塗りつぶしに関して必要なパラメータを一括で指定する
		 * @param fill 塗りつぶしを行うかどうか
		 * @param color 塗りつぶし色
		 * @param closePath パスを閉じるかどうか。省略した場合変更しない
		 * @param stroke 線を描画するかどうか。fillとstrokeがいずれもtrueである場合、線描画と塗りつぶしの両方が行われる。省略した場合変更しない
		 */
		setFill(fill:boolean, color:any, closePath?:boolean, stroke?:boolean) {
			this.fill = fill;
			this.setFillColor(color);
			if (closePath !== undefined)
				this.closePath = closePath;
			if (stroke !== undefined)
				this.stroke = stroke;
			return this;
		}

		/**
		 * 線を追加する
		 * @param line 数値またはCommonOffsetで指定。数値の場合はX座標
		 * @param y 数値でY座標を指定。省略する場合、lineはCommonOffsetである必要がある
		 */
		addLine(line:any, y?:number) {
			if (arguments.length == 2)
				line = {x: line, y: y};

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

		/**
		 * 二次ベジェ曲線を追加
		 * @param cp CommonOffsetで制御点を指定
		 * @param p CommonOffsetで終点を指定
		 */
		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;
		}

		/**
		 * 三次ベジェ曲線を追加
		 * @param cp1 CommonOffsetで制御点1を指定
		 * @param cp2 CommonOffsetで制御点2を指定
		 * @param p CommonOffsetで終点を指定
		 */
		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;
		}

		/**
		 * 曲線を追加
		 * @param p CommonOffsetで地点1の座標を指定
		 * @param p2 CommonOffsetで地点2の座標を指定
		 * @param radius 半径
		 */
		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";
		}

		/**
		 * 描画
		 * @param context 対象の描画コンテキスト
		 */
		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();
		}
	}
}