using System;
using System.Collections;
using System.Collections.Generic;
using MarketData.Utils;

// Filename: DateGenerator.cs
// Author:Sean Kessler

namespace MarketData.Utils
{
/// <summary>DateGenerator - Generate Historical Dates</summary>
  [Serializable]
  public class DateGenerator
  {
    private Hashtable holidays=null;
    public  DateGenerator()
    {
    }
    public DateTime GetPrevMonthStart(DateTime dateTime)
    {
      DateTime startDate = new DateTime(dateTime.Year, dateTime.Month, 1);
      startDate = startDate.AddMonths(-1);
      startDate = GetNextBusinessDay(startDate);
      return startDate;
    }
    public DateTime GetCurrMonthStart(DateTime dateTime)
    {
      DateTime startDate = new DateTime(dateTime.Year, dateTime.Month, 1);
      startDate = GetNextBusinessDay(startDate);
      return startDate;
    }
    public DateGenerator(Hashtable holidays)
    {
      this.holidays=holidays;
    }
    public static int GetPrevMonth(int month)
    {
      if(1==month)return 12;
      return month-1;
    }
/// <summary>FindPrevBusinessDay - Finds previous business day</summary>
/// <returns>DateTime</returns>   
    public DateTime FindPrevBusinessDay(DateTime asOf)
    {
	    TimeSpan oneDay=new TimeSpan(1,0,0,0);
	    asOf=asOf.Subtract(oneDay);
	    while(IsWeekend(asOf)||IsHoliday(asOf))asOf=asOf.Subtract(oneDay);
	    return asOf;
    }
    /// <summary>FindNextBusinessDays - Finds following business day</summary>
    /// <returns>DateTime</returns>   
    public DateTime FindForwardBusinessDay(DateTime asOf,int daysForward)
    {
      for (int index = 0; index < daysForward; index++)
      {
        asOf = FindNextBusinessDay(asOf);
      }
      return asOf;
    }
/// <summary>FindNextBusinessDay - Finds following business day</summary>
/// <returns>DateTime</returns>   
    public DateTime FindNextBusinessDay(DateTime asOf)
    {
	    TimeSpan oneDay=new TimeSpan(1,0,0,0);
	    asOf=asOf.Add(oneDay);
	    while(IsWeekend(asOf)||IsHoliday(asOf))asOf=asOf.Add(oneDay);
	    return asOf;
    }
/// <summary>GetPrevBusinessDay - Gets previous business day</summary>
/// <note>If the given date is a business day then this method will return the given date</note>
/// <returns>None</returns>   
    public DateTime GetPrevBusinessDay(DateTime asOf)
	  {
	    TimeSpan oneDay=new TimeSpan(1,0,0,0);
	    while(IsWeekend(asOf)||IsHoliday(asOf))asOf=asOf.Subtract(oneDay);
	    return asOf;
	  }
/// <summary>GetNextBusinessDay - Gets next business day</summary>
/// <note>If the given date is a business day then this method will return the given date</note>
/// <returns>None</returns>   
	  public DateTime GetNextBusinessDay(DateTime asOf)
	  {
	    TimeSpan oneDay=new TimeSpan(1,0,0,0);
	    while(IsWeekend(asOf)||IsHoliday(asOf))asOf=asOf.Add(oneDay);
	    return asOf;
	  }
/// <summary>GetNextDay - Gets next business day</summary>
/// <note>If the given date is a business day then this method will return the given date</note>
/// <returns>None</returns>   
	  public DateTime GetNextDay(DateTime asOf)
	  {
      return asOf.Add(Utility.OneDay);
	  }
/// <summary>GetPrevFriday - Gets date of prior friday</summary>
/// <note>Get the date of the previous friday - if previous Friday is holiday will seek previous business day</note>
/// <returns>None</returns>   
	  public DateTime GetPrevFriday(DateTime asOf)
	  {
	    TimeSpan oneDay=new TimeSpan(1,0,0,0);
	    List<DateTime> historicalDates=null;
	    int daysToFetch=7;
	    
	    if(DayOfWeek.Friday==asOf.DayOfWeek)
	    {
	      asOf=asOf.Subtract(oneDay);
	      daysToFetch--;
	    }
	    historicalDates=GenerateHistoricalDates(asOf,daysToFetch);
	    for(int index=0;index<historicalDates.Count;index++)
	    {
	      DateTime date=(DateTime)historicalDates[index];
	      if(DayOfWeek.Friday==date.DayOfWeek)return GetPrevBusinessDay(date);
	    }
	    throw new Exception("The week no longer contains Friday?");
	  }
 	  public DateTime GetCurrentMonthEnd(DateTime asOf)
	  {
	    TimeSpan oneDay=new TimeSpan(-1,0,0,0);
	    DateTime date=new DateTime(asOf.Year,asOf.Month,asOf.Day);
      date=new DateTime(date.Year,date.Month,DateTime.DaysInMonth(asOf.Year,asOf.Month));
	    while(IsWeekend(date)||IsHoliday(asOf))date=date.Add(oneDay);
	    return date;
	  }
	  public DateTime GetNextMonthEnd(DateTime asOf)
	  {
	    TimeSpan oneDay=new TimeSpan(-1,0,0,0);
	    DateTime date=new DateTime(asOf.Year,asOf.Month,asOf.Day);
      date=date.AddMonths(1);
      date=new DateTime(date.Year,date.Month,DateTime.DaysInMonth(date.Year,date.Month));
	    while(IsWeekend(date)||IsHoliday(asOf))date=date.Add(oneDay);
	    return date;
	  }
	  public DateTime GetPrevMonthEnd(DateTime asOf)
	  {
	    TimeSpan oneDay=new TimeSpan(-1,0,0,0);
	    DateTime date=new DateTime(asOf.Year,asOf.Month,asOf.Day);
      date=date.AddMonths(-1);
      date=new DateTime(date.Year,date.Month,DateTime.DaysInMonth(date.Year,date.Month));
	    while(IsWeekend(date)||IsHoliday(asOf))date=date.Add(oneDay);
	    return date;
	  }
	  public DateTime GetPrevMonthEnd(DateTime asOf,int count)
	  {
      for(int index=0;index<count;index++)
      {
        asOf=GetPrevMonthEnd(asOf);
      }
      return asOf;
	  }
	  public DateTime EnsureWeekday(DateTime asOf)
	  {
	    TimeSpan oneDay=new TimeSpan(1,0,0,0);
	    if(!IsWeekend(asOf))return asOf;
	    while(IsWeekend(asOf=asOf.Add(oneDay)));
	    return asOf;
	  }
    public DateTime GenerateHistoricalDate(DateTime startDate,int dayCount)
    {
      DateTime histDate;
      int datedDates=0;
	    TimeSpan singleDay;

      startDate = startDate.Date;
      histDate=startDate;
      if(dayCount<0)singleDay=new TimeSpan(-1,0,0,0);
      else singleDay=new TimeSpan(1,0,0,0);
      dayCount=dayCount<0?-dayCount:dayCount;
      while(datedDates<dayCount)
      {
        if(IsMarketOpen(ref histDate))datedDates++;
        histDate=histDate.Subtract(singleDay);
      }
      return histDate.Add(singleDay);
    }
// Does not account for weekends etc.,
    public DateTime GenerateFutureDate(DateTime startDate,int dayCount)
    {
      DateTime futureDate;
      int datedDates=0;
	    TimeSpan singleDay;

      startDate = startDate.Date;
      futureDate=startDate;
      singleDay=new TimeSpan(1,0,0,0);
      while(datedDates<dayCount)
      {
        futureDate=futureDate.Add(singleDay);
        datedDates++;
      }
      return futureDate.Add(singleDay);
    }
// Accounts for weekends
    public DateTime GenerateFutureBusinessDate(DateTime startDate,int dayCount)
    {
      DateTime futureDate;
      int datedDates=0;
	    TimeSpan singleDay;

      startDate = startDate.Date;
      futureDate=startDate;
      singleDay=new TimeSpan(1,0,0,0);
      while(datedDates<dayCount)
      {
        if(IsMarketOpen(ref futureDate))datedDates++;
        futureDate=futureDate.Add(singleDay);
      }
      return futureDate.Add(singleDay);
    }
    public int DaysBetweenActual(DateTime historicalDate)
    {
      DateTime today = DateTime.Now;
      TimeSpan timeSpan = historicalDate.Date - today;
      return (int)timeSpan.TotalDays;
    }
    public int DaysBetweenActual(DateTime historicalDate,DateTime startingDate)
    {
      TimeSpan timeSpan = historicalDate.Date-startingDate;
      int totalDays = (int)timeSpan.TotalDays;
      if (historicalDate < startingDate) totalDays = (Math.Abs(totalDays) * -1);
      else totalDays = Math.Abs(totalDays);
      return totalDays;
    }
    public DateTime DaysAddActual(DateTime date, int daysActual)
    {
      TimeSpan days = new TimeSpan(daysActual, 0, 0, 0);
      return date + days;
    }
    public int DaysBetween(DateTime startDate, DateTime endDate)
    {
      if (startDate > endDate)
      {
        TimeSpan timeSpan = startDate.Date - endDate.Date;
        return (int)timeSpan.TotalDays;
      }
      else
      {
        TimeSpan timeSpan = endDate.Date - startDate.Date;
        return (int)timeSpan.TotalDays;
      }
    }
    public static List<int> GenerateHistoricalYear(int startYear,int years)
    {
      List<int> yearsList = new List<int>();
      for (int index = 0; index < years; index++)
      {
        yearsList.Add(startYear);
        startYear--;
      }
      return yearsList;
    }
    public List<DateTime> GenerateHistoricalDates(DateTime startDate, int dayCount)
    {
      List<DateTime> histDates=new List<DateTime>();
      DateTime histDate;
	    TimeSpan singleDay;

      startDate = startDate.Date;  
      histDate=startDate;
      if(dayCount<0)singleDay=new TimeSpan(-1,0,0,0);
      else singleDay=new TimeSpan(1,0,0,0);
      dayCount=dayCount<0?-dayCount:dayCount;
      while(histDates.Count<dayCount)
      {
        if(IsMarketOpen(ref histDate))histDates.Add(histDate);
        histDate=histDate.Subtract(singleDay);
      }
      return histDates;
    }
// The function will figure out which date is the most recent and which one is the historical date.
// Generally, make startDate the most recent and endDate the historical date.
		public List<DateTime> GenerateHistoricalDates(DateTime startDate,DateTime endDate)
		{
      if (Utility.Epoch.Equals(startDate)||Utility.Epoch.Equals(endDate)) return null;
      startDate = startDate.Date;
      endDate = endDate.Date;
      List<DateTime> histDates = new List<DateTime>();
			DateTime histDate;
			TimeSpan singleDay;
      
			bool reverse = false;
			if (endDate > startDate)
			{
				reverse = true;
				DateTime swap = endDate;
				endDate = startDate;
				startDate = swap;
			}
			histDate=startDate;
			singleDay=new TimeSpan(1,0,0,0);
			while(histDate>=endDate)
			{
				if(IsMarketOpen(ref histDate))histDates.Add(histDate);
				histDate=histDate.Subtract(singleDay);
			}
			if (reverse) histDates.Reverse();
			return histDates;
		}
// The function will figure out which date is the most recent and which one is the historical date.
// Generally, make startDate the most recent and endDate the historical date.
    public List<DateTime> GenerateHistoricalDatesActual(DateTime startDate, DateTime endDate)
    {
      if (Utility.Epoch.Equals(startDate) || Utility.Epoch.Equals(endDate)) return null;
      startDate = startDate.Date;
      endDate = endDate.Date;
      List<DateTime> histDates = new List<DateTime>();
      DateTime histDate;
      TimeSpan singleDay;

      bool reverse = false;
      if (endDate > startDate)
      {
        reverse = true;
        DateTime swap = endDate;
        endDate = startDate;
        startDate = swap;
      }
      histDate = startDate;
      singleDay = new TimeSpan(1, 0, 0, 0);
      while (histDate >= endDate)
      {
        histDates.Add(histDate);
        histDate = histDate.Subtract(singleDay);
      }
      if (reverse) histDates.Reverse();
      return histDates;
    }
    private bool IsMarketOpen(ref DateTime dateTime)
    {
      if(IsWeekend(dateTime)||IsHoliday(dateTime))return false;
      return true;
    }
    public bool IsWeekend(DateTime dateTime)
    {
	    if(DayOfWeek.Sunday==dateTime.DayOfWeek || DayOfWeek.Saturday==dateTime.DayOfWeek)return true;
	    return false;
    }
    public bool IsHoliday(DateTime dateTime)
    {
      if(null==holidays)return false;
      return holidays.Contains(dateTime.Date);
    }
  }
}

