// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
//      Magicbane Emulator Project © 2013 - 2022
//                www.magicbane.com


package engine.powers;

import engine.Enum.ModType;
import engine.Enum.SourceType;
import engine.Enum.StackType;
import engine.gameManager.PowersManager;
import engine.objects.*;
import engine.powers.poweractions.AbstractPowerAction;
import org.pmw.tinylog.Logger;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;


public class ActionsBase {

	public int UUID;
		public String IDString;
	public String effectID;
	public int minTrains;
	public int maxTrains;
	public float duration;
	public float ramp;
	public boolean addFormula;
	public String stackType;
	public StackType stackTypeType;
	public int stackOrder;

	public boolean greaterThanEqual = false;
	public boolean always = false;
	public boolean greaterThan = false;
	public String stackPriority;

	private AbstractPowerAction powerAction;

	/**
	 * No Table ID Constructor
	 */
	public ActionsBase() {

	}

	/**
	 * ResultSet Constructor
	 */
	public ActionsBase(ResultSet rs, HashMap<String, AbstractPowerAction> apa) throws SQLException {

		this.UUID = rs.getInt("ID");
				this.IDString = rs.getString("powerID");
		this.effectID = rs.getString("effectID");
		this.minTrains = rs.getInt("minTrains");
		this.maxTrains = rs.getInt("maxTrains");
		this.duration = rs.getFloat("duration");
		this.ramp = rs.getFloat("ramp");
		this.addFormula = (rs.getInt("useAddFormula") == 1) ? true : false;
		this.stackType = rs.getString("stackType");
		this.stackTypeType = StackType.GetStackType(this.stackType);
		this.stackOrder = rs.getInt("stackOrder");
		this.stackPriority = rs.getString("stackPriority");

		switch (stackPriority) {
		case "GreaterThanOrEqualTo":
			this.greaterThanEqual = true;
			break;
		case "Always":
			this.always = true;
			break;
		case "GreaterThan":
			this.greaterThan = true;
			break;
		}
		this.powerAction = apa.get(this.effectID);
	}

	protected ActionsBase(int uUID, String effectID, int minTrains, int maxTrains, float duration, float ramp,
			boolean addFormula, String stackType, int stackOrder, boolean greaterThanEqual, boolean always,
			boolean greaterThan, AbstractPowerAction powerAction) {
		super();
		UUID = uUID;
		this.effectID = effectID;
		this.minTrains = minTrains;
		this.maxTrains = maxTrains;
		this.duration = duration;
		this.ramp = ramp;
		this.addFormula = addFormula;
		this.stackType = stackType;
		this.stackTypeType = StackType.GetStackType(this.stackType);
		if (this.stackTypeType == null)
			Logger.info("Invalid Stack Type " + this.stackTypeType + " for " + this.effectID);
		this.stackOrder = stackOrder;
		this.greaterThanEqual = greaterThanEqual;
		this.always = always;
		this.greaterThan = greaterThan;
		this.powerAction = powerAction;
		
		if (this.greaterThanEqual)
			this.stackPriority = "GreaterThanOrEqualTo";
		else if(this.always)
			this.stackPriority = "Always";
		else if (this.greaterThan)
			this.stackPriority = "GreaterThan";
	
	}

	//	public static ArrayList<ActionsBase> getActionsBase(String ID) {
	//		PreparedStatementShared ps = null;
	//		ArrayList<ActionsBase> out = new ArrayList<ActionsBase>();
	//		try {
	//			ps = new PreparedStatementShared("SELECT * FROM actions where powerID = ?");
	//			ps.setString(1, ID);
	//			ResultSet rs = ps.executeQuery();
	//			while (rs.next()) {
	//				ActionsBase toAdd = new ActionsBase(rs);
	//				out.add(toAdd);
	//			}
	//			rs.close();
	//		} catch (Exception e) {
	//			Logger.error("ActionsBase", e);
	//		} finally {
	//			ps.release();
	//		}
	//		return out;
	//	}

