/* 스트라이트 통합 이미지를 관리하는 클래스 */
$class({
	CSpriteBase : {
		SINGLETON : true,
		
		imgURL : 'http://c2down.cyworld.co.kr/download?fid=64221616d7c6410657fb16fbdf802ae3&name=sprite.gif',
		spriteSize:[
			[29,29],	// bomb
			[32,32]		// fire
		],
		spritePos : [
			[[0,0],[29,0],[58,0],[87,0],[0,29],[29,29]],		// bomb + explosion
			[[0,58],[32,58]]	// fire
		],
		
		/* create sprite */
		get : function(index,parent) {
			var ret=new CSprite(this.spriteSize[index][0],this.spriteSize[index][1],this.imgURL,this.spritePos[index],parent);
			return ret;
		}
	}
});

/* 분할된 스프라이트 이미지를 표시하는 div 래핑 클래스 */
$class({
	CSprite : {
		element : null,
		arrPos : null,
		curShape : null,
		parentPos:null,
		
		/* 통합 이미지에서 잘라내어 div로 표시하는 element를 생성한다 */
		init : function(width,height,url,arrPos,parent) {
			this.element=document.createElement('div');
			$style(this.element,'background','transparent url(http://c2down.cyworld.co.kr/download?fid=64221616d7c6410657fb16fbdf802ae3&name=sprite.gif) no-repeat 0px 0px');
			$style(this.element,'backgroundPosition','0px 0px');
			$style(this.element,'styleFloat','left');
			$style(this.element,'position','absolute');
			$style(this.element,'zIndex','1');
			$dom.hide(this.element);
			if (parent) this.parentPos=$dom.getPos(parent);
			parent = parent || document.body;
			parent.appendChild(this.element);
			$dom.setSize(this.element,width,height);
			this.arrPos=arrPos;
			this.shape(0);
		},
		/* 스프라이트의 모양을 바꾼다 */
		shape : function(index) {
			this.curShape=index;
			$style(this.element,'backgroundPositionX',-this.arrPos[index][0]+'px');
			$style(this.element,'backgroundPositionY',-this.arrPos[index][1]+'px');
		},
		/* 스프라이트를 보인다 */
		show : function() {
			$dom.show(this.element);
		},
		/* 스프라이트를 숨긴다 */
		hide : function() {
			$dom.hide(this.element);
		},
		/* 스프라이트 위치를 지정한다. parentElement를 지정하면, 이에 대한 상대좌표로 지정할 수 있다. */
		setPos : function(x,y,parentElement) {
			$dom.setPos(this.element,x,y,parentElement);
		},
		/* 스프라이트 위치를 얻는다. parentElement를 지정하면, 이에 대한 상대좌표를 반환한다. */
		getPos : function(parentElement) {
		   return $dom.getPos(this.element,parentElement);
		},
		/* 스프라이트를 표시하는 div를 반환 */
		getElement : function () {
			return this.element;
		},
		/* 스프라이트의 현재 모양을 반환 */
		getShape : function() {
			return this.curShape;
		}
	}
});

