package org.bench4q.web.api;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBException;

import org.apache.log4j.Logger;
import org.bench4q.share.helper.ExceptionLog;
import org.bench4q.share.helper.MarshalHelper;
import org.bench4q.share.models.agent.RunScenarioModel;
import org.bench4q.share.models.agent.scriptrecord.BehaviorModel;
import org.bench4q.share.models.agent.scriptrecord.PageModel;
import org.bench4q.share.models.agent.scriptrecord.UsePluginModel;
import org.bench4q.share.models.master.OperateScriptServerResponseModel;
import org.bench4q.share.models.master.ScriptModel;
import org.bench4q.web.masterMessager.ScriptMessager;
import org.bench4q.web.model.ScenarioModel;
import org.bench4q.web.service.ScriptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.multipart.MultipartFile;

import com.google.gson.Gson;

/**
 * @author hmm
 *
 */
@Controller
@SessionAttributes({ "accessToken" })
public class ScriptController extends BaseController {
	private ScriptMessager scriptMessager;
	private ScriptService scriptService;

	public ScriptMessager getScriptMessager() {
		return scriptMessager;
	}

	@Autowired
	public void setScriptMessager(ScriptMessager scriptMessager) {
		this.scriptMessager = scriptMessager;
	}

	public ScriptService getScriptService() {
		return scriptService;
	}

	@Autowired
	public void setScriptService(ScriptService scriptService) {
		this.scriptService = scriptService;
	}

	@RequestMapping("loadScripts")
	@ResponseBody
	public Map<String, Object> loadScript(
			@ModelAttribute("accessToken") String accessToken) {
		Map<String, Object> map = new HashMap<String, Object>();
		OperateScriptServerResponseModel operateScriptServerResponseModel = this
				.getScriptMessager().loadScripts(accessToken);
		if (!operateScriptServerResponseModel.isSuccess()) {
			return fail(map,
					operateScriptServerResponseModel.getFailCauseString());
		} else {
			map = success(map);
			map.put("scripts",
					operateScriptServerResponseModel.getScriptModels());
			return map;
		}

	}

	@RequestMapping("deleteScript")
	@ResponseBody
	public Map<String, Object> deleteScript(
			@ModelAttribute("accessToken") String accessToken,
			@RequestParam String scriptId) {
		Map<String, Object> map = new HashMap<String, Object>();
		return processScriptResponse(map, this.getScriptMessager()
				.deleteScript(accessToken, scriptId));
	}

	@RequestMapping("getScript")
	@ResponseBody
	public Map<String, Object> loadScriptContent(
			@ModelAttribute("accessToken") String accessToken,
			@RequestParam String scriptId) {

		Map<String, Object> map = new HashMap<String, Object>();
		OperateScriptServerResponseModel operateScriptServerResponseModel = this
				.getScriptMessager().queryScriptById(accessToken, scriptId);
		if (operateScriptServerResponseModel.isSuccess()) {
			ScriptModel scriptModel = operateScriptServerResponseModel
					.getScriptModels().get(0);
			RunScenarioModel runScenarioModel = (RunScenarioModel) MarshalHelper
					.tryUnmarshal(RunScenarioModel.class,
							scriptModel.getScriptContent());
			if (runScenarioModel == null) {
				return fail(map, "error script");
			}
			Gson gson = new Gson();
			map = success(map);
			map.put("script", gson.toJson(runScenarioModel));
			map.put("scriptId", scriptModel.getId());
			return map;
		} else {
			map = fail(map,
					operateScriptServerResponseModel.getFailCauseString());
			return map;
		}
	}

	@RequestMapping("getScriptXml")
	@ResponseBody
	public Map<String, Object> loadScriptXml(
			@ModelAttribute("accessToken") String accessToken,
			@RequestParam String scriptId) {

		Map<String, Object> map = new HashMap<String, Object>();
		OperateScriptServerResponseModel operateScriptServerResponseModel = this
				.getScriptMessager().queryScriptById(accessToken, scriptId);
		if (operateScriptServerResponseModel.isSuccess()) {
			ScriptModel scriptModel = operateScriptServerResponseModel
					.getScriptModels().get(0);
			RunScenarioModel runScenarioModel = (RunScenarioModel) MarshalHelper
					.tryUnmarshal(RunScenarioModel.class,
							scriptModel.getScriptContent());
			if (runScenarioModel == null) {
				return fail(map, "error script");
			}
			success(map);
			map.put("script", scriptModel.getScriptContent());// MarshalHelper.tryMarshal(runScenarioModel));
			return map;
		} else {
			map = fail(map,
					operateScriptServerResponseModel.getFailCauseString());
			return map;
		}
	}

