package com.eactive.eai.batch.scheduler;

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

import com.eactive.eai.batch.eventscheduler.holiday.HolidayManager;
import com.eactive.eai.common.util.Logger;

public class ScheduleBasisManager {

	
	//singleton 객체 생성
	private static ScheduleBasisManager instance = new ScheduleBasisManager();
	//singleton 객체 리턴
    public static ScheduleBasisManager getInstance() {
        return instance;
    }

	//생성자 (다른 클래스에서 new 불가)
	private ScheduleBasisManager() {}

    static Logger logger = Logger.getLogger(Logger.LOGGER_SCHEDULER);

	public ArrayList<String> getIntervalTypes( String days ){
		ArrayList<String> al = new ArrayList<String>();
    	Calendar cal = null;
    	int i_year, i_month, i_day;
    	try{
	    	i_year = Integer.parseInt(days.substring(0, 4));
	    	i_month = Integer.parseInt(days.substring(4,6));
			i_day = Integer.parseInt(days.substring(6,8));
			cal = new GregorianCalendar(i_year, i_month-1, i_day);
    	} catch (Exception e){
    		logger.error("[ScheduleBasisManager] getIntervalTypes - createCalendar failed - "+e.getMessage());
    		return al;	
    	}
    	
    	if ( ! checkDate(days) )
    		return al;

		// 1. 매일 처리하는 경우 추가 
		//(D)
		al.add( "D" );
		
		HolidayManager hm = HolidayManager.getInstance(); 
		// 2. 매 영업일에 처리 하는 경우 추가 
		//(Y)
		boolean isHoliday = true;
		if ( !hm.isAllHoliday(days) ){
			al.add( "Y" );
			isHoliday = false;
		}
		// 3. 매 영업 익일에 처리 하는 경우 추가
		//(Y+)
		String yesterday = getDaysOfDay( cal, -1);
		if ( !hm.isAllHoliday(yesterday) )
			al.add( "Y+" );
		
		// 4. 매 영업일 + 순수 토요일에 처리 하는 경우 추가
		//(S)
		if ( !hm.isHoliday(days) )
			al.add( "S" );
		
		String sMonth = days.substring( 4,6 );
		String sDay   = days.substring( 6,8 );

		// 5. 특정 또는 매월(00) 특정일에 처리 하는 경우
		// 5-1. 휴일 포함
		al.add( "00" + sDay );
		al.add( sMonth + sDay );

		// 특정 또는 매월(00) 특정일에 처리 하는 경우(휴일 포함)
		if ( isLastDay(cal) ){
			al.add( "00-1" );
			al.add( sMonth + "-1" );
		}

		int inx = 1;
		if ( !isHoliday ){
			// 5-2. 전 영업일
			// 내일부터 휴일인 동안 추가
			while( hm.isAllHoliday(getDaysOfDay( cal, inx )) ){
				String tmpDay = getDaysOfDay( cal, inx );
				//날짜를 뒤로 돌리다 보면 다음달로 넘어 가는 경우가 있어 별도의 변수를 사용한다.
				String tMonth = tmpDay.substring( 4,6 );
				sDay   = tmpDay.substring( 6,8 );
				al.add( "00" + sDay + "-" );
				al.add( tMonth + sDay + "-" );
				inx++;
			}
			// 5-3. 후 영업일
			inx = 1;
			while( hm.isAllHoliday(getDaysOfDay( cal, 0 - inx )) ){
				String tmpDay = getDaysOfDay( cal, 0 - inx );
				sMonth = tmpDay.substring( 4,6 );
				sDay   = tmpDay.substring( 6,8 );
				al.add( "00" + sDay + "+" );
				al.add( sMonth + sDay + "+" );
				inx++;
			}
		}

		// 6. 특정 또는 매월(00) n 번째 영업일
		if ( !isHoliday ){
			// 영업일 수
			int yCnt = 1;
			for ( inx = 1; inx < i_day; inx++){
				if ( !hm.isAllHoliday(getDaysOfDay( cal, 0 - inx )))
					yCnt++;
			}
			String s_holiCnt;
			if ( yCnt < 10 ){
				s_holiCnt = "0" + yCnt;
			}else{
				s_holiCnt = "" + yCnt;
			}
			al.add( "Y00" + s_holiCnt );
			al.add( "Y" + sMonth + s_holiCnt );
			
			// 마지막 영업일인지 여부 체크 - 휴일이 아닌경우에만 체크
			// 일단 마지막 영업일이라고 가정하고
			boolean isLastYday = true;
			Calendar tmpCal = new GregorianCalendar(cal.get ( Calendar.YEAR ), cal.get ( Calendar.MONTH ), cal.get ( Calendar.DAY_OF_MONTH ));
			tmpCal.add( Calendar.DAY_OF_MONTH, 1);
			while ((i_month -1) == tmpCal.get(Calendar.MONTH)){
				if ( !hm.isAllHoliday(getDaysOfDay( tmpCal, 1)) ){
					isLastYday = false;
					break;
				}
				tmpCal.add( Calendar.DAY_OF_MONTH, 1);
			}
			if ( isLastYday ){
				al.add( "Y00-1" );
				al.add( "Y" + sMonth + "-1" );
			}
		}
		
		// 7. 특정 요일( W1 )
		
		// 요일을 추출( 원래는 일요일  - 1 .. 토요일 7이지만  일괄전송에서는 일요일 0 .. 토요일 6으로 함.
		// 그렇게 하는 이유는 TANDEM에서 그렇게 했었기 때문에
		int i_date = cal.get(Calendar.DAY_OF_WEEK) - 1;
		al.add( "D" + ( i_date ));
		
		// 몇번째 주인가를 확인
		int i_week = cal.get( Calendar.WEEK_OF_MONTH );
		
		int yCnt = 0;

		boolean isLastYdayOfWeek = false;
		// 오늘이 영업일이면 이번주의 몇 번째 영업일인지를 찾아 낸다.
		if ( !isHoliday ){
			for ( inx=i_date -1; inx>=0; inx-- ){
				if ( !hm.isAllHoliday(getDaysOfDay( cal, 0 - inx)) ){
					yCnt++;
				}
			}
			// 이번주의 마지막 영업일인지 여부 체크 - 휴일이 아닌경우에만 체크
			// 일단 마지막 영업일이라고 가정하고
			isLastYdayOfWeek = true;
			Calendar tmpCal = new GregorianCalendar(cal.get ( Calendar.YEAR ), cal.get ( Calendar.MONTH ), cal.get ( Calendar.DAY_OF_MONTH ));
			// 금요일까지만 체크
			while (tmpCal.get(Calendar.DAY_OF_WEEK) < 7 ){
				if ( !hm.isAllHoliday(getDaysOfDay( tmpCal, 1)) ){
					isLastYdayOfWeek = false;
					break;
				}
				tmpCal.add( Calendar.DAY_OF_MONTH, 1);
			}
			
		}
		// 8. 매주 n번째 영업일
		if ( yCnt > 0 ){
			al.add( "DY" + yCnt );
		}
		// 9. n 주 특정 요일
		al.add( "W" + i_week + "D" + i_date );
		
		// 10. n 주 m 번째 영업일
		if ( yCnt > 0 ){
			al.add( "W" + i_week + "Y" + yCnt );
		}
		//
		if ( isLastYdayOfWeek ){
			al.add( "DY-1" );
			al.add( "W" + i_week + "Y-1" );
		}
		Collections.sort( al );
		
		return al;
	}