/* 개별 폭탄 클래스 */
$class({
	CBomb : {
		sprite : null,   // CSprite
		direction : 0,   // 0 : right, 1 : bottom, 2 : left, 3 : top
		fireLevel : 4,   // 폭발 단계
		/* 폭탄 위치 */
		x : 0,           
		y : 0,
		bombTable : null,
		dropY : 0,
		
		/* 이벤트 매핑 */
		EVENT_MAP : {
			click : 'onClick',  // 폭탄 클릭
			fire : 'onFire',    // 폭탄 폭발
			drop : 'onDrop'     // 폭탄 떨어짐
		},
		
		/* 폭탄 스프라이트를 생성*/
		init : function (bombTable,clickEnable) {
		    this.bombTable=bombTable;
			this.sprite = $factory.get(CSpriteBase).get(0,bombTable.element);
			this.direction = this.sprite.getShape();
			if (clickEnable==true) $event(this.sprite.getElement(),this);   // 맨 밑줄의 폭탄은 클릭할 수 없다.
			this.sprite.show();
		},
		/* 폭탄을 클릭 가능하게 함 */
		enable : function() {
		    $event(this.sprite.getElement(),this);
		},
		/* 폭탄의 위치 지정 */
		setPos : function (x,y) {
		    this.x=x;
		    this.y=y;
			this.sprite.setPos(x*29,y*29,this.bombTable.element);
		},
		/* 폭탄의 심지 방향 지정 */
		setDirection : function(dir) {
		    this.direction=dir;
		    this.sprite.shape(dir);
		},
		/* 현재 폭탄의 심지 방향 반환 */
		getDirection : function() {
		    return this.direction;
		},
		/* 폭탄의 폭발 애니메이션을 보여준다 */
		showExplosion : function () {
			if (this.fireLevel<6) {
				this.sprite.shape(this.fireLevel);
				this.fireLevel++;
				window.setTimeout(this.showExplosion.bind(this),100);
			} else {
				this.fireLevel=4;
				this.sprite.hide();
				$event.fire(this.bombTable,'explosion',this.x,this.y);
			}
		},
		/* 폭탄 클릭 이벤트 핸들러 - 폭탄 심지의 방향을 바꾼다 */
		onClick : function (evt,src,type) {
			this.direction++;
			if (this.direction>3) this.direction=0;
			this.sprite.shape(this.direction);
		},
		/* 인접 폭탄이 폭발했다 - 심지가 폭발 방향을 향하고 있으면, 자신도 폭발한다 */
		onFire : function(evt,src,type,x,y) {
		    if ((this.direction==0 && y==this.y && x==this.x+1)||
		        (this.direction==1 && y==this.y+1 && x==this.x)||
		        (this.direction==2 && y==this.y && x==this.x-1)||
		        (this.direction==3 && y==this.y-1 && x==this.x)) {
		            $event.fire(this.bombTable,'fireBomb',this.x,this.y);
		            this.doFire();
		        }
		},
		/* 인접 폭탄들에 폭발 메시지를 보낸다 */
		doFire : function() {
		    var arrSiblings=this.bombTable.getSiblingBomb(this.x,this.y);
		    for(var i=0;i<4;i++) {
		         if (arrSiblings[i] && this.direction!=i)
		             $event.post(arrSiblings[i],'fire',this.x,this.y);   // 비동기로 메시지를 전송한다.
		    }
		    this.showExplosion();
		},
		/* 폭탄이 아래로 떨어진다 */
		onDrop : function() {
		    this.dropY=0;
		    window.setTimeout(this.doDrop.bind(this),100);
		},
		/* 실제 폭탄이 떨어지는 애니메이션 구현 */
		doDrop : function() {
		    var finish=false;
		    this.dropY+=15;
		    if (this.dropY>29) {
		         this.dropY=29;
		         if (this.direction==1) this.setDirection(3);
		         else if (this.direction==3) this.setDirection(1);
		         finish=true;
		    }
		    this.sprite.setPos(this.x*29,this.y*29+this.dropY,this.bombTable.element);
		    if (finish) {
		         /* 폭탄이 모두 떨어지면 폭탄 테이블에 알린다 */
		         this.y++;
		         $event.fire(this.bombTable,'dropComplete',this,this.x,this.y-1);
		    } else {
		         window.setTimeout(this.doDrop.bind(this),100);
		    }
		}
	}
});

/* 불꽃 클래스 */
$class({
	CFire : {
		sprite : null,
		fireShape : 0,
		lane : null,
		bShow : true,
		
		/* 불꽃 스프라이트를 생성하고 애니메이션을 시작한다 */
		init : function (lane) {
		    this.lane=lane;
			this.sprite = $factory.get(CSpriteBase).get(1,lane.element);
			this.sprite.setPos(0,0,lane.element);
			this.fireShape = this.sprite.getShape();
			this.show();
			this.animate();
		},
		/* 불꽃을 보인다 */
		show : function() {
		    this.bShow=true;
			this.sprite.show();
		},
		/* 불꽃을 숨긴다 */
		hide : function() {
		    this.bShow=false;
			this.sprite.hide();
		},
		/* 불꽃 스프라이트 이미지를 주기적으로 바꾸어 애니메이션 구현 */
		animate : function() {
			this.fireShape++;
			if (this.fireShape>1) this.fireShape=0;
			this.sprite.shape(this.fireShape);
			if (this.bShow==true) window.setTimeout(this.animate.bind(this),400);
		},
		/* 불꽃을 아래로 한 칸 이동시킨다 */
		moveDown : function() {
		    var pos = this.sprite.getPos();
		    pos.y+=29;
		    this.sprite.setPos(pos.x,pos.y);
		}
	}
});