	@RequestMapping("uploadScriptFile")
	@ResponseBody
	public Map<String, Object> uploadScriptFile(
			@ModelAttribute("accessToken") String accessToken,
			@RequestParam String scriptName,
			@RequestParam MultipartFile scriptFile) throws IOException {
		Map<String, Object> map = new HashMap<String, Object>();
		String script = new String(scriptFile.getBytes());
		try {
			MarshalHelper.unmarshal(ScenarioModel.class, script);
		} catch (JAXBException e) {
			Logger.getLogger(this.getClass()).info(
					ExceptionLog.getStackTrace(e));
			return fail(map, "error script file");
		}

		OperateScriptServerResponseModel operateScriptServerResponseModel = this
				.getScriptMessager().uploadScriptFile(accessToken, scriptName,
						script);
		if (operateScriptServerResponseModel.isSuccess()) {
			success(map);
			map.put("message", (Object) new String("upload script:"
					+ scriptName + " success!"));
			return map;
		} else {
			return fail(map,
					operateScriptServerResponseModel.getFailCauseString());
		}
	}

	@RequestMapping("uploadScript")
	@ResponseBody
	public Map<String, Object> saveScript(
			@ModelAttribute("accessToken") String accessToken,
			@RequestParam(required = false) String scriptName,
			@RequestParam String content,
			@RequestParam(required = false) MultipartFile[] paramFiles)
			throws JAXBException {
		Map<String, Object> map = new HashMap<String, Object>();
		Gson gson = new Gson();
		ScenarioModel scenarioModel = gson.fromJson(content,
				ScenarioModel.class);
		String name = scenarioModel.getScriptName();
		OperateScriptServerResponseModel operateScriptServerResponseModel = this
				.getScriptMessager().uploadScript(
						accessToken,
						name,
						MarshalHelper.tryMarshal(scenarioModel
								.getRunScenarioModel()), paramFiles);
		if (operateScriptServerResponseModel.isSuccess()) {
			success(map);
			map.put("message", (Object) new String("upload script:"
					+ scriptName + " success!"));
			return map;
		} else {
			return fail(map,
					operateScriptServerResponseModel.getFailCauseString());
		}
	}

	@RequestMapping(value = "submitScriptCreated/{scriptName}")
	@ResponseBody
	public Map<String, Object> submitScriptCreated(
			@ModelAttribute("accessToken") String accessToken,
			@RequestParam String runScenarioModelStr,
			@PathVariable String scriptName,
			@RequestParam(required = false) MultipartFile[] paramFiles) {
		Map<String, Object> map = new HashMap<String, Object>();
		Gson gson = new Gson();
		RunScenarioModel runScenarioModel = gson.fromJson(runScenarioModelStr,
				RunScenarioModel.class);

		// RunScenarioModel runScenarioModel = this.getScriptService()
		// .createRunScenarioModel(scenarioModel.getUsePlugins(),
		// scenarioModel.getBehaviors());
		OperateScriptServerResponseModel operateScriptServerResponseModel = this
				.getScriptMessager().uploadScript(accessToken, scriptName,
						MarshalHelper.tryMarshal(runScenarioModel), paramFiles);
		if (operateScriptServerResponseModel == null) {
			return fail(map, SERVER_ERROR);
		}
		return success(map);

	}

	@RequestMapping("updateScript")
	@ResponseBody
	public Map<String, Object> updateScript(
			@ModelAttribute("accessToken") String accessToken,
			@RequestParam String scriptId,
			@RequestParam String scriptName,
			@RequestParam String runScenarioModelStr,
			@RequestParam(required = false) MultipartFile[] paramFiles) {
		Map<String, Object> map = new HashMap<String, Object>();
		// try {
		// System.out.println(script);
		// MarshalHelper.unmarshal(RunScenarioModel.class, script);
		// } catch (Exception e) {
		// return fail(map, "");
		// }
		Gson gson = new Gson();
		RunScenarioModel runScenarioModel = gson.fromJson(runScenarioModelStr,
				RunScenarioModel.class);
		return processScriptResponse(
				map,
				this.getScriptMessager().updateScript(accessToken, scriptId,
						MarshalHelper.tryMarshal(runScenarioModel), scriptName));

	}

	@RequestMapping("startRecordServer")
	@ResponseBody
	public Map<String, Object> startRecordServer(
			@ModelAttribute("accessToken") String accessToken) {
		Map<String, Object> map = new HashMap<String, Object>();
		OperateScriptServerResponseModel operateScriptServerResponseModel = this
				.getScriptMessager().startScriptRecordServer(accessToken);
		if (operateScriptServerResponseModel.isSuccess()) {
			map = success(map);
			map.put("server", operateScriptServerResponseModel);
			return map;
		} else {
			map = fail(map,
					operateScriptServerResponseModel.getFailCauseString());
			return map;
		}

	}
	@RequestMapping("startRecording")
	@ResponseBody
	public Map<String, Object> startRecording(
			@ModelAttribute("accessToken") String accessToken,
			@RequestParam String port, @RequestParam String scriptRecordUUID) {
		return processScriptResponse(
				new HashMap<String, Object>(),
				this.getScriptMessager().startRecording(accessToken,
						port, scriptRecordUUID));
	}
	
