package com.eactive.eai.batch.eventscheduler.holiday;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;

import org.quartz.impl.calendar.AnnualCalendar;
import org.quartz.impl.calendar.WeeklyCalendar;

import com.eactive.eai.batch.eventscheduler.util.LunarCalendar;
import com.eactive.eai.common.dao.DAOException;
import com.eactive.eai.common.dao.DAOFactory;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.lifecycle.Lifecycle;
import com.eactive.eai.common.lifecycle.LifecycleException;
import com.eactive.eai.common.lifecycle.LifecycleListener;
import com.eactive.eai.common.lifecycle.LifecycleSupport;
import com.eactive.eai.common.util.Logger;

public class HolidayManager implements Lifecycle
{
    static Logger logger = Logger.getLogger(Logger.LOGGER_SCHEDULER);
    private static HolidayManager instance = new HolidayManager();
    private LifecycleSupport lifecycle;
    private boolean started;
    private ArrayList<HolidayVO> holidays;
    
    private AnnualCalendar hcal;	//등록된 휴일, 토, 일
    private AnnualCalendar scal;	//등록된 휴일, 일

    private HolidayManager(){
        lifecycle = new LifecycleSupport(this);
        holidays = new ArrayList<HolidayVO>();        
    }

    public static HolidayManager getInstance(){
        return instance;
    }

    public void start() throws LifecycleException{
        if(started){
            throw new LifecycleException("BECEAIMEH201");
        } else{
            lifecycle.fireLifecycleEvent("starting", this);
            
            DAOFactory daoFactory = DAOFactory.newInstance();
            HolidayDAO dao = null;
            
            try {
            
            	dao = (HolidayDAO)daoFactory.create(HolidayDAO.class);
                holidays = dao.getAllHolidays();
                
            	makeCalendar();

            } catch(DAOException e) {
                throw new LifecycleException(ExceptionUtil.getErrorCode(e,"BECEAIMEH202"));
            } catch (Exception e){
            	throw new LifecycleException(ExceptionUtil.getErrorCode(e,"BECEAIMEH202"));
            }
            started = true;
            lifecycle.fireLifecycleEvent("started", this);
			
            return;
        }
    }

    public void stop()  throws LifecycleException{
        if(!started){
            throw new LifecycleException("BECEAIMEH203");
        } else {
            lifecycle.fireLifecycleEvent("stoping", this);
            holidays = null;
            started = false;
            lifecycle.fireLifecycleEvent("stopped", this);
            return;
        }
    }

    public void addLifecycleListener(LifecycleListener listener){
        lifecycle.addLifecycleListener(listener);
    }

    public LifecycleListener[] findLifecycleListeners(){
        return lifecycle.findLifecycleListeners();
    }

    public void removeLifecycleListener(LifecycleListener listener){
        lifecycle.removeLifecycleListener(listener);
    }
    
    public boolean isStarted(){
        return started;
    }

    public void removeHoliday(HolidayVO vo){
		String rymd = vo.getHoliday();
		String rtype = vo.getHolidayType();

		try{
			for (int i=0; i<holidays.size(); i++){
	    		HolidayVO ivo = (HolidayVO)holidays.get(i);
	    		
	    		String cymd = ivo.getHoliday();
	    		String ctype = ivo.getHolidayType();
	    		
	    		if (rymd.equals(cymd) && rtype.equals(ctype)){
	    			holidays.remove(i);
	    			break;
	    		}
	    	}

			this.makeCalendar();
		}catch (Exception e){
			throw new RuntimeException ("occur RuntimeException at HolidayManager.removeHoliday ");
		}
    }

    public void setHoliday(HolidayVO vo){
    	holidays.add(vo);
    	
    	try{
			this.makeCalendar();
		}catch (Exception e){
			throw new RuntimeException ("occur RuntimeException at HolidayManager.removeHoliday ");
		}
    }

    public ArrayList<HolidayVO> getAllHoliday(){
        return holidays;
    }
    
    public AnnualCalendar getCalendar (){
    	return this.hcal;
    }
    
    public void setCalendar (AnnualCalendar cal){
    	this.hcal = cal;
    }
    
    public boolean isAllHoliday(String day){
    	
    	if (hcal == null){ //휴일 Calendar가 null이면 휴일 없음.
    		return false;
    	}
    	boolean result = false;
    	try {
    		Calendar dcal = createCalendar(day);
    		result = hcal.isDayExcluded(dcal);

    		int dow = dcal.get(Calendar.DAY_OF_WEEK);   		
    		if (result==false){ //등록된 휴일 목록에 없으면 주말휴일에 포함되는지 확인
    			WeeklyCalendar wcal = (WeeklyCalendar)hcal.getBaseCalendar();
        		result = wcal.isDayExcluded(dow);
    			
    		}
    	}catch (Exception e){
    		throw new RuntimeException ("occur RuntimeException at HolidayManager.isAllHoliday - "+day);
    	}
    	return result;
    }
    
