/**
 *  
 */
package core.services;

import java.io.File;
import java.io.PrintWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import play.Play;
import core.services.ApiService.OKResponseObject;
import core.services.QuestionService.QuestionObjectComplete;
import core.services.TestService.CreateTestRequest;
import models.Report;
import models.Test;
import models.Test.TestStatus;
import models.User;
import play.Logger;
import utils.StringUtil;

/**
 *
 * @author luca
 */
public class ReportService {

	private static enum ReportServiceSingleton {
		INSTANCE;

		ReportService singleton = new ReportService();

		public ReportService getSingleton() {
			return singleton;
		}
	}

	public static ReportService instance() {
		return ReportService.ReportServiceSingleton.INSTANCE.getSingleton();
	}

	private static ApiService apiService = ApiService.instance();
	private static OptionService optionService = OptionService.instance();
	private static QuestionService questionService = QuestionService.instance();
	private static UserService userService = UserService.instance();

	public static class ReportObjectSmall {
		public String reportId;
		public String reportDate;
		public String reportCsvPath;
		public String reportTestName;
		public String reportTestId;
		public String authorId;
		public Boolean isSaved;
		public Boolean isUploaded;
		public Boolean toEdit;
		
		public ReportObjectSmall(String reportId, String reportDate, 
				String reportCsvPath, String reportTestName, 
				String reportTestId, String authorId) {
			this.reportId = reportId;
			this.reportDate = reportDate;
			this.reportCsvPath = reportCsvPath;
			this.reportTestName = reportTestName;
			this.reportTestId = reportTestId;
			this.authorId = authorId;
			this.isSaved = true;
			this.isUploaded = true;
			this.toEdit = false;
		}
		
	}

	/*public static class TestObjectComplete {

		public String TestId;

		public String TestName;

		public String authorId;

		public Boolean deleted;

		public String state;

		public String revId;

		public Boolean toEdit;

		public List<QuestionObjectComplete> questions;

		public Map<String, String> options;

	}*/

	/*public static class CreateReportRequest {
		public TestObjectComplete test;
	}*/

	public static class CreateReportResponse extends OKResponseObject {
		public ReportObjectSmall response;
	}

	public String createReport() {
		//TODO
		Report report = new Report();
		report.setAuthor(userService.getCurrentUserLogged());
		report.setReportDate(new Date());
		report.save();
		String json = apiService.getCurrentJson();
		TestResult req = null;
		try {
			req = (TestResult) apiService.buildObjectFromJson(json, TestResult.class);
			Test t = Test.findById(req.testId);
			if(t!=null){
				report.setReportTest(t);
				report.save();
			} else {
				System.err.println("Test non trovato: " + req.testId);
			}
		} catch(Exception e){
			System.out.println(e.getMessage());
			e.printStackTrace();
			instance().saveJson(json, "newjson_"+StringUtil.date(new Date(), Test.REVISION_DATE_FORMAT));
		}
		if(req == null){
			instance().saveJson(json, "newjson_"+StringUtil.date(new Date(), Test.REVISION_DATE_FORMAT));
			return null;
		}
		instance().saveJson(json,report.getUuid());
		
		String csvPath = instance().generateCsvFromJson(req, report.getUuid());
		if(csvPath != null) {
			report.setReportCsvPath(csvPath);
			report.save();
		}
		ReportObjectSmall ros = buildReportObjectSmallFromReport(report);
		CreateReportResponse res = new CreateReportResponse();
		res.response = ros;
		String ret = res.toJson();
		return ret;
	}


