package org.bench4q.master.api;

import java.io.File;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.bench4q.master.domain.entity.Port;
import org.bench4q.master.domain.entity.Script;
import org.bench4q.master.domain.factory.BusinessModelMapFactory;
import org.bench4q.master.domain.service.PortPoolService;
import org.bench4q.master.domain.service.ScriptService;
import org.bench4q.master.domain.service.UserService;
import org.bench4q.master.exception.Bench4QException;
import org.bench4q.master.exception.ExceptionLog;
import org.bench4q.recorder.ScriptCapturer;
import org.bench4q.share.models.master.OperateScriptServerResponseModel;
import org.bench4q.share.models.master.ScriptModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

@Controller
@RequestMapping("/script")
public class ScriptController extends BaseController {
	private ScriptCapturer scriptCapturer;
	private ScriptService scriptService;
	private PortPoolService portPoolService = new PortPoolService();
	private static final Object PORT_LOCK = new Object();
	private Logger logger = Logger.getLogger(ScriptController.class);

	private ScriptCapturer getScriptCapturer() {
		return scriptCapturer;
	}

	@Autowired
	private void setScriptCapturer(ScriptCapturer scriptCapturer) {
		this.scriptCapturer = scriptCapturer;
	}

	private ScriptService getScriptService() {
		return scriptService;
	}

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

	private PortPoolService getPortPoolService() {
		return portPoolService;
	}

	@Autowired
	private void setPortPoolService(PortPoolService portPoolService) {
		this.portPoolService = portPoolService;
	}

//	@RequestMapping(value = "/startScriptRecordServer", method = {
//			RequestMethod.GET, RequestMethod.POST })
//	@ResponseBody
//	public OperateScriptServerResponseModel startScriptRecordServer()
//			throws UnknownHostException {
//		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
//			return buildReponseModel(false,
//					"has no power for recording script!!!", "", -1, null, null,
//					null);
//		}
//		Port port = new Port();
//		synchronized (PORT_LOCK) {
//			port = this.getPortPoolService().getAPortNotInUse();
//			if (port == null) {
//				return buildReponseModel(false, "port is in use!", "", -1,
//						null, null, null);
//			}
//		}
//		UUID uuid = UUID.randomUUID();
//		this.getScriptCapturer().startRecord(port.getPort(),
//				buildScriptSavePath(), uuid.toString());
//
//		return buildReponseModel(true, "", this.getScriptCapturer()
//				.getIpHttpCaptureServerAdress(), port.getPort(), null, null,
//				uuid.toString());
//	}
	
	@RequestMapping(value = "/startScriptRecordServer", method = {
			RequestMethod.GET, RequestMethod.POST })
	@ResponseBody
	public OperateScriptServerResponseModel startScriptRecordServer()
			throws UnknownHostException {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			return buildReponseModel(false,
					"has no power for recording script!!!", "", -1, null, null,
					null);
		}
		Port port = new Port();
		synchronized (PORT_LOCK) {
			port = this.getPortPoolService().getAPortNotInUse();
			if (port == null) {
				return buildReponseModel(false, "port is in use!", "", -1,
						null, null, null);
			}
		}
		UUID uuid = UUID.randomUUID();
		this.getScriptCapturer().startRecordServer(port.getPort(),
				buildScriptSavePath(), uuid.toString());