	@RequestMapping("stopRecordServer")
	@ResponseBody
	public Map<String, Object> stopRecordServer(
			@ModelAttribute("accessToken") String accessToken,
			@RequestParam String port, @RequestParam String scriptRecordUUID) {
		return processScriptResponse(
				new HashMap<String, Object>(),
				this.getScriptMessager().stopScriptRecordServer(accessToken,
						port, scriptRecordUUID));
	}
	
	@RequestMapping("stopScriptRecording")
	@ResponseBody
	public Map<String, Object> stopScriptRecording(
			@ModelAttribute("accessToken") String accessToken,
			@RequestParam String port, @RequestParam String scriptRecordUUID) {
		return processScriptResponse(
				new HashMap<String, Object>(),
				this.getScriptMessager().stopScriptRecording(accessToken,
						port, scriptRecordUUID));
	}
	
	@RequestMapping("testRecordProxy")
	@ResponseBody
	public Map<String, Object> testRecordProxy(
			@RequestHeader HttpHeaders headers) {
		Map<String,Object> map = new HashMap<String,Object>();
		//is from proxy
		boolean isFromProxy = Boolean.parseBoolean(headers.getFirst("FromProxy"));
		if(isFromProxy)
		{
			success(map);
		}else{
			fail(map,"proxy setting error!");
		}
		return map ;
	}

	@RequestMapping("saveScriptRecorded")
	@ResponseBody
	public Map<String, Object> saveScriptRecorded(
			@ModelAttribute("accessToken") String accessToken,
			@RequestParam String port, @RequestParam String scriptRecordUUID,
			@RequestParam String scriptName) {
		Map<String, Object> map = new HashMap<String, Object>();
		return processScriptResponse(
				map,
				this.getScriptMessager().saveScriptRecorded(accessToken,
						scriptName, port, scriptRecordUUID));

	}

	@RequestMapping("editScriptWithPlugin/{scriptId}")
	@ResponseBody
	public Map<String, Object> editScriptWithPlugin(
			@ModelAttribute("accessToken") String accessToken,
			@PathVariable String scriptId) {
		Map<String, Object> map = new HashMap<String, Object>();
		OperateScriptServerResponseModel operateScriptServerResponseModel = this
				.getScriptMessager().queryScriptById(accessToken, scriptId);
		if (operateScriptServerResponseModel == null
				|| !operateScriptServerResponseModel.isSuccess()) {
			return fail(map, "");
		}

		RunScenarioModel runScenarioModel = (RunScenarioModel) MarshalHelper
				.tryUnmarshal(
						RunScenarioModel.class,
						operateScriptServerResponseModel.getScriptModels()
								.get(0).getScriptContent());
		this.updateRunScenarioModel(runScenarioModel);
		map.put("usePlugin",
				this.getScriptService().createUsePluginInfoModelsWithValue(
						runScenarioModel.getUsePlugins(), accessToken));
		map.put("behaviors",
				this.getScriptService().createBehaviorInfoModelsWithValue(
						runScenarioModel, accessToken));

		return map;
	}
	
	private void updateRunScenarioModel(RunScenarioModel runScenarioModel) {
		List<BehaviorModel> behaviorModels = new LinkedList<BehaviorModel>();
		for (PageModel pageModel : runScenarioModel.getPages()) {
			behaviorModels.add(pageModel.getBatches().get(0).getBehaviors()
					.get(0));
		}
		int count = 0;
		for (UsePluginModel usePluginModel : runScenarioModel.getUsePlugins()) {
			String usePluginId = usePluginModel.getName() + "_" + count;
			for (BehaviorModel behaviorModel : behaviorModels) {
				if (behaviorModel.getUse().equals(usePluginModel.getId())) {
					behaviorModel.setUse(usePluginId);
				}
			}
			count++;
			usePluginModel.setId(usePluginId);
		}
		count = 0;
		for (BehaviorModel behaviorModel : behaviorModels) {
			behaviorModel.setId(count);
			count++;
		}
	}

	private Map<String, Object> processScriptResponse(Map<String, Object> map,
			OperateScriptServerResponseModel operateScriptServerResponseModel) {
		if (operateScriptServerResponseModel.isSuccess()) {
			return success(map);
		} else {
			map = fail(map,
					operateScriptServerResponseModel.getFailCauseString());
			return map;
		}
	}

}