/* 폭탄들이 배열되는 폭탄 테이블 클래스 */
$class({
	CBombTable : {
		arrBombTable : null,
		size : {width : 0,height : 0},
		element : null,
		parentElement : null,
		
		/* 이벤트 맵 */
		EVENT_MAP : {
			explosion : 'onExplosion',  // 폭탄 폭발 완료
			dropComplete : 'onDropComplete', // 폭탄 떨어짐 완료
			fireBomb : 'onFireBomb' // 폭탄 폭발 시작
		},
        // 폭탄 테이블 div를 생성한다.
		init : function(width,height,parent) {
			this.size.width=width;
			this.size.height = height;
			this.arrBombTable=new Array(width*height);
			parent = parent || document.body;
			this.parentElement=parent;
			parent.innerHTML='<div id="_bomblink_bombtable" style="position:relative;float:left;border:1px solid #9090f0;width:'+width*29+'px;height:'+height*29+'px;">'+
			'<div style="position:relative;top:0px;left:0px;border-bottom:1px solid red;z-index:2;width:'+width*29+'px;height:29px;" ></div>'+
			'<div style="position:relative;top:'+(height-2)*29+'px;left:0px;border-top:1px solid blue;z-index:2;width:'+width*29+'px;height:29px;" ></div>'+
			'</div>';
			this.element=$('_bomblink_bombtable');
		},
		// 폭탄의 심지 방향을 랜덤으로 하여 현재 레벨에 맞게 폭탄을 생성한다.
		createLevel : function(level) {
			if (level<5) level=3;
			var height = Math.min(level,10);
			for(var y=this.size.height-(level+1);y<this.size.height;y++) {
				for(var x=0;x<this.size.width;x++) {
					var bomb=new CBomb(this,y<this.size.height-1);
					this.arrBombTable[x+y*this.size.width]=bomb;
					bomb.setPos(x,y);
					bomb.setDirection((Math.round(Math.random()*10))%4);
				}
			}
		},
		// 지정 위치의 폭탄을 반환
		getBomb : function(x,y) {
		    return this.arrBombTable[x+y*this.size.width];
		},
		// 지정 폭탄의 인접 폭탄을 배열로 반환
		getSiblingBomb : function(x,y) {
		    return [(x<this.size.width-1)?this.arrBombTable[x+1+y*this.size.width]:null,  // right
		            (y<this.size.height-2)?this.arrBombTable[x+(y+1)*this.size.width]:null,  // bottom
		            (x>0)?this.arrBombTable[x-1+y*this.size.width]:null,  // left
		            (y>0)?this.arrBombTable[x+(y-1)*this.size.width]:null]; // top
		},
		// 불꽃이 지나갈 때 마다 체크하여 해당 위치의 폭탄에 폭발 메시지 전달
		fire : function(y,x) {   // x : 0 - left, 1 - right
		    if (y==this.size.height-1) return;
		    if (x==0) {  // left
		         var bomb=this.getBomb(0,y);
		         if (bomb) $event.fire(bomb,'fire',-1,y);
		    } else { // right
		         var bomb=this.getBomb(this.size.width-1,y);
		         if (bomb) $event.fire(bomb,'fire',this.size.width,y);
		    }
		    bomb=null;
		},
		// 폭탄을 1줄 추가한다
		tableUp : function() {
		    for(var y=0;y<this.size.height-1;y++) {
		         for(var x=0;x<this.size.width;x++) {
		             var index=x+y*this.size.width;
		             var bomb = this.arrBombTable[index]=this.arrBombTable[index+this.size.width];
		             if (bomb) {
		                 bomb.setPos(x,y);
		                 if (y==this.size.height-2) bomb.enable();
		             }
		         }
		    }
		    for(var x=0;x<this.size.width;x++) {
				var bomb=new CBomb(this,false);
				var y=this.size.height-1;
				this.arrBombTable[x+y*this.size.width]=bomb;
				bomb.setPos(x,y);
				bomb.setDirection((Math.round(Math.random()*10))%4);
		    }
		    for(var x=0;x<this.size.width;x++) {
		         if (this.arrBombTable[x]) {
		             alert('Game Over');
		         }
		    }
		},
		// 폭탄 폭발 시작
		onFireBomb : function (evt,src,type,x,y) {
		     this.arrBombTable[x+y*this.size.width]=null;
		},
		// 폭탄 폭발 완료 - 사라진 폭탄의 위에 있는 폭탄에 떨어지는 메시지를 전달한다
		onExplosion : function(evt,src,type,x,y) {
		     var index=x+y*this.size.width;
	         
		     for(;index>0;index-=this.size.width) {
		         var bomb=this.arrBombTable[index];
		         if (bomb) {
			         this.arrBombTable[index]=null;
		             $event.fire(bomb,'drop');
		         }
		     }
		},
		// 폭탄 떨어짐 완료 - 만약 해당 폭탄의 아래가 비었으면 계속 떨어지게 한다
		onDropComplete : function(evt,src,type,bomb,x,y) {
		     var index=x+(y+1)*this.size.width;
		     this.arrBombTable[index]=bomb;
		     if (this.arrBombTable[index+this.size.width]==null) {
		         this.arrBombTable[index]=null;
	             $event.fire(bomb,'drop');
		     }
		     bomb=null;
		},
		// 현재 폭탄 테이블의 상태를 디버그 창에 표시
		dumpTable : function() {
			 $debug.outputLn('=============================================');
		     for(var y=0;y<this.size.height;y++) {
			     var msg='';
			     for(var x=0;x<this.size.width;x++) {
			         msg+=(this.arrBombTable[x+y*this.size.width])?'o':'x';
			     }
			     $debug.outputLn(msg);
			 }
		}
	}
});