		return buildReponseModel(true, "", this.getScriptCapturer()
				.getIpHttpCaptureServerAdress(), port.getPort(), null, null,
				uuid.toString());
	}
	
	@RequestMapping(value = "/startScriptRecording", method = {
			RequestMethod.POST, RequestMethod.GET })
	@ResponseBody
	public OperateScriptServerResponseModel startScriptRecording(
			@RequestParam int port, @RequestParam String fileNameUUID) {
		if (!checkScope(UserService.NORAML_AUTHENTICATION)) {
			return buildReponseModel(false,
					"has no power for stopScriptCapture!!!", "", -1, null,
					null, null);
		}

		this.getScriptCapturer().startRecording(port);
	
		return buildReponseModel(true, "RecordServer stop", "", port, null,
				null, fileNameUUID);
	}
	
	private String buildScriptSavePath() {
		String dirString = "Scripts" + System.getProperty("file.separator")
				+ this.getPrincipal().getUserName()
				+ System.getProperty("file.separator")
				+ new SimpleDateFormat("yyyyMMdd").format(new Date());
		File dirFile = new File(dirString);
		if (!dirFile.exists()) {
			dirFile.mkdirs();
		}
		return dirString;
	}

	@RequestMapping(value = "/stopScriptRecordServer", method = {
			RequestMethod.POST, RequestMethod.GET })
	@ResponseBody
	public OperateScriptServerResponseModel stopScriptRecordServer(
			@RequestParam int port, @RequestParam String fileNameUUID) {
		if (!checkScope(UserService.NORAML_AUTHENTICATION)) {
			return buildReponseModel(false,
					"has no power for stopScriptCapture!!!", "", -1, null,
					null, null);
		}

		this.getScriptCapturer().stopProxyServer(port);
		synchronized (PORT_LOCK) {
			this.getPortPoolService().backPort(port);
		}

		return buildReponseModel(true, "RecordServer stop", "", port, null,
				null, fileNameUUID);
	}

	@RequestMapping(value = "/stopScriptRecording", method = {
			RequestMethod.POST, RequestMethod.GET })
	@ResponseBody
	public OperateScriptServerResponseModel stopScriptRecording(
			@RequestParam int port, @RequestParam String fileNameUUID) {
		if (!checkScope(UserService.NORAML_AUTHENTICATION)) {
			return buildReponseModel(false,
					"has no power for stopScriptCapture!!!", "", -1, null,
					null, null);
		}

		this.getScriptCapturer().stopRecording(port);
		
		return buildReponseModel(true, "RecordServer stop", "", port, null,
				null, fileNameUUID);
	}
	
	@RequestMapping(value = "/saveScriptToDB", method = { RequestMethod.GET,
			RequestMethod.POST })
	@ResponseBody
	public OperateScriptServerResponseModel saveScriptToDB(
			@RequestParam String scriptName, @RequestParam int port,
			@RequestParam String fileNameUUID) {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			return buildReponseModel(false,
					"saveScriptToDB check your scope fail!", "", -1, null,
					null, null);
		}

		File file = new File(buildScriptSavePath()
				+ System.getProperty("file.separator") + fileNameUUID + ".xml");
		if (!file.exists()) {
			return buildReponseModel(false, "no that script", null, -1, null,
					null, fileNameUUID);
		}
		try {
			String content = FileUtils.readFileToString(file);
			logger.info("when saveToDB, scriptContent is " + content);
			boolean success = this.getScriptService().saveScript(scriptName,
					this.getPrincipal().getId(), content, null);
			// this.getUserService().add
			return buildReponseModel(success, "Save to DataBase!!", "", port,
					null, null, null);
		} catch (Exception e) {
			return buildReponseModel(false, "exception when read from file",
					null, -1, null, null, fileNameUUID);
		}
	}

	@RequestMapping(value = "/uploadScript/{scriptName}", method = {
			RequestMethod.PUT, RequestMethod.POST })
	@ResponseBody
	public OperateScriptServerResponseModel uploadScriptToDB(
			@PathVariable String scriptName,
			@RequestParam String scenarioModel,
			@RequestParam(value = "paramFiles[]", required = false) List<MultipartFile> paramFiles)
			throws Bench4QException {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			throw new Bench4QException(HAVE_NO_POWER, HAVE_NO_POWER
					+ "for upload a script", "/uploadScript");
		}
		try {
			boolean success = this.getScriptService().saveScript(scriptName,
					this.getPrincipal().getId(), scenarioModel, paramFiles);
			logger.info("upload script:" + scenarioModel);
			return buildReponseModel(success, null, null, -1, null, null, null);
		} catch (Exception e) {
			this.logger.error(ExceptionLog.getStackTrace(e));
			return buildReponseModel(false,
					"The script is not in the right format", null, -1, null,
					null, null);
		}
	}

	@RequestMapping(value = "/loadScriptList", method = { RequestMethod.POST,
			RequestMethod.GET })
	@ResponseBody
	public OperateScriptServerResponseModel loadScriptList() {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			return null;
		}
		List<Script> scripts = this.getScriptService().loadScripts(
				this.getPrincipal());

		return this.buildReponseModel(true, null, null, 0,
				dealWithCollection(scripts), null, null);
	}
	
	@RequestMapping(value = "/queryScriptsByDate", method = RequestMethod.POST)
	@ResponseBody
	public OperateScriptServerResponseModel queryScriptsByDate(
			@RequestParam Date startDate, @RequestParam Date endDate) {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			return null;
		}
		List<Script> scripts = this.getScriptService()
				.queryScriptsByCreateTime(startDate, endDate,
						this.getPrincipal());
		return this.buildReponseModel(true, null, null, 0,
				dealWithCollection(scripts), null, null);
	}

	private List<ScriptModel> dealWithCollection(Collection<Script> scripts) {
		List<ScriptModel> scriptModels = new ArrayList<ScriptModel>();
		for (Script script : scripts) {
			scriptModels.add(BusinessModelMapFactory.toModel(script));
		}
		return scriptModels;
	}

	@RequestMapping(value = "/queryScriptById", method = { RequestMethod.GET,
			RequestMethod.POST })
	@ResponseBody
	public OperateScriptServerResponseModel queryScriptById(
			@RequestParam int scriptId) {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			return null;
		}
		return this.buildReponseModel(true, null, null, 0, null,
				BusinessModelMapFactory.toModel(this.getScriptService()
						.getScript(scriptId)), null);
	}

	@RequestMapping(value = "/queryScriptByName", method = { RequestMethod.GET,
			RequestMethod.POST })
	@ResponseBody
	public OperateScriptServerResponseModel queryScriptByName(
			@RequestParam String name) {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {

			return null;
		}
		return this.buildReponseModel(true, null, null, 0, null,
				BusinessModelMapFactory.toModel(this.getScriptService()
						.getScriptByName(name)), null);
	}

	@RequestMapping(value = "/deleteScript", method = { RequestMethod.GET,
			RequestMethod.POST })
	@ResponseBody
	public OperateScriptServerResponseModel deleteScript(
			@RequestParam int scriptId) {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			return null;
		}
		return this.buildReponseModel(
				this.getScriptService().deleteScript(scriptId,
						this.getPrincipal().getId()), "", "", 0, null, null,
				null);
	}

	@RequestMapping(value = "updateScript/{scriptId}", method = {
			RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT })
	@ResponseBody
	public OperateScriptServerResponseModel updateScript(
			@PathVariable int scriptId, @RequestParam String content, @RequestParam String scriptName) {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			Logger.getLogger(ScriptController.class).info("no power");
			return null;
		}
		return this.buildReponseModel(
				this.getScriptService().alterScriptContent(scriptId,
						this.getPrincipal().getId(),content,scriptName), "", "", 0, null,
				null, null);
	}

