package lu.tudor.santec.gecamed.billing.utils.rules;

import java.util.Collection;
import java.util.HashSet;

import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Act;

/**
 * @author jens.ferring(at)tudor.lu
 * 
 * @version
 * <br>$Log: DefaultChangeActCondition.java,v $
 * <br>Revision 1.4  2013-03-11 12:38:21  ferring
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.3  2013-03-11 09:26:36  ferring
 * <br>comments added
 * <br>
 * <br>Revision 1.2  2013-03-11 07:48:12  ferring
 * <br>Fixed: Options have been implemented wrong
 * <br>
 * <br>Revision 1.1  2013-02-27 08:04:29  ferring
 * <br>Rule system "outsourced" to helper classes, that can be converted to drools functions, if needed
 * <br>
 */
public class DateChangeCondition implements ActChangeCondition
{
	/* ======================================== */
	// 		CONSTANTS
	/* ======================================== */
	
	
	/**
	 * All Saturday after 12 o'clock (including 12 o'clock)
	 */
	public static final int	SATURDAY_AFTERNOON	= 1<<0;
	
	/**
	 * All Sunday
	 */
	public static final int SUNDAY				= 1<<1;
	
	/**
	 * Every legal holiday entered in the DB table core.national_holiday
	 */
	public static final int HOLIDAY				= 1<<2;
	
	/**
	 * Every day from (including) 20 to (excluding) 22 o'clock
	 */
	public static final int EVENING				= 1<<3;
	
	/**
	 * Every day from (including) 22 to (excluding) 7 o'clock at the next day
	 */
	public static final int LATE_NIGHT			= 1<<4;
	
	/**
	 * Combines evening and late night. Means every day from (including) 20 o'clock to 
	 * (excluding) 7 o'clock the next day
	 */
	public static final int NIGHT				= EVENING | LATE_NIGHT;
	
	/**
	 * Combines the Sunday and the holiday option.
	 */
	public static final int SUN_AND_HOLIDAY		= SUNDAY | HOLIDAY;
	
	/**
	 * Combines all options, except Saturday afternoon (SUNDAY, HOLIDAY, EVENING, LATE_NIGH).
	 */
	public static final int SUNDAY_HOLIDAY_NIGHT= SUN_AND_HOLIDAY | NIGHT;
	
	
	
	
	/* ======================================== */
	// 		MEMBERS
	/* ======================================== */
	
//	private Set<String>	originals;
	
	private String	saturdayAfternoon;
	
	private String	sunday;
	
	private String	holiday;
	
	private String	evening;
	
	private String	night;
	
	private String	original;
	
	private String	defaultCode;
	
	private Collection<String>	differentCodes	= new HashSet<String>();
	
	private HashSet<String> specialCodes		= new HashSet<String>();

	private int eveningStart = 20;
	
	
	
	/* ======================================== */
	// 		CONSTRUCTORS
	/* ======================================== */
	
	public DateChangeCondition(String originalCode, String saturdayAfternoon, String sunAndHoliday, 
			String evening, String night)
	{
		this.saturdayAfternoon	= saturdayAfternoon;
		this.sunday				= sunAndHoliday;
		this.holiday			= sunAndHoliday;
		this.evening			= evening;
		this.night				= night;
		
		init(originalCode);
	}
	
	
	public DateChangeCondition(int eveningStart, Iterable<String> originals, String saturdayAfternoon, String sunAndHoliday, 
			String evening, String night)
	{
		this.eveningStart 		= eveningStart;
		this.saturdayAfternoon	= saturdayAfternoon;
		this.sunday				= sunAndHoliday;
		this.holiday			= sunAndHoliday;
		this.evening			= evening;
		this.night				= night;
		
		init(originals);
	}
	
	
	public DateChangeCondition(String[] originals, String saturdayAfternoon, String sunAndHoliday, 
			String evening, String night)
	{
		this.saturdayAfternoon	= saturdayAfternoon;
		this.sunday				= sunAndHoliday;
		this.holiday			= sunAndHoliday;
		this.evening			= evening;
		this.night				= night;
		
		init(originals);
	}
	
	
	public DateChangeCondition (String originalCode, String changedCode, int option)
	{
		if ((option & SATURDAY_AFTERNOON)	!= 0)
			this.saturdayAfternoon	= changedCode;
		if ((option & SUNDAY)	!= 0)
			this.sunday				= changedCode;
		if ((option & HOLIDAY)	!= 0)
			this.holiday			= changedCode;
		if ((option & EVENING)	!= 0)
			this.evening			= changedCode;
		if ((option & LATE_NIGHT)	!= 0)
			this.night				= changedCode;
		
		init(originalCode);
	}
	
	
	
	/* ======================================== */
	// 		CLASS BODY
	/* ======================================== */
	
	public void setDefaultCode (String defaultCode)
	{
		this.defaultCode = defaultCode;
	}
	
	
	public String getCodeToChangeTo (RulesObjectsHolder roh, Act act)
	{
		String	changedCode;
		Integer	performedHour	= act.getPerformedHour();
		
		
		if (night	!= null 
				&& (performedHour >= 22 
				||  performedHour < 7))
		{
			// act was performed at night time
			changedCode	= night;
		}
		else if (sunday != null && act.performedOnSunday())
		{
			// act was performed on a Sunday
			changedCode	= sunday;
		}
		else if (holiday != null && act.performedOnHoliday())
		{
			// act was performed on a holiday
			changedCode	= holiday;
		}
		else if (evening != null 
				&& performedHour >= this.eveningStart) 
//				&& performedHour < 22) // this is already excluded
		{
			// act was performed at evening time 
			changedCode	= evening;
		}
		else if (saturdayAfternoon != null 
				&& act.performedOnSaturday()
				&& performedHour >= 12)
		{
			// act was performed on a Saturday
			changedCode	= saturdayAfternoon;
		}
		else if (specialCodes.contains(act.getCode()))
		{
			/*    none of the other cases fits 
			 * && its a special code
			 */
//			if (original == null)
//				// TODO: Warn the user, because this act code is not allowed on that day or time
//				act.addRuleWarning();
			
			changedCode	= defaultCode;
		}
		else
		{
			changedCode	= null;
		}
		
		
		return changedCode;
	}


	public Collection<String> getCodesToChange ()
	{
		return differentCodes;
	}
	
	
	
	/* ======================================== */
	// 		HELP METHODS
	/* ======================================== */
	
	private void init  (String original)
	{
		differentCodes.add(original);
		init();
	}
	
	
	private void init  (Iterable<String> originals)
	{
		for (String code : originals)
			differentCodes.add(code);
		init();
	}
	
	
	private void init  (String[] originals)
	{
		for (String code : originals)
			differentCodes.add(code);
		init();
	}
	
	
	private void init ()
	{
		specialCodes.add(this.sunday);
		specialCodes.add(this.holiday);
		specialCodes.add(this.saturdayAfternoon);
		specialCodes.add(this.evening);
		specialCodes.add(this.night);
		differentCodes.addAll(specialCodes);
		
		this.original	= null;
		for (String code : differentCodes)
		{
			if (specialCodes.contains(code))
				continue;
			
			if (original != null)
			{
				// more than one original found
				original = null;
				break;
			}
			else
			{
				original = code;
			}
		}
		/* if original is null, no original or more than one was found. 
		 * In any case, a clear rematch to the original code is not possible.
		 */
	}
}
