package star;

import java.util.ArrayList;

public class Ship implements Cloneable {
	
	/*--------------------------------------------------------------*/
	/*----------------        Initialization        ----------------*/
	/*--------------------------------------------------------------*/
	
	public Ship(String s){
		this(s.split("[;,]"));//should allow semi or comma delimiters
	}
	
	public Ship(String[] args) {
		for(int i=0; i<args.length; i++){
			String arg=args[i];
			String[] split=arg.split("=");
			String a=split[0].toLowerCase();
			String b=split.length>1 ? split[1] : null;
			if(b!=null && b.equalsIgnoreCase("null")){b=null;}

			if(a.equals("side")){
				side=Character.toUpperCase(b.charAt(0));
				assert(side=='A' || side=='B');
			}else if(a.equals("class") || a.equals("type")){
				shipClass=b.replace('_', ' ');
			}else if(a.equals("name")){
				shipName=b.replace('_', ' ');
			}else if(a.equals("hull")){
				maxHull=Integer.parseInt(b);
			}else if(a.equals("armor")){
				maxArmor=Integer.parseInt(b);
			}else if(a.equals("flux")){
				maxFlux=Integer.parseInt(b);
			}else if(a.equals("downflux")){
				downFlux=Integer.parseInt(b);
			}else if(a.equals("upflux")){
				upFlux=Integer.parseInt(b);
			}else if(a.equals("dissipation")){
				dissipation=Integer.parseInt(b);
			}else if(a.equals("shield") || a.equals("hasshield")){
				hasShield=parseBoolean(b);
			}else if(a.equals("vent") || a.equals("canvent")){
				canVent=parseBoolean(b);
			}else if(a.equals("fluxperdamage") || a.equals("efficiency")){
				fluxPerDamage=Float.parseFloat(b);
			}else if(a.equals("minarmormodifier")){
				minArmorModifier=Float.parseFloat(b);
			}else if(a.equals("strippedarmorfraction")){
				strippedArmorFraction=Float.parseFloat(b);
			}else if(a.equals("minarmor") || a.equals("bonusarmor")){
				bonusArmor=Float.parseFloat(b);
			}else if(a.equals("shieldflux") || a.equals("upkeep")){
				shieldFlux=Float.parseFloat(b);
			}else if(addWeapon(arg)){
				//do nothing
			}else{
				assert(false) : "Unknown parameter "+args[i];
			}
		}
		reset();

		if(upFlux<0){upFlux=(int)(maxFlux*0.5);}
		if(downFlux<0){downFlux=(int)(maxFlux*0.9);}
		
		dissipationPerCenti=dissipation*0.01f;
		ventingDissipationPerCenti=dissipationPerCenti*2;
		fluxPerCenti=shieldFlux*0.01f;
	}
	
	public void reset(){
		
		hull=maxHull;
		armor=maxArmor;
		hardFlux=0;
		softFlux=0;
		empDamageTaken=0;
		
		shieldsUp=hasShield;
		venting=false;
		for(Weapon w : weapons){w.reset();}
	}
	
	public Ship copy(){
		try {
			Ship s=(Ship)this.clone();
			s.weapons=new ArrayList<Weapon>();
			for(Weapon w : weapons){s.weapons.add(w.copy());}
			s.reset();
			return s;
		} catch (CloneNotSupportedException e) {
			throw new RuntimeException(e);
		}
	}
	
	private boolean addWeapon(String s){
		try {
			int mult=1;
			if(Character.isDigit(s.charAt(0))){
				int x=s.indexOf('x');
				if(x<0){x=s.indexOf('X');}
				if(x>0){
					mult=Integer.parseInt(s.substring(0, x));
					s=s.substring(x+1);
				}
			}
			Weapon w=Weapon.makeWeapon(s);
			for(int i=0; i<mult; i++){
				weapons.add(w.copy());
			}
		} catch (NumberFormatException e) {
			return false;
		}
		return true;
	}
	
	/*--------------------------------------------------------------*/
	/*----------------        Outer Methods         ----------------*/
	/*--------------------------------------------------------------*/
	
	public void process(Ship b){
		assert(validate()) : this;
		adjustShields();
		if(shieldsUp){softFlux+=fluxPerCenti;}
		if(!venting){fireWeapons(b);}
		if(venting){softFlux-=dissipationPerCenti;}
		softFlux-=dissipationPerCenti;
//		assert(false) : this;
		if(softFlux<0){
			hardFlux=max(0, hardFlux+softFlux);
			softFlux=0;
		}
//		assert(false) : this;
		assert(validate()) : this;
	}
	
	public boolean validate(){
		assert(!hasShield || shieldFlux>0);
		assert(hardFlux>=0);
		assert(softFlux>=0);
		assert(hull==maxHull || armor==0);
		return true;
	}
	