//	@RequestMapping(value = "updateScript/{scriptId}", method = {
//			RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT })
//	@ResponseBody
//	public OperateScriptServerResponseModel updateScript(
//			@PathVariable int scriptId,
//			@RequestParam(value = "paramContents[]", required = true) List<String> paramContents,
//			@RequestParam(value = "paramFiles[]", required = false) List<MultipartFile> paramFiles)
//			throws Bench4QException {
//		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
//			throw new Bench4QException(HAVE_NO_POWER, HAVE_NO_POWER
//					+ "for update a script", "/updateScript");
//		}
//		try {
//			boolean success = this.getScriptService().alterScriptContent(scriptId,
//					this.getPrincipal().getId(), paramContents.get(0), paramContents.get(1));
//			logger.info("update script:" + paramContents.get(0));
//			return buildReponseModel(success, null, null, -1, null, null, null);
//		} catch (Exception e) {
//			this.logger.error(ExceptionLog.getStackTrace(e));
//			return buildReponseModel(false,
//					"The script is not in the right format", null, -1, null,
//					null, null);
//		}
//	}
	
	private OperateScriptServerResponseModel buildReponseModel(
			boolean isSuccess, String failCauseString, String hostName,
			int port, List<ScriptModel> scriptModels, ScriptModel scriptModel,
			String fileName) {
		OperateScriptServerResponseModel responseModel = new OperateScriptServerResponseModel();
		responseModel.setSuccess(isSuccess);
		responseModel.setFailCauseString(failCauseString);
		responseModel.setHostName(hostName);
		responseModel.setPort(port);
		responseModel.setFileName(fileName);
		if (scriptModels == null || scriptModels.size() == 0) {
			scriptModels = new ArrayList<ScriptModel>();
			scriptModels.add(scriptModel);
		}
		responseModel.setScriptModels(scriptModels);
		return responseModel;
	}

}
