module jg {
	export interface CharacterMoveInfo {
		x:number;
		y:number;
		dx:number;
		dy:number;
		f:number;
		t:number;
	}

	export interface CharacterMovedEventArgs {
		nextMove?: string;
	}

	export class Character extends FrameSprite {
		moving:bool;
		moveInfo:CharacterMoveInfo;
		nextMove:string;
		beginMove:Trigger;
		moved:Trigger;
		charaSeq: number;
		charaCol: number;
		animeCnt: number;
		movePixel: number;
		moveTime: number;
		angleSeq: any;//{[key:Angle]: number; };
		currentAngle:Angle;

		constructor(image:any, width:number, height:number, wait?:number) {
			super(image, width, height, wait);

			this.moving = false;
			if (! wait)
				wait = 200;

			this.animeCnt = 2;
			this.charaSeq = 0;
			this.charaCol = 1;
			this.movePixel = 64;
			this.moveTime = 300;

			this.startTimer();
		}

		moveLeft(stackNext?:bool) {
			if (this.move(-this.movePixel, 0, this.moveTime)) {
				this.angle(Angle.Left);
				return true;
			}
			if (stackNext && this.moveInfo.t*2 >= this.moveInfo.f)
				this.nextMove = "Left";
			return false;
		}

		moveRight(stackNext?:bool) {
			if (this.move(this.movePixel, 0, this.moveTime)) {
				this.angle(Angle.Right);
				return true;
			}
			if (stackNext && this.moveInfo.t*2 >= this.moveInfo.f)
				this.nextMove = "Right";
			return false;
		}

		moveUp(stackNext?:bool) {
			if (this.move(0, -this.movePixel, this.moveTime)) {
				this.angle(Angle.Up);
				return true;
			}
			if (stackNext && this.moveInfo.t*2 >= this.moveInfo.f)
				this.nextMove = "Up";
			return false;
		}

		moveDown(stackNext?:bool) {
			if (this.move(0, this.movePixel, this.moveTime)) {
				this.angle(Angle.Down);
				return true;
			}
			if (stackNext && this.moveInfo.t*2 >= this.moveInfo.f)
				this.nextMove = "Down";
			return false;
		}

		move(x:number, y:number, f:number) {
			if (this.moving)
				return false;

			this.moving = true;
			this.moveInfo = {
				x: this.x,
				y: this.y,
				dx: this.x + x,
				dy: this.y + y,
				f: f,
				t: 0
			}
			if (this.beginMove)
				this.beginMove.fire(this.moveInfo);

			if (this.moving)
				this.start();

			return true;
		}

		update(t:number) {
			if (this.moving) {
				this.moveInfo.t += t;
				if (this.moveInfo.t < this.moveInfo.f) {
					this.moveTo(
						this.moveInfo.x + Math.round((this.moveInfo.dx - this.moveInfo.x) / this.moveInfo.f * this.moveInfo.t),
						this.moveInfo.y + Math.round((this.moveInfo.dy - this.moveInfo.y) / this.moveInfo.f * this.moveInfo.t)
					);
				} else {
					this.moveTo(this.moveInfo.dx, this.moveInfo.dy);
					this.endMove();
				}
			}
		}

		endMove() {
			this.moving = false;
			this.stop();
			var e:CharacterMovedEventArgs = {}
			if (this.nextMove) {
				e.nextMove = this.nextMove;
				delete this.nextMove;
			}
			if (this.moved)
				this.moved.fire(e);
			if (e.nextMove)
				this["move"+e.nextMove]();
		}

		angle(angle:Angle) {
			this.currentAngle = angle;
			var rowP = Math.floor(this.charaSeq / this.charaCol) * 4;

			switch (angle) {
			case Angle.Up:
				rowP += (this.angleSeq ? this.angleSeq[Angle.Up] : 3);
			break;
			case Angle.Down:
				rowP += (this.angleSeq ? this.angleSeq[Angle.Down] : 0);
			break;
			case Angle.Left:
				rowP += (this.angleSeq ? this.angleSeq[Angle.Left] : 1);
			break;
			case Angle.Right:
				rowP += (this.angleSeq ? this.angleSeq[Angle.Right] : 2);
			break;
			}

			var f = this.animeCnt * (this.charaSeq % this.charaCol) + this.charaCol * this.animeCnt * rowP;
			this.frame = [];
			if (this.animeCnt % 2 == 1) {
				for (var i=0; i<this.animeCnt; i++)
					this.frame.push(i+f);
				for (var i=this.animeCnt-2; i>0; i--)
					this.frame.push(i+f);
			} else {
				for (var i=0; i<this.animeCnt; i++)
					this.frame.push(i+f);
			}
			this.changeFrame();
		}
	}
}
