package cn.com.qmth.stmms.admin.report;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.qmth.boot.tools.io.ZipWriter;

import cn.com.qmth.stmms.admin.exam.BaseExamController;
import cn.com.qmth.stmms.admin.thread.ScoreReportThread;
import cn.com.qmth.stmms.biz.exam.model.Exam;
import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
import cn.com.qmth.stmms.biz.exam.service.*;
import cn.com.qmth.stmms.biz.file.enums.FormatType;
import cn.com.qmth.stmms.biz.file.service.FileService;
import cn.com.qmth.stmms.biz.lock.LockService;
import cn.com.qmth.stmms.biz.report.model.ReportSubject;
import cn.com.qmth.stmms.biz.report.query.ReportSubjectQuery;
import cn.com.qmth.stmms.biz.report.service.*;
import cn.com.qmth.stmms.common.annotation.Logging;
import cn.com.qmth.stmms.common.annotation.RoleRequire;
import cn.com.qmth.stmms.common.domain.WebUser;
import cn.com.qmth.stmms.common.enums.LockType;
import cn.com.qmth.stmms.common.enums.LogType;
import cn.com.qmth.stmms.common.enums.Role;
import cn.com.qmth.stmms.common.utils.Encodes;
import cn.com.qmth.stmms.common.utils.RequestUtils;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

/**
 * 课程分段统计
 *
 * @author xuwenjin
 * @date 2019-04-12 17:05
 */
@Controller
@RequestMapping("/admin/exam/reportSubjectRange")
public class ReportSubjectRangeController extends BaseExamController {

    protected static Logger log = LoggerFactory.getLogger(ReportSubjectRangeController.class);

    @Autowired
    private ReportSubjectService reportSubjectService;

    @Autowired
    private ExamSubjectService subjectService;

    @Autowired
    private ExamQuestionService questionService;

    @Autowired
    private LockService lockService;

    @Qualifier("task-executor")
    @Autowired
    private AsyncTaskExecutor taskExecutor;

    @Autowired
    private ExamStudentService studentService;

    @Autowired
    private MarkGroupService groupService;

    @Autowired
    private ExamService examService;

    @Autowired
    private ReportService reportService;

    @Autowired
    private FileService fileService;

    @Autowired
    private ReportSubjectQuestionService reportSubjectQuestionService;

    @Autowired
    private ReportSubjectClassService reportSubjectClassService;

    @Autowired
    private ReportSubjectTeacherService reportSubjectTeacherService;

    @Autowired
    private ReportSubjectCollegeService reportSubjectCollegeService;

    @Autowired
    private ReportSubjectGroupService reportSubjectGroupService;

    @Logging(menu = "课程分段统计", type = LogType.QUERY)
    @RequestMapping
    public String list(Model model, HttpServletRequest request, ReportSubjectQuery query) {
        WebUser wu = RequestUtils.getWebUser(request);
        int examId = getSessionExamId(request);
        List<ExamSubject> sList = getExamSubject(examId, wu);
        if (StringUtils.isNotBlank(query.getSubjectCode()) && query.getRange() != null) {
            ReportSubject subject = reportSubjectService.findOne(examId, query.getSubjectCode());
            if (subject != null && subject.getScoreRange() != null && subject.getTotalScore() != null
                    && subject.getRealityCount() != null) {
                model.addAttribute("total", getScoreRange(subject.getScoreRange(), subject.getTotalScore(),
                        subject.getRealityCount(), query.getRange()));
            }
            model.addAttribute("locked",
                    lockService.isLocked(LockType.SCORE_CALCULATE, examId, query.getSubjectCode()));
        }
        model.addAttribute("oldSubjectCode", query.getSubjectCode());
        model.addAttribute("subjectList", sList);
        model.addAttribute("query", query);
        return "modules/report/reportSubjectRange";
    }

    @Logging(menu = "课程统计导出", type = LogType.EXPORT)
    @RequestMapping("/export")
    public String export(ReportSubjectQuery query, HttpServletRequest request, HttpServletResponse response,
            RedirectAttributes redirectAttributes, @RequestParam(required = false) Boolean all) {
        int examId = getSessionExamId(request);
        response.reset();
        response.setContentType("application/octet-stream; charset=utf-8");
        try {
            ZipWriter writer = ZipWriter.create(response.getOutputStream());
            if (all) {
                Exam exam = examService.findById(examId);
                WebUser wu = RequestUtils.getWebUser(request);
                response.setHeader("Content-Disposition",
                        "attachment; filename=" + Encodes.urlEncode(exam.getName() + ".zip"));
                List<ExamSubject> list = getExamSubject(examId, wu);
                for (ExamSubject subject : list) {
                    String subjectName = subject.getCode() + "-" + subject.getName();
                    addFileToZip(examId, writer, subjectName);
                }
            } else {
                if (StringUtils.isBlank(query.getSubjectCode())) {
                    addMessage(redirectAttributes, "导出分析失败！请先查询出结果再导出");
                    return "redirect:/admin/exam/reportSubjectRange";
                }
                ExamSubject subject = subjectService.find(examId, query.getSubjectCode());
                String subjectName = subject.getCode() + "-" + subject.getName();
                response.setHeader("Content-Disposition",
                        "attachment; filename=" + Encodes.urlEncode(subjectName + ".zip"));
                addFileToZip(examId, writer, subjectName);
            }
            writer.close();
            return null;
        } catch (Exception e) {
            addMessage(redirectAttributes, "导出分析失败！" + e.getMessage());
            return "redirect:/admin/exam/reportSubjectRange";
        }
    }

