module util.collision;
private import util.parts;
private import util.vector;
private import util.beam;
private import std.math;
private import br.ship;
public interface Collision{
	public bool add(Parts p, int group );
	public void collisionDetect(int kind);
	public void clear();
}
public class CollisionLaser:Collision{
	
	public:
	Beam laser;
	Parts[] group;
	int idx;
	const int maxParts;
	
	public this(){
		maxParts = 16;
		group.length = maxParts;
		idx = 0;
		
	}
	public this(int n){
		maxParts = n;
		group.length = maxParts;
		idx = 0;
		
	}
	public bool add(Parts p ,int g ){
		switch(g){
			case 1:
			laser = cast(Beam)p;
			
			assert(laser !is null);
			return true;
			//break;
			case 2:
			for(int i = 0;i < group.length;i ++){
			if(group[idx] is null || !group[idx].exists()){
				group[idx] = p;
				
				return true;
			}
				idx ++;
				if(group.length <= idx)idx = 0;
			}
			//break;
			return false;
			default:
			return false;
			//break;
		}
		return false;
	}
	public void collisionDetect(int kind){
		
		if(laser is null || !laser.exists || !laser.drawn){
			foreach(inout Parts p;group){
				if (p !is null && p.exists){
					p.laserState =p.NONE;
				}
			}
			return;
		}
		Vector l = new Vector(laser.rpos.x ,laser.rpos.y);
		Vector aim = new Vector(cos(laser.rlinkZ*PI/180) , sin(laser.rlinkZ*PI/180));
		Vector vaim = new Vector(-aim.y ,aim.x);
		Vector el = l + aim * laser.length;
		//Parts p;
		//if(p is null || p.exists || p.drawn)return;
		foreach(inout Parts p;group){
	    if (p !is null && p.exists){
				if(!p.inCollisionRange){
					p.laserState =p.NONE;
					continue;
				}
				Vector dpos = new Vector(p.rpos.x ,p.rpos.y) - l + aim * p.collisionRange;
//				Vector dposb = new Vector(p.rpos.x ,p.rpos.y) - l - aim * p.collisionRange;
				Vector dpos2 = new Vector(p.rpos.x ,p.rpos.y) - el - aim * p.collisionRange;
//				Vector dpos2b = new Vector(p.rpos.x ,p.rpos.y) - el - aim * p.collisionRange;
				if(vecDot(dpos ,aim) < 0 || vecDot(dpos2 ,aim * -1) < 0){
					p.laserState = p.NONE;
				}else{
					if(vecDot(dpos ,vaim) > 0){
						if(p.laserState == p.LEFT){
							laser.reportCollision(kind);
							p.reportCollision(kind);
						}
						p.laserState = p.RIGHT;
					}else{
						if(p.laserState == p.RIGHT){
							laser.reportCollision(kind);
							p.reportCollision(kind);
						}
						p.laserState = p.LEFT;
					}
				}
				if(laser is null)break;
				if(!laser.exists)break;
	    }
		}
	}
	public void clear(){
		laser = null;
		foreach(inout Parts p;group){
			p = null;
		}
		idx = 0;
	}
}
public class CollisionNormal:Collision{
		public:
	Parts[] group1;
	Parts[] group2;
	int idx1 ,idx2;
	const int maxParts;
	public this() {
		maxParts = 16;
		group1.length = maxParts;
		group2.length = maxParts;
		idx1 = 0;
		idx2 = 0;
	}
	public this(int n){
		maxParts = n;
		group1.length = maxParts;
		group2.length = maxParts;
		idx1 = 0;
		idx2 = 0;
	}
	public bool add(Parts p ,int group ){
		Parts[] g;
		int *idx;
		switch(group){
			case 1:g = group1;idx = &idx1;break;
			case 2:g = group2;idx = &idx2;break;
			default:return false;
		}
		for(int i = 0;i < g.length;i ++){
			if(g[*idx] is null || !g[*idx].exists()){
				g[*idx] = p;
				return true;
			}
			*idx = *idx + 1;
			if(g.length <= *idx)*idx = 0;
		}
		return false;
	}
	public void collisionDetect(int kind){
		Parts p1 ,p2;
		for (int i = 0;i < group1.length;i ++){
			p1 = group1[i];
    		if (p1 !is null && p1.exists && p1.drawn){
			  	if(!p1.inCollisionRange)continue;
				for (int j = 0;j < group2.length;j ++){
					p2 = group2[j];
				
					if (p2 !is null && p2.exists && p2.drawn){
						if(!p2.inCollisionRange)continue;
						if(p1.rpos.dist(p2.rpos) < p1.collisionRange + p2.collisionRange){
						
							p1.reportCollision(kind);
							p2.reportCollision(kind);
						}
					}
					if(p1 is null)break;
					if(!p1.exists)break;
				}
    		}
		}
	}
	public void clear() {
		foreach(inout Parts p;group1){
			p = null;
		}
		foreach(inout Parts p;group2){
			p = null;
		}
    idx1 = idx2 = 0;
  }

}

public class CollisionManager{
	public:
	enum kind { BULLET ,SHIP ,LASER ,SWORD ,BOMB};
	const int MAX;
	Collision[] collisions;
	protected:
  int partsIdx = 0;

	public this(int n) {
		MAX  = kind.max + 1;
		collisions.length = MAX;
		for(int i = 0;i < MAX;i ++){
			switch(i){
				case kind.LASER:
				case kind.SWORD:
				collisions[i] = new CollisionLaser(n);
				break;
				default:
				collisions[i] = new CollisionNormal(n);
				break;
			}
			
		}
	}


	public bool add(Parts p ,int kind ,int group){
		
		if(kind < 0 || MAX <= kind)return false;
		
			return collisions[kind].add(p ,group);
	}
	public void collisionDetect(){
		for(int i = 0;i < MAX; i++){
			collisions[i].collisionDetect(i);
		}
	}
	 public void clear() {
		foreach(Collision c;collisions){
			c.clear();
		}
  }
}