/* 불꽃 레인 클래스 - 불꽃이 떨어지는 곳 */
$class({
   CLane : {
       element : null,
       game : null,
       bombTable : null,
       fireball : null,
       fireSpeed : 1,
       firePos : 0,
       lanePos : 0,
       
       // 좌, 우 설정에 따라 불꽃 레인 div를 생성하고 폭탄 테이블 옆에 위치시킨다
       init : function(game,bombTable,isLeft) {
           this.game = game;
           this.bombTable = bombTable;
           this.lanePos = (isLeft==true)?0 : 1;
           element = document.createElement('div');
           this.element=element;
           $style(element,'border','1px solid #e090e0');
           $style(element,'background','#e0e0e0');
           $style(element,'styleFloat','left');
           if (isLeft==true) {
               bombTable.parentElement.insertBefore(element,bombTable.element);
           } else {
               bombTable.parentElement.insertBefore(element);
           }
           $dom.setSize(element,32,bombTable.size.height*29);
       },
       // 불꽃을 생성하여 떨어뜨린다
       fire : function(speed) {
           this.fireSpeed=speed;
           this.fireball = new CFire(this);
           window.setTimeout(this.moveFire.bind(this),this.fireSpeed*100);
       },
       // 불꽃을 한칸씩 아래로 이동시킨다
       moveFire : function(speed) {
           this.fireball.moveDown();
           this.firePos++;
           if (this.firePos<this.bombTable.size.height) {
               this.bombTable.fire(this.firePos,this.lanePos); // 폭탄 테이블에 불꽃이 지나감을 알림
               window.setTimeout(this.moveFire.bind(this),this.fireSpeed*100);
           } else {
               // 불꽃이 모두 내려갔으면 불꽃을 지운다
               this.fireball.hide();
               this.fireball=null;
               this.firePos=0;
               $event.fire(this.game,'fireDrop');
           }
       }
   }
});

/* 게임 메인 클래스 */
$class({
    CBombLink : {
       bombTable : null,
       lane : [],
       level : 1,
       curFireNum : 0,
       
		EVENT_MAP : {
			fireDrop : 'onFireDrop' // 불꽃이 모두 내려갔을 때 수신하는 이벤트
		},
        // 폭탄 테이블, 불꽃 레인 등을 생성하고, 폭탄을 배열한다.
       init : function(width,height,element) {
           this.bombTable = new CBombTable(width,height,element);
           this.lane[0] = new CLane(this,this.bombTable,true);
           this.lane[1] = new CLane(this,this.bombTable,false);
           this.bombTable.createLevel(this.level);
           element = null;
           this.doStart();
       },
       // 게임을 시작한다. 좌, 우 레인 중 랜덤하게 불꽃을 생성한다.
       doStart : function() {
           //this.bombTable.dumpTable();
           this.lane[(Math.round(Math.random()*10))%2].fire(Math.min(3,(Math.round(Math.random()*10))%3+(10-this.level)));
           this.curFireNum++;
           if (this.curFireNum%(6-this.level)==0) this.bombTable.tableUp();   // 불꽃이 지정 개수만큼 떨어지면, 폭탄을 1줄 추가한다.
       },
       // 불꽃이 모두 내려가면, 1초 후 새로운 불꽃을 생성한다.
       onFireDrop : function() {
           window.setTimeout(this.doStart.bind(this),1000);
       }
    }
});
