package com.topie.workcal.service;

import java.text.SimpleDateFormat;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import javax.xml.datatype.Duration;

import com.topie.api.tenant.TenantConnector;
import com.topie.api.tenant.TenantDTO;
import com.topie.api.workcal.WorkCalendarConnector;

import com.topie.workcal.persistence.domain.WorkcalPart;
import com.topie.workcal.persistence.domain.WorkcalRule;
import com.topie.workcal.persistence.manager.WorkcalPartManager;
import com.topie.workcal.persistence.manager.WorkcalRuleManager;
import com.topie.workcal.support.DayPart;
import com.topie.workcal.support.Holiday;
import com.topie.workcal.support.WorkCalendar;
import com.topie.workcal.support.WorkDay;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WorkCalendarService implements WorkCalendarConnector {
    private static Logger logger = LoggerFactory
            .getLogger(WorkCalendarService.class);
    public static final int STATUS_WEEK = 0;
    public static final int STATUS_HOLIDAY = 1;
    public static final int STATUS_HOLIDAY_TO_WORKDAY = 2;
    public static final int STATUS_WORKDAY_TO_HOLIDAY = 3;
    private WorkcalRuleManager workcalRuleManager;
    private WorkcalPartManager workcalPartManager;
    private String hourFormatText = "HH:mm";
    private boolean enabled = true;
    private Map<String, WorkCalendar> map = new HashMap<String, WorkCalendar>();
    private TenantConnector tenantConnector;

    public Date processDate(Date date, String tenantId) {
        WorkCalendar workCalendar = map.get(tenantId);

        return workCalendar.findWorkDate(date);
    }

    public Date add(Date date, Duration duration, String tenantId) {
        WorkCalendar workCalendar = map.get(tenantId);

        return workCalendar.add(date, duration);
    }

    public void processWeek(WorkCalendar workCalendar, String tenantId)
            throws Exception {
        List<WorkDay> days = new ArrayList<WorkDay>(8);
        days.add(new Holiday(workCalendar));
        days.add(new Holiday(workCalendar));
        days.add(new Holiday(workCalendar));
        days.add(new Holiday(workCalendar));
        days.add(new Holiday(workCalendar));
        days.add(new Holiday(workCalendar));
        days.add(new Holiday(workCalendar));
        days.add(new Holiday(workCalendar));

        // 每周的工作规则
        List<WorkcalRule> workcalRules = workcalRuleManager.find(
                "from WorkcalRule where status=? and tenantId=?", STATUS_WEEK,
                tenantId);

        for (WorkcalRule workcalRule : workcalRules) {
            WorkDay day = new WorkDay(workCalendar);
            int dayPartIndex = 0;
            List<DayPart> dayParts = day.getDayParts();

            for (WorkcalPart workcalPart : workcalPartManager.findBy(
                    "workcalRule", workcalRule)) {
                DayPart dayPart = new DayPart();
                dayPart.setWorkDay(day);
                dayPart.setIndex(dayPartIndex);

                Date startDate = new SimpleDateFormat(hourFormatText)
                        .parse(workcalPart.getStartTime());
                Date endDate = new SimpleDateFormat(hourFormatText)
                        .parse(workcalPart.getEndTime());
                Calendar startCalendar = Calendar.getInstance();
                startCalendar.setTime(startDate);

                Calendar endCalendar = Calendar.getInstance();
                endCalendar.setTime(endDate);
                dayPart.setFromHour(startCalendar.get(Calendar.HOUR));
                dayPart.setFromMinute(startCalendar.get(Calendar.MINUTE));
                dayPart.setToHour(endCalendar.get(Calendar.HOUR));
                dayPart.setToMinute(endCalendar.get(Calendar.MINUTE));
                dayParts.add(dayPart);
            }

            days.set(workcalRule.getWeek(), day);
        }
    }

    public void processHoliday(WorkCalendar workCalendar,
            WorkcalRule workcalRule) throws Exception {
        Date date = workcalRule.getWorkDate();
        Holiday holiday = new Holiday(workCalendar);
        holiday.setDate(date);
        workCalendar.addHoliday(holiday);
    }

    public void processWorkDay(WorkCalendar workCalendar,
            WorkcalRule workcalRule) throws Exception {
        Date date = workcalRule.getWorkDate();
        WorkDay workDay = new WorkDay(workCalendar);
        workDay.setDate(date);

        int dayPartIndex = 0;
        List<DayPart> dayParts = workDay.getDayParts();

        for (WorkcalPart workcalPart : workcalPartManager.findBy("workcalRule",
                workcalRule)) {
            DayPart dayPart = new DayPart();
            dayPart.setWorkDay(workDay);
            dayPart.setIndex(dayPartIndex);

            Date startDate = new SimpleDateFormat(hourFormatText)
                    .parse(workcalPart.getStartTime());
            Date endDate = new SimpleDateFormat(hourFormatText)
                    .parse(workcalPart.getEndTime());
            Calendar startCalendar = Calendar.getInstance();
            startCalendar.setTime(startDate);

            Calendar endCalendar = Calendar.getInstance();
            endCalendar.setTime(endDate);
            dayPart.setFromHour(startCalendar.get(Calendar.HOUR));
            dayPart.setFromMinute(startCalendar.get(Calendar.MINUTE));
            dayPart.setToHour(endCalendar.get(Calendar.HOUR));
            dayPart.setToMinute(endCalendar.get(Calendar.MINUTE));
            dayParts.add(dayPart);
        }

        workCalendar.addWorkDay(workDay);
    }

    @PostConstruct
    public void init() throws Exception {
        if (!enabled) {
            logger.info("skip work calendar");

            return;
        }

        for (TenantDTO tenantDto : tenantConnector.findAll()) {
            String tenantId = tenantDto.getId();
            WorkCalendar workCalendar = new WorkCalendar();
            map.put(tenantId, workCalendar);
            this.processWeek(workCalendar, tenantId);

            // 特殊日期
            List<WorkcalRule> extraWorkcalRules = workcalRuleManager.find(
                    "from WorkcalRule where status<>? and tenantId=?",
                    STATUS_WEEK, tenantId);

            for (WorkcalRule workcalRule : extraWorkcalRules) {
                if (workcalRule.getStatus() == STATUS_HOLIDAY) {
                    this.processHoliday(workCalendar, workcalRule);
                } else if (workcalRule.getStatus() == STATUS_HOLIDAY_TO_WORKDAY) {
                    this.processWorkDay(workCalendar, workcalRule);
                } else {
                    this.processHoliday(workCalendar, workcalRule);
                }
            }
        }
    }

    @Resource
    public void setWorkcalRuleManager(WorkcalRuleManager workcalRuleManager) {
        this.workcalRuleManager = workcalRuleManager;
    }

    @Resource
    public void setWorkcalPartManager(WorkcalPartManager workcalPartManager) {
        this.workcalPartManager = workcalPartManager;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    @Resource
    public void setTenantConnector(TenantConnector tenantConnector) {
        this.tenantConnector = tenantConnector;
    }
}
