package com.k_int.discover.util;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashSet;

public class DateRange
{

  private Date startDate;
  private Date endDate;
  private String outputFormatWithEra = "yyyy-MM-dd'T'HH:mm:ss'Z' G";
  private String outputFormatWithoutEra = "yyyy-MM-dd'T'HH:mm:ss'Z'";
  private String startEra = "AD";
  private String endEra = "AD";
  private String text = "";
  private SimpleDateFormat sdf;
  private SimpleDateFormat sdfWithEra;
  
  public static final String GRANULARITY_CENTURY = "Century";
  public static final String GRANULARITY_DECADE = "Decade";
  public static final String GRANULARITY_YEAR = "Year";
  public static final String GRANULARITY_MONTH = "Month";
  public static final String GRANULARITY_DAY = "Day";
  public static ArrayList<String> granularities = new ArrayList<String>();
  
  static {
	granularities.add(GRANULARITY_CENTURY);
    granularities.add(GRANULARITY_DECADE);
    granularities.add(GRANULARITY_YEAR);
    granularities.add(GRANULARITY_MONTH);
    granularities.add(GRANULARITY_DAY);
  }
  
  public DateRange()
  {
    this.sdf = new SimpleDateFormat(outputFormatWithoutEra);
    this.sdfWithEra = new SimpleDateFormat(outputFormatWithEra);
  }

  public DateRange(Date startDate, Date endDate)
  {
    this.startDate = startDate;
    this.endDate = endDate;
    this.sdf = new SimpleDateFormat(outputFormatWithoutEra);
    this.sdfWithEra = new SimpleDateFormat(outputFormatWithEra);
  }

  public String getGranularity()
  {
    if (getStartDate() == null || getEndDate() == null)
      return GRANULARITY_DECADE;
    Calendar start = new GregorianCalendar();
    start.setTime(getStartDate());
    Calendar end = new GregorianCalendar();
    end.setTime(getEndDate());
    if (start.get(Calendar.YEAR) == end.get(Calendar.YEAR) && start.get(Calendar.MONTH) == end.get(Calendar.MONTH) && start.get(Calendar.DAY_OF_MONTH) == end.get(Calendar.DAY_OF_MONTH))
      return GRANULARITY_DAY;
    else if (start.get(Calendar.YEAR) == end.get(Calendar.YEAR) && start.get(Calendar.MONTH) == end.get(Calendar.MONTH))
      return GRANULARITY_MONTH;
    else if (start.get(Calendar.YEAR) == end.get(Calendar.YEAR))
      return GRANULARITY_YEAR;
    else if ( end.get(Calendar.YEAR) - start.get(Calendar.YEAR) <= 100 )
      return GRANULARITY_DECADE;
    else 
      return GRANULARITY_CENTURY;
  }
  
  /** Returns true if second is more precise then the first
   * 
   * @param first
   * @param second
   * @return
   */
  public static boolean morePrecise(DateRange first, DateRange second, boolean allowEqual)
  {
    if (first == null || second == null || first.getStartDate() == null  || second.getStartDate() == null)
      return false;
    String granularityF = first.getGranularity();
    String granularityS = second.getGranularity();
    int grS  = granularities.indexOf(granularityS);
    int grF  = granularities.indexOf(granularityF);
    if ((grS > grF) || (allowEqual && grS == grF))
    {
      Calendar calF = new GregorianCalendar();
      calF.setTime(first.getStartDate());
      Calendar calS = new GregorianCalendar();
      calS.setTime(second.getStartDate());
      if ( granularityF == GRANULARITY_CENTURY && first.getDecade().containsAll(second.getDecade()) ) 
    	return true;
      else if (granularityF == GRANULARITY_DECADE &&  first.getDecade().containsAll(second.getDecade()))
        return true;
      else if (granularityF == GRANULARITY_YEAR &&  calF.get(Calendar.YEAR)== calS.get(Calendar.YEAR))
        return true;
      else if (granularityF == GRANULARITY_MONTH &&  calF.get(Calendar.MONTH)== calS.get(Calendar.MONTH) &&  calF.get(Calendar.YEAR)== calS.get(Calendar.YEAR))
        return true;
      else if (allowEqual && granularityF == GRANULARITY_DAY &&  calF.get(Calendar.MONTH)== calS.get(Calendar.MONTH) &&  calF.get(Calendar.YEAR)== calS.get(Calendar.YEAR) &&  calF.get(Calendar.DAY_OF_MONTH)== calS.get(Calendar.DAY_OF_MONTH))
        return true;
    }  
    return false;
  }
  
  public Date getStartDate()
  {
    return startDate;
  }

  public void setStartDate(Date startDate)
  {
    this.startDate = startDate;
  }

  public Date getEndDate()
  {
    return endDate;
  }

  public void setEndDate(Date endDate)
  {
    this.endDate = endDate;
  }

  public HashSet<String> getDecade()
  {
    HashSet<String> decade = new HashSet<String>();
    Long startDecade = findDecade(startDate, startEra);
    Long endDecade = findDecade(endDate, endEra);
    if (startDecade != null && endDecade != null)
    {
        // If we have a date range of over 1000 years then only return the start and end decades
        Long range = 0l;
        if ( endDecade > startDecade) {
            range = endDecade - startDecade;

        } else {
            range = startDecade - endDecade;
        }

        if ( range > 1000 ) {
            decade.add(startDecade + "0");
            decade.add(endDecade + "0");
        } else {
            // Manageable range - add all decades
          for (Long i = startDecade; i <= endDecade; i++)
            decade.add(i + "0");
        }
    }
    return decade;
  }

  private Long findDecade(Date date, String era)
  {
    if (date == null)
      return null;
    Calendar cal = new GregorianCalendar();
    cal.setTime(date);
    Long decadeNum = new Long(cal.get(Calendar.YEAR)/10);
    
    // Take care of converting BC decades into negative numbers
    if ( "BC".equals(era) ) 
    	decadeNum = -decadeNum;
    
    
    return decadeNum;
  }

  public String getFormatedStartDate(boolean withEra)
  {
    String result = null;
    if (startDate != null)
    {
    	if ( withEra ) {
    		result = sdfWithEra.format(startDate);
    	} else {
    		result = sdf.format(startDate);
    	}
    }
    return result;
  }
  
  
  public String getFormatedEndDate(boolean withEra)
  {
    String result = null;
    if (endDate != null)
    {
    	if ( withEra ) {
    		result = sdfWithEra.format(endDate);
    	} else {
    		result = sdf.format(endDate);
    	}
    }
    return result;
  }

  public String getOutputFormatWithEra()
  {
    return outputFormatWithEra;
  }
  
  public void setOutputFormatWithEra(String outputFormatWithEra)
  {
    this.outputFormatWithEra = outputFormatWithEra;
    sdfWithEra = new SimpleDateFormat(outputFormatWithEra);
  }

  public String getOutputFormatWithoutEra()
  {
    return outputFormatWithoutEra;
  }
  
  public void setOutputFormatWithoutEra(String outputFormatWithoutEra)
  {
    this.outputFormatWithoutEra = outputFormatWithoutEra;
    sdf = new SimpleDateFormat(outputFormatWithoutEra);
  }
  
  public String getStartEra()
  {
	  return startEra;
  }
  
  public void setStartEra(String startEra)
  {
	  this.startEra = startEra;
  }
  
  public String getEndEra()
  {
	  return endEra;
  }
  
  public void setEndEra(String endEra)
  {
	  this.endEra = endEra;
  }

  public String getText()
  {
    return text;
  }

  public void setText(String text)
  {
    this.text = text;
  }
}