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

namespace nft.core.schedule {
    public enum Weather : short {
        Fine = 0, Cloudy, Fog, LightRain, HeavyRain, Thunderstrom, Rainstrom, Snow, Snowstorm
    }
    public enum DayNight : short {
        Morning = 0, Afternoon, Sunset, Evening, Midnight, Dawn
    }
    public enum Season : short { Spring = 0, Summer, Autumn, Winter }
    public enum SeasonDetail : short {
        EarlySpring = 0, MidSpring, LateSpring,
        EarlySummer, MidSummer, LateSummer,
        EarlyAutumn, MidAutumn, LateAutumn,
        EarlyWinter, MidWinter, LateWinter
    }

    public interface ISeasonScheduler {
        SeasonDetail ToSeason(DateTime date);
    }
    public interface IDayNightScheduler {
        DayNight ToDayNight(DateTime datetime);
    }

    class EnvironmentEnums {
        /// <summary>
        /// Returns true if 'w' is not associated with rain or snow.
        /// </summary>
        /// <param name="w"></param>
        /// <returns></returns>
        public static bool IsDry(Weather w) {
            short _w = (short)w;
            return _w < 3;
        }
        public static bool IsRain(Weather w) {
            short _w = (short)w;
            return _w >= 3 && _w <= 6;
        }
        public static bool IsSnow(Weather w) {
            short _w = (short)w;
            return _w >= 7;
        }
        /// <summary>
        /// Retruns true if specifed DayNight value grouped as daytime.
        /// </summary>
        /// <param name="dn"></param>
        /// <returns></returns>
        public static bool IsDaytime(DayNight dn) {
            short _dn = (short)dn;
            return _dn < 3;
        }

        public static Season ToSeason(SeasonDetail sd) {
            Season s = (Season)((short)sd / 3);
            return s;
        }

        private static ISeasonScheduler seasonSeparator = new DefaultSeasonSeparator();
        public static ISeasonScheduler SeasonSeparator {
            get { return seasonSeparator; }
            set { seasonSeparator = (value != null) ? value : new DefaultSeasonSeparator(); }
        }

        private static IDayNightScheduler daynightSeparator = new DefaultDayNightSeparator();
        public static IDayNightScheduler DayNightSeparator {
            get { return daynightSeparator; }
            set { daynightSeparator = (value != null) ? value : new DefaultDayNightSeparator(); }
        }

        public static DayNight GetDayNightAt(DateTime datetime) {
            return daynightSeparator.ToDayNight(datetime);
        }

        public static SeasonDetail GetSeasonAt(DateTime date) {
            return seasonSeparator.ToSeason(date);
        }
    }

    /// <summary>
    /// Default ISeasonSeparator implementation, allocate three months for each season.
    /// </summary>
    public class DefaultSeasonSeparator : ISeasonScheduler {

        public DefaultSeasonSeparator()
            : this(3, 6, 9, 12) {
        }
        protected DefaultSeasonSeparator(int springbegin, int summerbegin, int autumnbegin, int winterbegin) {
            seasons = new SeasonDetail[13];
            int n;
            IEnumerator<SeasonDetail> en =
                (IEnumerator<SeasonDetail>)Enum.GetValues(typeof(SeasonDetail)).GetEnumerator();
            n = (springbegin > summerbegin) ? summerbegin + 12 : summerbegin;
            for (int i = springbegin; i < n; i++) {
                seasons[i > 12 ? (i - 12) : i] = en.Current;
                en.MoveNext();
            }
            n = (summerbegin > autumnbegin) ? autumnbegin + 12 : autumnbegin;
            for (int i = summerbegin; i < n; i++) {
                seasons[i > 12 ? (i - 12) : i] = en.Current;
                en.MoveNext();
            }
            n = (autumnbegin > winterbegin) ? winterbegin + 12 : winterbegin;
            for (int i = autumnbegin; i < n; i++) {
                seasons[i > 12 ? (i - 12) : i] = en.Current;
                en.MoveNext();
            }
            n = (winterbegin > springbegin) ? springbegin + 12 : springbegin;
            for (int i = winterbegin; i < n; i++) {
                seasons[i > 12 ? (i - 12) : i] = en.Current;
                en.MoveNext();
            }
        }
        protected readonly SeasonDetail[] seasons;
        #region ISeasonSeparator メンバ
        public SeasonDetail ToSeason(DateTime date) {
            return seasons[date.Month];
        }
        #endregion
    }

    /// <summary>
    /// Default IDayNightSeparator implementation, allocate three months for each season.
    /// </summary>
    public class DefaultDayNightSeparator : IDayNightScheduler {
        public DefaultDayNightSeparator()
            : this(5, 18, 21) {
        }
        protected DefaultDayNightSeparator(int dawnhour, int sunsethour, int midnightbegin) {
            daynights = new DayNight[13];
            Array arr = Enum.GetValues(typeof(DayNight));
            int m = arr.Length;
            int n;
            n = dawnhour;
            for (int i = 0; i < n; i++) {
                daynights[i] = DayNight.Midnight;
            }
            daynights[n] = DayNight.Dawn;
            n++;
            for (int i = n; i < 12; i++) {
                daynights[i] = DayNight.Morning;                
            }
            n = sunsethour;
            for (int i = 12; i < n; i++) {
                daynights[i] = DayNight.Afternoon;
            }
            daynights[n] = DayNight.Sunset;
            n++;
            if (midnightbegin > 12) {
                for (int i = n; i < midnightbegin; i++) {
                    daynights[i] = DayNight.Evening;
                }
                for (int i = midnightbegin; i < 24; i++) {
                    daynights[i] = DayNight.Midnight;
                }
            } else {
                for (int i = n; i < 24; i++) {
                    daynights[i] = DayNight.Evening;
                }
                for (int i = 0; i < midnightbegin; i++) {
                    daynights[i] = DayNight.Evening;
                }
            }
        }

        protected readonly DayNight[] daynights;
        #region IDayNightSeparator メンバ
        public DayNight ToDayNight(DateTime time) {
            return daynights[time.Hour];
        }

        #endregion
    }

}