package org.bench4q.master.domain.entity;

import java.util.UUID;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.bench4q.master.domain.RunningAgentInterface;
import org.bench4q.master.domain.RunningScriptInterface;
import org.bench4q.master.domain.repository.AgentRepository;
import org.bench4q.master.infrastructure.communication.AgentMessenger;
import org.bench4q.share.helper.MarshalHelper;
import org.bench4q.share.models.agent.RunScenarioModel;
import org.bench4q.share.models.agent.RunScenarioResultModel;
import org.bench4q.share.models.agent.StopTestModel;
import org.bench4q.share.models.agent.TestBriefStatusModel;

@Entity
@Table(name = "runningAgent")
public class RunningAgentDB implements RunningAgentInterface {
	private int id;
	private Agent agent;
	private int loadInUse;
	private Script script;
	private UUID agentRunId;
	private TestPlanScript testPlanScript;

	private AgentMessenger agentMessenger;
	private AgentRepository agentRepository;
	private boolean hasSubstitute;
	private boolean stoped;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id", nullable = false)
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	@ManyToOne
	@JoinColumn(name = "agentId")
	public Agent getAgent() {
		return agent;
	}

	public void setAgent(Agent agent) {
		this.agent = agent;
	}

	@Column(name = "loadInUse", nullable = false)
	public int getLoadInUse() {
		return loadInUse;
	}

	public void setLoadInUse(int loadInUse) {
		this.loadInUse = loadInUse;
	}

	@ManyToOne
	@JoinColumn(name = "scriptId")
	public Script getScript() {
		return script;
	}

	public void setScript(Script script) {
		this.script = script;
	}

	@Column(name = "agentRunId")
	public UUID getAgentRunId() {
		return agentRunId;
	}

	public void setAgentRunId(UUID agentRunId) {
		this.agentRunId = agentRunId;
	}

	@ManyToOne
	@JoinColumn(name = "testPlanScriptId")
	public TestPlanScript getTestPlanScript() {
		return testPlanScript;
	}

	public void setTestPlanScript(TestPlanScript testPlanScript) {
		this.testPlanScript = testPlanScript;
	}

	@Transient
	public int getScriptId() {
		return this.getTestPlanScript().getId();
	}

	@Transient
	public boolean isBreakDown() {
		return this.getAgent().isBreakDown();
	}

	@Transient
	public AgentMessenger getAgentMessenger() {
		return agentMessenger;
	}

	public void setAgentMessenger(AgentMessenger agentMessenger) {
		this.agentMessenger = agentMessenger;
	}

	@Transient
	private AgentRepository getAgentRepository() {
		return agentRepository;
	}

	public void setAgentRepository(AgentRepository agentRepository) {
		this.agentRepository = agentRepository;
	}

	@Transient
	public boolean hasSubstitute() {
		return hasSubstitute;
	}

	private void setHasSubstitute(boolean hasSubstitute) {
		this.hasSubstitute = hasSubstitute;
	}

	@Transient
	private boolean isStoped() {
		return stoped;
	}

	private void setStoped(boolean hasStoped) {
		this.stoped = hasStoped;
	}

	public static RunningAgentDB buildRunningAgentDBWithoutId(Agent agent,
			int loadInUse, TestPlanScript testPlanScript, UUID agentRunId) {
		RunningAgentDB runningAgentDB = new RunningAgentDB();
		runningAgentDB.setAgent(agent);
		runningAgentDB.setAgentRunId(agentRunId);
		runningAgentDB.setLoadInUse(loadInUse);
		runningAgentDB.setTestPlanScript(testPlanScript);
		return runningAgentDB;
	}

	public static RunningAgentDB buildRunningAgentDBWithoutIdAndRunningScript(
			Agent agent, int loadInUse, UUID agentRunId) {
		return buildRunningAgentDBWithoutId(agent, loadInUse, null, agentRunId);
	}

	@Transient
	public RunningScriptInterface getRunningScript() {
		return this.getTestPlanScript();
	}

	@Override
	public RunningAgentInterface completeWithRunningScript(
			RunningScriptInterface runningScript) {
		this.setTestPlanScript((TestPlanScript) runningScript);
		return this;
	}

	/**
	 * Distribute script and parameter files, and it will fill the agentRunId of
	 * this RunningAgengDB
	 * 
	 * @return
	 */

	public boolean distributeScriptAndParams() {
		Script script2 = this.getTestPlanScript().getScript();
		RunScenarioModel runScenarioModel = (RunScenarioModel) MarshalHelper
				.tryUnmarshal(RunScenarioModel.class,
						script2.getFilteredScriptCnt());
		runScenarioModel.setPoolSize(getLoadInUse());
		RunScenarioResultModel runScenarioResultModel = this
				.getAgentMessenger().submitScenrioWithParams(this.getAgent(),
						this.getAgentRunId(), script2.loadParamFiles(),
						runScenarioModel, this.getRunningScript().getStartTime());
		if (runScenarioResultModel == null) {
			return false;
		}
		return true;
	}

	/**
	 * 
	 * @return
	 */
	public boolean run() {
		// TODO: refactor this function to transaction
		RunScenarioResultModel runScenarioResultModel = this
				.getAgentMessenger().runWithParams(this.getAgent(),
						this.getAgentRunId());
		return runScenarioResultModel != null;
	}

	public boolean stop() {
		if (this.isStoped()) {
			return true;
		}
		StopTestModel result = this.getAgentMessenger().stop(getAgent(),
				getAgentRunId());
		this.getAgent().backLoad(this.getLoadInUse());
		this.getAgentRepository().update(this.getAgent());
		if (result != null) {
			this.setStoped(result.isSuccess());
		}
		return result != null && result.isSuccess();
	}

	public void substituteOnBoard() {
		this.getTestPlanScript().applyForLoad(this.getLoadInUse());
		this.distributeScriptAndParams();
		this.run();
		this.setHasSubstitute(true);
	}

	@Override
	public TestBriefStatusModel briefAll() {
		if (this.isStoped()) {
			return null;
		}
		return this.getAgentMessenger().scriptBriefAll(this.getAgent(),
				this.getAgentRunId());
	}
}