	private String generateCsvFromJson(TestResult req, String reportUuid) {
		
		
		
		String csv = "";
		String sep = "|";
		
		csv += "Test Name";
		csv += sep;
		csv += "Test ID";
		csv += sep;
		csv += "Test Administrator";
		csv += sep;
		csv += "Administrator ID";
		csv += sep;
		csv += "Participant First Name";
		csv += sep;
		csv += "Participant Last Name";
		csv += sep;
		csv += "Participant Age";
		csv += sep;
		csv += "Participant Gender";
		csv += sep;
		csv += "Participant Custom1";
		csv += sep;
		csv += "Participant Custom2";
		csv += sep;
		csv += "Participant Custom3";
		csv += sep;
		csv += "Block Number";
		csv += sep;
		csv += "Block presentation order";
		csv += sep;
		csv += "Slide ID";
		csv += sep;
		csv += "Item order in test";
		csv += sep;
		csv += "Item order in block";
		csv += sep;
		csv += "Answer";
		csv += sep;
		csv += "Expected Answer";
		csv += sep;
		csv += "Match";
		csv += sep;
		csv += "Stimulus slide start";
		csv += sep;
		csv += "Stimulus slide end";
		csv += sep;
		csv += "Delay";
		csv += sep;
		csv += "Answer slide start";
		csv += sep;
		csv += "Answer slide end";
		csv += sep;
		csv += "Question duration";
		csv += sep;
		csv += "U-R";
		csv += sep;
		csv += "Answer duration";
		csv += "\n";
		for(QuestionResult q : req.questions){
			csv += req.testName + sep;
			csv += req.testId + sep;
			csv += userService.getCurrentUserLogged().buildFullName() + sep;
			csv += userService.getCurrentUserLogged().getUuid() + sep;
			if(req.userInfo != null){
				csv += req.userInfo.name + sep;
				csv += req.userInfo.surname + sep;
				csv += req.userInfo.age + sep;
				csv += req.userInfo.gender + sep;
				csv += req.userInfo.custom1 + sep;
				csv += req.userInfo.custom2 + sep;
				csv += req.userInfo.custom3 + sep;
			} else {
				csv += "NA"+sep;
				csv += "NA"+sep;
				csv += "NA"+sep;
				csv += "NA"+sep;
				csv += "NA"+sep;
				csv += "NA"+sep;
				csv += "NA"+sep;
			}
			csv += q.randomizationBlock + sep;
			csv += q.randomizationPresentationBlock + sep;
			csv += q.stimulusSlideId + sep;
			csv += q.stimulusOrderInTest + sep;
			csv += q.stimulusOrderInBlock + sep;
			String dr = "";
			String sep1 = "";
			for(String dk : q.directAnswer.keySet()){
				Map<String, Object> dv = q.directAnswer.get(dk);
				for(String ddk : dv.keySet()){
					Object ddv = dv.get(ddk);
					dr += ddv + sep1;
					sep1 = ",";
				}
			}
			csv += dr + sep;
			String er = "";
			sep1 = "";
			for(String ek : q.expectedAnswer.keySet()){
				Object ev = q.expectedAnswer.get(ek);
				er += ev + sep1;
				sep1 = ",";
			}
			csv += er + sep;
			csv += "false" + sep;
			csv += q.stimulusStartTime + sep;
			csv += q.stimulusEndTime + sep;
			if(q.answerStartTime!=null && q.stimulusEndTime !=null){
				csv += q.answerStartTime - q.stimulusEndTime + sep; //q.delay + sep;
			} else {
				csv +="NA"+sep;
			}
			csv += q.answerStartTime + sep;
			csv += q.answerEndTime + sep;
			if(q.answerEndTime != null && q.stimulusStartTime != null) {
				csv += (q.answerEndTime - q.stimulusStartTime) + sep;
			} else {
				csv +=" "+sep;
			}
			if(q.answerEndTime != null && q.stimulusEndTime != null) {
			csv += (q.answerEndTime - q.stimulusEndTime) + sep;
			} else {
				csv +=" "+sep;
			}
			if(q.answerEndTime != null && q.answerStartTime != null) {
				csv += (q.answerEndTime - q.answerStartTime) + sep;
			} else {
				csv +=" "+sep;
			}
			csv += "\n";
		}
		return instance().saveCsv(csv, reportUuid);
	}
	
	private String saveJson(String json, String testId) {
		return saveFile(json, testId, "jsonDir", "json");
	}
	
	private String saveCsv(String stringa, String testId) {
		return saveFile(stringa, testId, "csvDir", "csv");
	}
	
	private String saveFile(String stringa, String fileName, String dirName, String extension) {
		File outDir = new File(Play.tmpDir, dirName);
		if(!outDir.exists()){
			outDir.mkdirs();
		}
		File outFile = new File(outDir, fileName + "." + extension);
		try {
			PrintWriter printer = new PrintWriter(outFile);
			printer.print(stringa);
			printer.flush();
			printer.close();
		} catch(Exception e){
			e.printStackTrace();
			return null;
		}
		return outFile.getAbsolutePath();
	}


	public static class ReportListResponse extends OKResponseObject {
		public List<ReportObjectSmall> response;
	}

	public String reportList(String type) {
		List<Report> reportObjs = new LinkedList<Report>();
		reportObjs = Report.findAllByType(type);
		List<ReportObjectSmall> reportObjsSmall = buildReportObjectSmallListFromReportList(reportObjs);
		ReportListResponse res = new ReportListResponse();
		res.response = reportObjsSmall;
		String ret = res.toJson();
		return ret;
	}

	public List<ReportObjectSmall> buildReportObjectSmallListFromReportList(List<Report> reports) {
		List<ReportObjectSmall> reportObjsSmall = new LinkedList<ReportObjectSmall>();
		if (reports != null && !reports.isEmpty()) {
			for (Report report : reports) {
				ReportObjectSmall reportObjSmall = buildReportObjectSmallFromReport(report);
				if(reportObjSmall!=null)
					reportObjsSmall.add(reportObjSmall);
			}
		}
		return reportObjsSmall;
	}

