using System;
using System.Collections.Generic;
using System.Text;

namespace nft.core.schedule {
    [Serializable]
    public class SimpleTimeTickConverter : ITimeTickConverter{
        public readonly long TicksIn1Minute;
        public readonly int MinutesIn1Hour;
        public readonly int HoursIn1Day;
        public readonly int DaysIn1Month;
        public readonly int DaysIn1Week = 7; // fixed
        public readonly int MonthsIn1Year;
        public readonly long TicksIn1Hour;
        public readonly long TicksIn1Day;
        public readonly long TicksIn1Week;
        public readonly long TicksIn1Month;
        public readonly long TicksIn1Year;

        protected readonly long[] ticksForUnit;

        public SimpleTimeTickConverter(int ticks_in_1minute, int days_in_1month) : this(ticks_in_1minute, 60, 24, days_in_1month, 12) {
        }


        public SimpleTimeTickConverter(int ticks_in_1minute, int minutes_in_1hour, int hours_in_1day, int days_in_1month, int months_in_1year) {
            this.TicksIn1Minute = ticks_in_1minute;
            this.MinutesIn1Hour = minutes_in_1hour;
            this.HoursIn1Day = hours_in_1day;
            this.DaysIn1Month = days_in_1month;
            this.MonthsIn1Year = months_in_1year;
            // calculate total ticks
            this.TicksIn1Hour = this.TicksIn1Minute * this.MinutesIn1Hour;
            this.TicksIn1Day = this.TicksIn1Hour * this.HoursIn1Day;
            this.TicksIn1Month = this.TicksIn1Day * this.DaysIn1Month;
            this.TicksIn1Year = this.TicksIn1Month * this.MonthsIn1Year;
            this.TicksIn1Week = this.TicksIn1Day * this.DaysIn1Week;

            ticksForUnit = new long[Enum.GetValues(typeof(TimeUnit)).Length];
            ticksForUnit[(int)TimeUnit.Ticks] = long.MaxValue;
            ticksForUnit[(int)TimeUnit.Minute] = this.TicksIn1Minute;
            ticksForUnit[(int)TimeUnit.Hour] = this.TicksIn1Hour;
            ticksForUnit[(int)TimeUnit.Day] = this.TicksIn1Day;
            ticksForUnit[(int)TimeUnit.Week] = this.TicksIn1Week;
            ticksForUnit[(int)TimeUnit.Month] = this.TicksIn1Month;
            ticksForUnit[(int)TimeUnit.Years] = this.TicksIn1Year;
        }

        public bool TreatLeapYears {
            get{ return false; }
        }

        public bool DaysInMonthVariable {
            get{ return true; }
        }

        public long ToTicks(Date d) {
            return d.Year * TicksIn1Year + (d.Month-1) * TicksIn1Month + (d.Day-1) * TicksIn1Day;
        }

        public long ToTicks(TimeOfDay td) {
            return td.Hour * TicksIn1Hour + td.Minute * TicksIn1Minute + (td.Second * TicksIn1Minute / 60);
        }

        public Date ToDate(long ticks) {
            long d = ticks/TicksIn1Day;
            long y,m;
            m = Math.DivRem(d, DaysIn1Month, out d);
            y = Math.DivRem(m, MonthsIn1Year, out m);
            return new Date((int)y, (int)m+1, (int)d+1);
        }

        public TimeOfDay ToTimeOfDay(long ticks) {
            long s;
            long m = Math.DivRem(ticks, TicksIn1Minute, out s);
            s = 60 * s / TicksIn1Minute; // Normarize
            long h = Math.DivRem(m, MinutesIn1Hour, out m);
            h = h % HoursIn1Day;
            return new TimeOfDay((int)h, (int)m, (int)s);
        }

        public int GetSecond(long ticks) {
            return (int)(ticks % TicksIn1Minute);
        }
        public int GetMinute(long ticks) {
            long m = ticks / TicksIn1Minute;
            return (int)(m % MinutesIn1Hour);
        }
        public int GetHour(long ticks) {
            long h = ticks / TicksIn1Hour;
            return (int)(h % HoursIn1Day);
        }

        public int GetDay(long ticks) {
            long d = ticks / TicksIn1Day;
            return 1+(int)(d % DaysIn1Month);
        }

        public int GetDayOfWeek(long ticks) {
            long d = ticks / TicksIn1Day;
            return (int)(d % DaysIn1Week);
        }

        public int GetMonth(long ticks) {
            long m = ticks % TicksIn1Year;
            return 1+(int)(m / TicksIn1Month);
        }

        public int GetYear(long ticks) {
            return (int)(ticks / TicksIn1Year);
        }

        public int GetDayOfWeek(Date d) {
            long dd = (d.Year * MonthsIn1Year + d.Month-1) * DaysIn1Month + d.Day-1;
            return (int)(dd % DaysIn1Week);
        }

        public long DailyTicks {
            get {
                return TicksIn1Day;
            }
        }

        public long HourlyTicks {
            get {
                return TicksIn1Hour;
            }
        }

        public long MinutelyTicks {
            get {
                return TicksIn1Minute;
            }
        }

        // average ticks in a month.
        public long MonthlyTicksAve {
            get {
                return TicksIn1Month;
            }
        }
        // average ticks in a year.
        public long YearlyTicksAve {
            get {
                return TicksIn1Year;
            }
        }

        /// <summary>
        /// Retruns reminder ticks (in seconds) divided by given cycle.
        /// </summary>
        /// <param name="t"></param>
        /// <param name="cycle"></param>
        /// <returns></returns>
        public long TicksForUnit(TimeUnit cycle) {
            return ticksForUnit[(int)cycle];
        }
    }
}
