using System;

namespace Swan {
  /// <summary>
  /// Represents a struct of DateTimeSpan to compare dates and get in 
  /// separate fields the amount of time between those dates.
  /// 
  /// Based on https://stackoverflow.com/a/9216404/1096693.
  /// </summary>
  public struct DateTimeSpan {
    /// <summary>
    /// Initializes a new instance of the <see cref="DateTimeSpan"/> struct.
    /// </summary>
    /// <param name="years">The years.</param>
    /// <param name="months">The months.</param>
    /// <param name="days">The days.</param>
    /// <param name="hours">The hours.</param>
    /// <param name="minutes">The minutes.</param>
    /// <param name="seconds">The seconds.</param>
    /// <param name="milliseconds">The milliseconds.</param>
    public DateTimeSpan(Int32 years, Int32 months, Int32 days, Int32 hours, Int32 minutes, Int32 seconds, Int32 milliseconds) {
      this.Years = years;
      this.Months = months;
      this.Days = days;
      this.Hours = hours;
      this.Minutes = minutes;
      this.Seconds = seconds;
      this.Milliseconds = milliseconds;
    }

    /// <summary>
    /// Gets the years.
    /// </summary>
    /// <value>
    /// The years.
    /// </value>
    public Int32 Years {
      get;
    }

    /// <summary>
    /// Gets the months.
    /// </summary>
    /// <value>
    /// The months.
    /// </value>
    public Int32 Months {
      get;
    }

    /// <summary>
    /// Gets the days.
    /// </summary>
    /// <value>
    /// The days.
    /// </value>
    public Int32 Days {
      get;
    }

    /// <summary>
    /// Gets the hours.
    /// </summary>
    /// <value>
    /// The hours.
    /// </value>
    public Int32 Hours {
      get;
    }

    /// <summary>
    /// Gets the minutes.
    /// </summary>
    /// <value>
    /// The minutes.
    /// </value>
    public Int32 Minutes {
      get;
    }

    /// <summary>
    /// Gets the seconds.
    /// </summary>
    /// <value>
    /// The seconds.
    /// </value>
    public Int32 Seconds {
      get;
    }

    /// <summary>
    /// Gets the milliseconds.
    /// </summary>
    /// <value>
    /// The milliseconds.
    /// </value>
    public Int32 Milliseconds {
      get;
    }

    internal static DateTimeSpan CompareDates(DateTime date1, DateTime date2) {
      if(date2 < date1) {
        DateTime sub = date1;
        date1 = date2;
        date2 = sub;
      }

      DateTime current = date1;
      Int32 years = 0;
      Int32 months = 0;
      Int32 days = 0;

      Phase phase = Phase.Years;
      DateTimeSpan span = new DateTimeSpan();
      Int32 officialDay = current.Day;

      while(phase != Phase.Done) {
        switch(phase) {
          case Phase.Years:
            if(current.AddYears(years + 1) > date2) {
              phase = Phase.Months;
              current = current.AddYears(years);
            } else {
              years++;
            }

            break;
          case Phase.Months:
            if(current.AddMonths(months + 1) > date2) {
              phase = Phase.Days;
              current = current.AddMonths(months);
              if(current.Day < officialDay &&
                  officialDay <= DateTime.DaysInMonth(current.Year, current.Month)) {
                current = current.AddDays(officialDay - current.Day);
              }
            } else {
              months++;
            }

            break;
          case Phase.Days:
            if(current.AddDays(days + 1) > date2) {
              current = current.AddDays(days);
              TimeSpan timespan = date2 - current;
              span = new DateTimeSpan(years, months, days, timespan.Hours, timespan.Minutes, timespan.Seconds, timespan.Milliseconds);
              phase = Phase.Done;
            } else {
              days++;
            }

            break;
        }
      }

      return span;
    }

    private enum Phase {
      Years,
      Months,
      Days,
      Done,
    }
  }
}