	public String toString(){
		StringBuilder sb=new StringBuilder();

		sb.append("side       \t").append(side).append('\n');
		sb.append("shipClass  \t").append(shipClass).append('\n');
		sb.append("shipName   \t").append(shipName).append('\n');
		sb.append("maxHull    \t").append(maxHull).append('\n');
		sb.append("maxArmor    \t").append(maxArmor).append('\n');
		sb.append("maxFlux    \t").append(maxFlux).append('\n');
		sb.append("downFlux   \t").append(downFlux).append('\n');
		sb.append("upFlux     \t").append(upFlux).append('\n');
		sb.append("dissipation\t").append(dissipation).append('\n');
		sb.append("fluxPerDamage\t").append(fluxPerDamage).append('\n');
		sb.append("minArmorModifier\t").append(minArmorModifier).append('\n');
		sb.append("strippedArmorFraction\t").append(strippedArmorFraction).append('\n');
		sb.append("bonusArmor \t").append(bonusArmor).append('\n');
		sb.append("shieldFlux \t").append(shieldFlux).append('\n');
		sb.append("dissipationPerC\t").append(dissipationPerCenti).append('\n');
		sb.append("fluxPerC   \t").append(fluxPerCenti).append('\n');
		sb.append("hull       \t").append(hull).append('\n');
		sb.append("armor      \t").append(armor).append('\n');
		sb.append("hardFlux   \t").append(hardFlux).append('\n');
		sb.append("softFlux   \t").append(softFlux).append('\n');
		sb.append("flux       \t").append(flux()).append('\n');
		sb.append("hasShield  \t").append(hasShield).append('\n');
		sb.append("canVent    \t").append(canVent).append('\n');
		sb.append("shieldsUp  \t").append(shieldsUp).append('\n');
		sb.append("venting    \t").append(venting);
		for(Weapon w : weapons){
			sb.append('\n');
			sb.append(w);
		}
		return sb.toString();
	}
	
	
	/*--------------------------------------------------------------*/
	/*----------------        Inner Methods         ----------------*/
	/*--------------------------------------------------------------*/
	
	void adjustShields(){
		if(flux()+fluxPerCenti>=downFlux){
			shieldsUp=false;
			if(canVent){venting=true;}
		}else if(flux()<=upFlux && !venting){
			if(hasShield){shieldsUp=true;}
		}else if(venting && flux()<=0){
			venting=false;
			if(hasShield){shieldsUp=true;}
		}
	}
	
	void fireWeapons(Ship b){
		assert(!venting);
		for(Weapon w : weapons){
			w.use(this, b);
		}
	}
	
	/*--------------------------------------------------------------*/
	/*----------------           Helpers            ----------------*/
	/*--------------------------------------------------------------*/
	
	/**
	 * Parse this argument.  More liberal than Boolean.parseBoolean.
	 * Null, t, true, or 1 all yield true.
	 * Everything else, including the String "null", is false.
	 * @param s Argument to parse
	 * @return boolean form
	 */
	public static boolean parseBoolean(String s){
		if(s==null || s.length()<1){return true;}
		if(s.length()==1){
			char c=Character.toLowerCase(s.charAt(0));
			return c=='t' || c=='1';
		}
		if(s.equalsIgnoreCase("null") || s.equalsIgnoreCase("none")){return false;}
		return Boolean.parseBoolean(s);
	}
	
	public static final int min(int x, int y){return x<y ? x : y;}
	public static final int max(int x, int y){return x>y ? x : y;}
	public static final float min(float x, float y){return x<y ? x : y;}
	public static final float max(float x, float y){return x>y ? x : y;}
	public static final int roundUp(float x){return (int)Math.ceil(x);}
	
	/*--------------------------------------------------------------*/
	/*----------------           Fields             ----------------*/
	/*--------------------------------------------------------------*/
	
	/*--------------------------------------------------------------*/
	/*----------------     Initialized Fields       ----------------*/
	/*--------------------------------------------------------------*/
	
	char side; //A or B
	
	String shipClass;
	String shipName;
	
	int maxHull;
	int maxArmor;
	int maxFlux;
	int downFlux=-1; //Flux at which shields drop
	int upFlux=-1; //Flux at which shields recover
	int dissipation;
	boolean hasShield=true;
	boolean canVent=false;
	float fluxPerDamage; //typically 1.0f (this is shield efficiency)
	float minArmorModifier=0.15f; //could be 0.10f
	float strippedArmorFraction=0.05f; //Residual armor when stripped
	float bonusArmor=0; //could be 150
	float shieldFlux=0; //flux per second for shield
	
	ArrayList<Weapon> weapons=new ArrayList<Weapon>();
	
	/*--------------------------------------------------------------*/
	/*----------------        Derived Fields        ----------------*/
	/*--------------------------------------------------------------*/

	final float dissipationPerCenti;
	final float ventingDissipationPerCenti;
	final float fluxPerCenti;
	
	/*--------------------------------------------------------------*/
	/*----------------       Mutable Fields         ----------------*/
	/*--------------------------------------------------------------*/
	
	float hull;
	float armor;
	float hardFlux;
	float softFlux;
	float empDamageTaken;
	float flux(){return hardFlux+softFlux;}

	boolean shieldsUp=false;
	boolean venting=false;
	
	/*--------------------------------------------------------------*/
	/*----------------          Constants           ----------------*/
	/*--------------------------------------------------------------*/
	
	public static final int BOLT=0, BEAM=1;
	public static final int HARD=0, SOFT=1;
	public static final int KINETIC=0, HE=1, FRAG=2, ENERGY=3;

	public static final String[] types={"bolt", "beam"};
	public static final String[] fluxTypes={"hard", "soft"};
	public static final String[] damageTypes={"kinetic", "he", "frag", "energy"};
	
}
