using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using nft.framework;
using System.Xml;
using nft.util;
using System.Diagnostics;
using nft.core.schedule;
using System.IO;
using nft.framework.loader;
using nft.core.view;
using nft.framework.drawing;

namespace nft.test.test {
    class ClockTest {        
        [TestEntry]
        public static void InitCalendar() {
            if (Calendar.ActiveCalendar != null) {
                Debug.WriteLine("The calendar is already initialized.");
                return;
            }
            XmlDocument doc = XmlUtil.LoadFile(Path.Combine(Directories.AppBaseDir, "core_modules.xml"));
            ParamsReader reader = new ParamsReader(doc.BaseURI, new XmlParamParser(doc));
            ParamsReader root = reader["modules"];
            foreach (ParamsReader cn in root.EnumChildren("module")) {
                if (cn["name"].InnerText.Equals("Game Time Calendar")) {
                    Debug.WriteLine("Initialize the calendar.");
                    new Calendar(cn);
                }
            }
        }

        [TestEntry(new object[]{new int[]{0,3,13,31}})]
        public static void TestDateConversion(int[] years) {
            ITimeTickConverter ttc = Calendar.ActiveCalendar.TimeTickConverter;
            long day = ttc.DailyTicks;
            long yer = ttc.YearlyTicksAve;
            long mnt = ttc.MonthlyTicksAve;
            int mcnt = (int)Math.Ceiling(yer / (double)mnt);
            int dcnt = (int)Math.Ceiling(mnt / (double)day);
            int erros = 0;
            Debug.WriteLine(String.Format("{0} months per year, {1} days per month.", mcnt, dcnt));
            for (int i = 0; i < years.Length; i++) {
                Debug.WriteLine(String.Format("## Start loop for the {0}th year ##", years[i]));
                for (int m = 1; m <= mcnt; m++) {
                    for (int d = 1; d <= dcnt; d++) {
                        Date date = new Date(years[i], m, d);
                        Time time = Time.Create(date, ttc);
                        Date dat2 = time.ToDate();
                        if (!date.Equals(dat2)) {
                            erros++;
                            Debug.WriteLine(String.Format("Conversion Error: {0} -> {1}", date, dat2));
                        }
                    }
                }
            }
            if(erros>0) throw new Exception(String.Format("Detect {0} conversion errors!",erros));
        }

        [TestEntry(new object[] { new int[] { 0, 31, 977, 4073 } })]
        public static void TestTimeOfDayConversion(int[] days) {
            ITimeTickConverter ttc = Calendar.ActiveCalendar.TimeTickConverter;
            long day = ttc.DailyTicks;
            long secs = ttc.MinutelyTicks;
            int erros = 0;
            for (int i = 0; i < days.Length; i++) {
                Date offset = TimeLength.FromDays(ttc, days[i]).ToDate();
                Debug.WriteLine(String.Format("## Start 24H loop for the {0} ##", offset));
                for (int h = 0; h < 24; h++) {
                    for (int m = 0; m < 60; m++) {
                        for (int s = 0; s < secs; s++) {
                            TimeOfDay tod = new TimeOfDay(h, m, s);
                            Time time = Time.Create(offset, tod, ttc);
                            TimeOfDay tod2 = time.GetTimeOfTheDay();
                            if (!tod.Equals(tod2)) {
                                erros++;
                                Debug.WriteLine(String.Format("Conversion Error: {0} -> {1}", tod, tod2));
                            }
                        }
                    }
                }
            }
            if (erros > 0) throw new Exception(String.Format("Detect {0} conversion errors!", erros));
        }

        [TestEntry]
        public static void TestClock() {
            Clock clock = Calendar.ActiveCalendar.CreateNewClock(new Date());
            clock.RegisterDailyHandler(new ClockEventCallback("Daily").Callback, new TimeOfDay(12, 34));
            clock.RegisterWeeklyHandler(new ClockEventCallback("Weekly_A").Callback, DayOfWeek.Friday, new TimeOfDay(23, 45));
            clock.RegisterYearlyHandler(new ClockEventCallback("Yearly2/2").Callback, Time.Create(new Date(0,2,2)));
            clock.RegisterYearlyHandler(new ClockEventCallback("Yearly3/3").Callback, Time.Create(new Date(0,3,3)));
            clock.RegisterWeeklyHandler(new ClockEventCallback("Weekly_B").Callback, DayOfWeek.Monday, new TimeOfDay(0, 0));
            clock.RegisterYearlyHandler(new ClockEventCallback("Yearly4/4").Callback, Time.Create(new Date(0, 4, 4), new TimeOfDay(4,4)));

            long l = Calendar.ActiveCalendar.TimeTickConverter.HourlyTicks;
            long lmax = Time.Create(new Date(2, 5, 1)).Ticks;
            while (lmax > 0) {
                clock.Tick(l);
                lmax -= l;
            }

        }

        [TestEntry]
        public static void TestConditionedResource() {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(@"<root>
<default>Apple</default>
<condition when='winter'><value>Banana</value>
  <condition when='night'>Orange</condition>
</condition>
<condition when='night'>
  <condition when='summer'>Pinapple</condition>
</condition>
</root>");
            ParamsReader reader = new ParamsReader("test", new XmlParamParser(doc.FirstChild));
            ConditionedResource<String> res = ConditionedResourceFactory.LoadAsConditiondResources<String>(reader, ParseString );
            ITimeTickConverter ttc = Calendar.ActiveCalendar.TimeTickConverter;
            Date spring = new Date(1, 4, 6);
            Date summer = new Date(1, 7, 10);
            Date winter = new Date(1, 1, 5);
            TimeOfDay night = new TimeOfDay(0, 0);
            TimeOfDay day = new TimeOfDay(13, 30);
            Debug.WriteLine("Spring,Day="+res[new TestParams(Time.Create(spring, day))]);
            PrimitiveConditions.InvalidateCachedConditions();
            Debug.WriteLine("Spring,Night=" + res[new TestParams(Time.Create(spring, night))]);
            PrimitiveConditions.InvalidateCachedConditions();
            Debug.WriteLine("Summer,Day=" + res[new TestParams(Time.Create(summer, day))]);
            PrimitiveConditions.InvalidateCachedConditions();
            Debug.WriteLine("Summer,Night=" + res[new TestParams(Time.Create(summer, night))]);
            PrimitiveConditions.InvalidateCachedConditions();
            Debug.WriteLine("Winter,Day=" + res[new TestParams(Time.Create(winter, day))]);
            PrimitiveConditions.InvalidateCachedConditions();
            Debug.WriteLine("Winter,Night=" + res[new TestParams(Time.Create(winter, night))]);

        }

        static private String ParseString(ParamsReader r) {
            return r.InnerText;
        }

        class ClockEventCallback {
            String msg;
            int n;
            public ClockEventCallback(String m) {
                msg = m;
                n = 0;
            }

            public bool Callback(Clock clock) {
                Debug.Write(clock);
                Debug.Write(" " + msg);
                Debug.WriteLine(" " + n++);
                return n < 10;
            }
        }

        class TestParams : ISceneParams {
            private Time time;
            public TestParams(Time t) {
                time = t;
                Debug.WriteLine("Time="+time);
            }

            public Time Time { get{ return time; }}

            public object Weather { get { return null; }}

            public Scaler Scaler { get {return Scaler.Default; }}
        }
    }
}