	private String getDaysOfDay( Calendar cal, int days )
    {
    	StringBuffer sbDate = new StringBuffer ( );
    	Calendar tmpCal = new GregorianCalendar(cal.get ( Calendar.YEAR ), cal.get ( Calendar.MONTH ), cal.get ( Calendar.DAY_OF_MONTH ));
    	tmpCal.add ( Calendar.DAY_OF_MONTH, days );
    	int nYear = tmpCal.get ( Calendar.YEAR );
    	int nMonth = tmpCal.get ( Calendar.MONTH ) + 1;
    	int nDay = tmpCal.get ( Calendar.DAY_OF_MONTH );
    	
    	sbDate.append ( nYear );
    	if ( nMonth < 10 )
    		sbDate.append ( "0" );
    	sbDate.append ( nMonth );
    	if ( nDay < 10 )
    		sbDate.append ( "0" );
    	sbDate.append ( nDay );

    	return sbDate.toString ( );
    }
	
	private boolean isLastDay( Calendar cal ){
    	Calendar tmpCal = new GregorianCalendar(cal.get ( Calendar.YEAR ), cal.get ( Calendar.MONTH ), cal.get ( Calendar.DAY_OF_MONTH ));
    	tmpCal.add ( Calendar.DAY_OF_MONTH, 1 );
    	return ( cal.get ( Calendar.MONTH ) != tmpCal.get ( Calendar.MONTH ) );
	}

	private boolean checkDate(String sDate) {
		int[] days = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

		if ( sDate.length() != 8 )
			return false;
		try {
			int year  = new Integer(sDate.substring(0, 4)).intValue();
			int month = new Integer(sDate.substring(4, 6)).intValue();
			int day   = new Integer(sDate.substring(6, 8)).intValue();
			
			
			if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
				days[2] = 29;
			} // end of if
			else {
				days[2] = 28;
			} // end of else
			
			if(month < 1 || month > 12) {
				return false;
			}
			
			if(day < 1 || day > days[month]) {
				return false;
			}	
		}
		catch(Exception e) {
			return false;
		}
		
		return true;
	}
	
	
}