    private void addFileToZip(int examId, ZipWriter writer, String subjectName) throws Exception, IOException {
        if (fileService.reportExist(examId, subjectName, "课程分段统计", FormatType.XLSX)) {
            byte[] range = fileService.downloadReport(examId, subjectName, "课程分段统计", FormatType.XLSX);
            writer.write(new ByteArrayInputStream(range), subjectName, "课程分段统计.xlsx");
            byte[] college = fileService.downloadReport(examId, subjectName, "电厂分析", FormatType.XLSX);
            writer.write(new ByteArrayInputStream(college), subjectName, "电厂分析.xlsx");
            byte[] className = fileService.downloadReport(examId, subjectName, "机组分析", FormatType.XLSX);
            writer.write(new ByteArrayInputStream(className), subjectName, "机组分析.xlsx");
            byte[] teacher = fileService.downloadReport(examId, subjectName, "总评统计", FormatType.XLSX);
            writer.write(new ByteArrayInputStream(teacher), subjectName, "总评统计.xlsx");
            byte[] objective = fileService.downloadReport(examId, subjectName, "客观题分析", FormatType.XLSX);
            writer.write(new ByteArrayInputStream(objective), subjectName, "客观题分析.xlsx");
            byte[] subjective = fileService.downloadReport(examId, subjectName, "主观题分析", FormatType.XLSX);
            writer.write(new ByteArrayInputStream(subjective), subjectName, "主观题分析.xlsx");
            byte[] group = fileService.downloadReport(examId, subjectName, "分组统计分析", FormatType.XLSX);
            writer.write(new ByteArrayInputStream(group), subjectName, "分组统计分析.xlsx");
        }
    }

    @Logging(menu = "科目成绩分析计算", type = LogType.UPDATE)
    @RequestMapping("/report")
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.COLLEGE_ADMIN })
    public String calculate(HttpServletRequest request, RedirectAttributes redirectAttributes,
            @RequestParam String subjectCode,@RequestParam(required = false)Double range) {
        int examId = getSessionExamId(request);
        Set<String> subjectSet = new HashSet<String>();
        if (!lockService.isLocked(LockType.SCORE_CALCULATE, examId, subjectCode)) {
            subjectSet.add(subjectCode);
        }
        if (!subjectSet.isEmpty()) {
            Map<Integer, Set<String>> map = new HashMap<Integer, Set<String>>();
            map.put(examId, subjectSet);
            ScoreReportThread thread = new ScoreReportThread(map, reportService, false);
            taskExecutor.submit(thread);
        }
        return "redirect:/admin/exam/reportSubjectRange?subjectCode=" + subjectCode+"&range="+range;
    }

    private JSONArray getScoreRange(String scoreRange, double totalScore, Integer totalCount, double range) {
        JSONArray result = new JSONArray();
        JSONObject jsonObject = JSONObject.fromObject(scoreRange);
        int rangeCount = 0;
        int sumCount = 0;
        int total = (int) Math.ceil(totalScore / range);
        for (int i = total; i >= 0; i--) {
            double start = i * range;
            double end = (i + 1) * range;
            if (start > totalScore) {
                start = Math.ceil(totalScore);
            }
            if (end > totalScore) {
                end = Math.ceil(totalScore);
            }
            if (end < 0) {
                end = 0;
            }
            rangeCount = getSumCount(jsonObject, start, end);
            sumCount = sumCount + rangeCount;
            result.add(getRangeJson(totalCount, rangeCount, sumCount, i * range));
        }
        return result;
    }

    private int getSumCount(JSONObject jsonObject, double start, double end) {
        int sumCount = 0;
        int currentCount = 0;
        double rate = 0.25;
        if (start < end) {
            // for (int i = start; i < end; i++) {
            // currentCount = jsonObject.getInt(String.valueOf(i));
            // sumCount = sumCount + currentCount;
            // }
            double length = (end - start) / rate;
            for (int i = 0; i < length; i++) {
                currentCount = jsonObject.getInt(String.valueOf(start + i * rate));
                sumCount = sumCount + currentCount;
            }
        } else if (start > end) {
            // for (int i = end; i < start; i++) {
            // currentCount = jsonObject.getInt(String.valueOf(i));
            // sumCount = sumCount + currentCount;
            // }
            double length = (start - end) / rate;
            for (int i = 0; i < length; i++) {
                currentCount = jsonObject.getInt(String.valueOf(end + i * rate));
                sumCount = sumCount + currentCount;
            }
        } else {
            sumCount = jsonObject.getInt(String.valueOf(start));
        }
        return sumCount;
    }

    private JSONObject getRangeJson(Integer totalCount, int rangeCount, int sumCount, double score) {
        JSONObject value = new JSONObject();
        value.accumulate("score", score);
        value.accumulate("rangeCount", rangeCount);
        value.accumulate("rangeRate", rangeCount * 100.0 / totalCount);
        value.accumulate("sumCount", sumCount);
        value.accumulate("sumRate", sumCount * 100.0 / totalCount);
        return value;
    }
}