	public static void getActionsBase(HashMap<String, PowersBase> powers, HashMap<String, AbstractPowerAction> apa) {
		PreparedStatementShared ps = null;
		try {
			ps = new PreparedStatementShared("SELECT * FROM static_power_action");
			ResultSet rs = ps.executeQuery();
			String IDString; ActionsBase toAdd; PowersBase pb;
			while (rs.next()) {
				IDString = rs.getString("powerID");
				pb = powers.get(IDString);
				if (pb != null) {
					toAdd = new ActionsBase(rs, apa);
					pb.getActions().add(toAdd);
				}
			}
			rs.close();
		} catch (Exception e) {
			Logger.error( e.toString());
		} finally {
			ps.release();
		}
		
		int gateID = 5000;
		for (String IDString : Runegate.GetAllOpenGateIDStrings()){
			gateID++;
			ActionsBase openGateActionBase = new ActionsBase(gateID, "OPENGATE", 5, 9999, 0, 0, true, "IgnoreStack", 0, true, false, false, PowersManager.getPowerActionByIDString("OPENGATE"));
		
			PowersBase openGatePower = powers.get(IDString);
			
			if (openGatePower == null){
				Logger.error( "no powerbase for action " + IDString);
				break;
			}
			openGatePower.getActions().add(openGateActionBase);
		}
	}
	

	public int getUUID() {
		return this.UUID;
	}

	public String getEffectID() {
		return this.effectID;
	}

	public int getMinTrains() {
		return this.minTrains;
	}

	public int getMaxTrains() {
		return this.maxTrains;
	}

	public float getDuration() {
		return this.duration;
	}

	public AbstractPowerAction getPowerAction() {
		return this.powerAction;
	}

	public int getDuration(int trains) {
		if (this.addFormula)
			return (int)((this.duration + (this.ramp * trains)) * 1000);
		else
			return (int)((this.duration * (1 + (this.ramp * trains))) * 1000);
	}

	public float getDurationAsFloat(int trains) {
		if (this.addFormula)
			return ((this.duration + (this.ramp * trains)) * 1000);
		else
			return ((this.duration * (1 + (this.ramp * trains))) * 1000);
	}

	public int getDurationInSeconds(int trains) {
		if (this.addFormula)
			return (int)(this.duration + (this.ramp * trains));
		else
			return (int)(this.duration * (1 + (this.ramp * trains)));
	}

	public String getStackType() {
		return this.stackType;
	}

	public int getStackOrder() {
		return this.stackOrder;
	}

	public boolean greaterThanEqual() {
		return this.greaterThanEqual;
	}

	public boolean greaterThan() {
		return this.greaterThan;
	}

	public boolean always() {
		return this.always;
	}

	//Add blocked types here
	public boolean blocked(AbstractWorldObject awo, PowersBase pb, int trains) {
		if (AbstractWorldObject.IsAbstractCharacter(awo)) {
			AbstractCharacter ac = (AbstractCharacter) awo;
			PlayerBonuses bonus = ac.getBonuses();
			if (bonus == null)
				return false;
			
			//TODO make this more efficient then testing strings
			if (this.stackType.equals("Stun") && bonus.getBool(ModType.ImmuneTo, SourceType.Stun))
				return true; //Currently stun immune. Skip stun
			else if(this.stackType.equals("Snare") && bonus.getBool(ModType.ImmuneTo, SourceType.Snare))
				return true; //Currently snare immune. Skip snare
			else if(this.stackType.equals("Blindness") && bonus.getBool(ModType.ImmuneTo, SourceType.Blind))
				return true; //Currently blind immune. Skip blind
			else if(this.stackType.equals("PowerInhibitor") && bonus.getBool(ModType.ImmuneTo, SourceType.Powerblock))
				return true; //Currently power block immune. Skip power block
			else if (this.stackType.equals("Root") && bonus.getBool(ModType.ImmuneTo, SourceType.Root))
				return true;
			//			else if (pb.isHeal() && (bonus.getByte("immuneTo.Heal")) >= trains)
			//				return true; //Currently shadowmantled. Skip heals
			else if (this.stackType.equals("Flight") && bonus.getBool(ModType.NoMod, SourceType.Fly))
				return true;
			else if (this.stackType.equals("Track") && bonus.getBool(ModType.CannotTrack, SourceType.None))
				return true;
			else return pb.vampDrain() && bonus.getBool(ModType.BlockedPowerType, SourceType.VAMPDRAIN);
		}
		return false;
	}
}