    public boolean isHoliday(String day){
    	
    	if (scal == null){ //휴일 Calendar가 null이면 휴일 없음.
    		return false;
    	}
    	boolean result = false;
    	try {
    		Calendar dcal = createCalendar(day);
    		result = scal.isDayExcluded(dcal);

    		int dow = dcal.get(Calendar.DAY_OF_WEEK);   		
    		if (result==false){ //등록된 휴일 목록에 없으면 일요일에 포함되는지 확인
    			WeeklyCalendar wcal = (WeeklyCalendar)scal.getBaseCalendar();
        		result = wcal.isDayExcluded(dow);
    		}
    	}catch (Exception e){
    		logger.error(e.getMessage(), e);
    		throw new RuntimeException ("occur RuntimeException at HolidayManager.isHoliday - "+day + "\n" + e.getMessage());
    	}
    	return result;

    }

    /*
    public String[] getHolidayNm(){
        Iterator it = holidays.keySet().iterator();
        String eaiIns[] = new String[holidays.size()];
        for(int i = 0; it.hasNext(); i++)
            eaiIns[i] = (String)it.next();

        Arrays.sort(eaiIns);
        return eaiIns;
    }
    
    public HolidayVO[] getHolidayVos(){
    	
        Iterator it = holidays.values().iterator();
        HolidayVO vos[] = new HolidayVO[holidays.size()];
        for(int i = 0; it.hasNext(); i++){
            vos[i] = (HolidayVO)it.next();
        }
    	return vos;
    }
*/
    public void makeCalendar() throws Exception{
    	TimeZone tz = TimeZone.getTimeZone("Asia/Seoul");
		WeeklyCalendar weeklys = new WeeklyCalendar(tz);
		weeklys.setDayExcluded(Calendar.SUNDAY, true);	//일요일
		weeklys.setDayExcluded(Calendar.SATURDAY, true);	//토요일
		
		
		WeeklyCalendar sunweek = new WeeklyCalendar(tz);
		sunweek.setDayExcluded(Calendar.SUNDAY, true);	//일요일

		AnnualCalendar aholidays = new AnnualCalendar(weeklys);
		scal = new AnnualCalendar(sunweek);

		Calendar cal = null;
		Calendar c = new GregorianCalendar();
		int year = c.get(Calendar.YEAR);
		  
		if (holidays.size()>0){
			for (int i=0; i<holidays.size(); i++){
				HolidayVO vo = (HolidayVO)holidays.get(i);
				String days = vo.getHoliday();
				String type = vo.getHolidayType();

				try{
					if (HolidayKeys.NATIONAL_HOLIDAY_TYPE.equals(type)){//명절
						
						String lunar = "";
						String day = days.substring(4,8);
						
						if ("1230".equals(day)){
							lunar = (year-1)+day;	//음력 12월 30일은 전년도로 계산해야 함.
						}
						else {
							lunar = year+day;
						}
						String toSolar = LunarCalendar.toSolarOfNationalHoliday(lunar);
						cal = createCalendar(toSolar);
						aholidays.setDayExcluded(cal, true); //토,일을 포함한 전체 휴일
						scal.setDayExcluded(cal, true); //일요일을 포함한  휴일
					}
					else {
						
						if (HolidayKeys.LEGAL_HOLIDAY_TYPE.equals(type)){//공휴일
							String calDays = year+days.substring(4,8);
							cal = createCalendar(calDays);
							
						}else {//임시휴일
							int tyear = Integer.parseInt(days.substring(0, 4));
							
							if (year <= tyear){ //임시휴일의 년도가 현재 년도 이후이면 셋팅
								cal = createCalendar(days);
							}
						}
						aholidays.setDayExcluded(cal, true);//토,일을 포함한 전체 휴일
						scal.setDayExcluded(cal, true);//일요일을 포함한 전체 휴일
					}
					
				}catch (Exception e){
					logger.error("HolidayManager ] makeCalendar - Holiday set failed - "+e.getMessage(),e);
					throw new Exception(ExceptionUtil.getErrorCode(e,"BECEAIMEH204"));
					
				}
			}
		}
		
		setCalendar(aholidays);
    }
    
    private Calendar createCalendar(String days) throws Exception{
    	Calendar cal = null;
    	
    	try{
	    	int year = Integer.parseInt(days.substring(0, 4));
	    	int month = Integer.parseInt(days.substring(4,6));
			int day = Integer.parseInt(days.substring(6,8));
			
			cal = new GregorianCalendar(year, month-1, day);
			
			return cal;
    	} catch (Exception e){
    		logger.error("HolidayManager ] createCalendar - createCalendar failed - "+e.getMessage(),e);
    		String arg[] = new String[1];
    		arg[0] = days;
    		
			throw new Exception(ExceptionUtil.getErrorCode(e, "BECEAIMEH205", arg));
				
    	}
		
    }

}