	/*public static class GetTestObjectSmallResponse extends OKResponseObject {
		public TestObjectSmall response;
	}

	public static class GetTestObjectCompleteResponse extends OKResponseObject {
		public TestObjectComplete response;
	}

	public String getTest(String testId, Boolean complete) {
		if (StringUtil.isNil(testId)) {
			return apiService.buildMandatoryParamsErrorResponse();
		}

		Test test = Test.findById(testId);
		if (test == null) {
			return apiService.buildEntityDoesNotExistsErrorResponse();
		}

		if (!userService.hasCurrentUserLoggedAdminRole()) {
			if (test.isDeleted()) {
				return apiService.buildEntityDoesNotExistsErrorResponse();
			}
		}

		if (complete == null) {
			return apiService.buildMandatoryParamsErrorResponse();
		}

		String ret;
		if (complete) {
			GetTestObjectCompleteResponse res = new GetTestObjectCompleteResponse();
			res.response = buildTestObjectCompleteFromTest(test);
			ret = res.toJson();
		} else {
			GetTestObjectSmallResponse res = new GetTestObjectSmallResponse();
			res.response = buildTestObjectSmallFromTest(test);
			ret = res.toJson();
		}
		return ret;
	}*/

	public ReportObjectSmall buildReportObjectSmallFromReport(Report report) {
		ReportObjectSmall ret = new ReportObjectSmall(report.getUuid(), 
		StringUtil.date(report.getReportDate(), Test.REVISION_DATE_FORMAT), report.getReportCsvPath(), 
		report.getReportTest().getTestName(), report.getReportTest().getUuid(), 
		report.getAuthor().getUuid());
		if(StringUtil.isNil(report.getReportCsvPath()))
				return null;
		File f = new File(report.getReportCsvPath());
		if(!f.exists())
			return null;
		
		return ret;
	}

	/*public TestObjectComplete buildTestObjectCompleteFromTest(Test test) {
		TestObjectComplete ret = new TestObjectComplete();
		if (test != null) {
			ret.authorId = test.getAuthor().getUuid();
			ret.deleted = test.isDeleted();
			ret.options = optionService.buildOptionsMapFromOptionsList(test.getOptions());
			ret.questions = questionService.buildQuestionObjectCompleteListFromQuestionList(test.getQuestions());
			ret.revId = test.buildRevisionDateFormatted();
			ret.state = test.getState().name();
			ret.TestId = test.getUuid();
			ret.TestName = test.getTestName();
			if (userService.hasCurrentUserLoggedAdminRole()) {
				ret.toEdit = true;
			} else {
				User currentUserLogged = userService.getCurrentUserLogged();
				if (currentUserLogged.equals(test.getAuthor())) {
					ret.toEdit = true;
				} else {
					ret.toEdit = false;
				}
			}
		}
		return ret;
	}
*/
	public static class TestResult {
		  String date;
		  String testName;
		  String testId;
		  String piId; //identificativo del presentatore;
		  String partecipantId; //identificativo del partecipante;
		  String partecipantAge; //età del partecipante;
		  List<QuestionResult> questions;
		  UserInfo userInfo;
		  Long testStartTime;
		  Long testEndTime;
	}

	public static class QuestionResult {
		public String stimulusOrderInBlock;
		public String stimulusOrderInTest;
		String randomizationBlock;
		String randomizationPresentationBlock;
		String stimulusSlideId;
		Integer stimulusSlideNumber;
		Map<String, Map<String, Object>> directAnswer;
		Map<String, Object> expectedAnswer;
		Long stimulusStartTime;
		Long stimulusEndTime;
		Long delay;
		Long answerStartTime;
		Long answerEndTime;
	}
	
	public static class UserInfo {
		String name;
		String surname;
		Integer age;
		String gender;
		String custom1;
		String custom2;
		String custom3;
	}

	public static File getCsvFile(String reportId) {
		if(StringUtil.isEmpty(reportId))
			return null;
		Report r = Report.findById(reportId);
		if(r==null)
			return null;
		if(StringUtil.isEmpty(r.getReportCsvPath()))
			return null;
		File f = new File(r.getReportCsvPath());
		return f;
	}
	
	public static String getCsvPublic(String reportId){
		if(StringUtil.isEmpty(reportId))
			return null;
		Report r = Report.findById(reportId);
		if(r==null)
			return null;
		if(StringUtil.isEmpty(r.getReportCsvPath()))
			return null;
		String code = UUID.randomUUID().toString();
		instance().publicLinks.put(code, reportId);
		return code;
	}
	
	public static File getCsvFromCode(String code){
		if(StringUtil.isEmpty(code))
			return null;
		if(!instance().publicLinks.containsKey(code))
			return null;
		String reportId = instance().publicLinks.get(code);
		instance().publicLinks.remove(code);
		Report r = Report.findById(reportId);
		if(r==null)
			return null;
		if(StringUtil.isEmpty(r.getReportCsvPath()))
			return null;
		File f = new File(r.getReportCsvPath());
		return f;
	}
	
	
	Map<String,String> publicLinks = new HashMap<String,String>();
	
	
}