using System;
using System.Text;
using System.IO;
using System.ComponentModel;
using System.Reflection;
using System.Windows.Forms;
using System.Collections.Generic;
using Halo3Map.Settings.Controls;
using System.Drawing.Design;

namespace Engineer
{

    public abstract class ISettings
    {
        public virtual void Read(BinaryReader br)
        {
            //For each property lets get the value and write it
            foreach (PropertyInfo pi in this.GetType().GetProperties(
                BindingFlags.Public | BindingFlags.Instance))
            {
                pi.SetValue(this, ReadValue(br, pi.PropertyType), null);
            }
        }

        public virtual void Write(BinaryWriter bw)
        {
            //For each property lets get the value and write it
            foreach (PropertyInfo pi in this.GetType().GetProperties(
                BindingFlags.Public | BindingFlags.Instance))
            {
                WriteValue(bw, pi.GetValue(this, null));
            }
        }

        private object ReadValue(BinaryReader br, Type type)
        {
            if (type == typeof(int))
                return br.ReadInt32();
            else if (type == typeof(bool))
                return br.ReadBoolean();
            else if (type == typeof(string))
                return br.ReadString();
            else if (type.IsEnum)
            {
                Type t = Enum.GetUnderlyingType(type);
                if (t == typeof(byte))
                    return br.ReadByte();
                else if (t == typeof(short))
                    return br.ReadInt16();
                else if (t == typeof(int))
                    return br.ReadInt32();
                else if (t == typeof(long))
                    return br.ReadInt64();
                else
                {
                    MessageBox.Show("Unable to read this enum type! Type: " + t.ToString());
                }
            }
            else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ISettingList<>))
            {
                object settingList = Activator.CreateInstance(type, new object[] { });
                settingList.GetType().GetMethod("Read").Invoke(settingList, new object[] { br });
                return settingList;
            }
            else
                MessageBox.Show("Cannot read type " + type.ToString());

            return null;
        }

        private void WriteValue(BinaryWriter bw, object value)
        {
            Type type = value.GetType();

            if (type == typeof(int))
                bw.Write((int)value);
            else if (type == typeof(bool))
                bw.Write((bool)value);
            else if (type == typeof(string))
                bw.Write((string)value);
            else if (type.IsEnum)
            {
                Type t = Enum.GetUnderlyingType(type);
                if (t == typeof(byte))
                    bw.Write((byte)value);
                else if (t == typeof(short))
                    bw.Write((short)value);
                else if (t == typeof(int))
                    bw.Write((int)value);
                else if (t == typeof(long))
                    bw.Write((long)value);
                else
                {
                    MessageBox.Show("Unable to write this enum type! Type: " + t.ToString());
                }
            }
            else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ISettingList<>))
            {
                MethodInfo mi = type.GetMethod("Write");
                mi.Invoke(value, new object[] { bw });
            }
            else
                MessageBox.Show("Cannot write type " + type);
        }
    }

    public class ISettingList<T> : List<T> where T : ISettings
    {
        public void Read(BinaryReader br)
        {
            int count = br.ReadInt32();
            for (int x = 0; x < count; x++)
            {
                T setting = (T)Activator.CreateInstance(typeof(T));
                setting.Read(br);
                Add(setting);
            }
        }

        public void Write(BinaryWriter bw)
        {
            bw.Write(this.Count);
            for (int x = 0; x < this.Count; x++)
                this[x].Write(bw);
        }
    }

    public class RecentFiles : ISettings
    {
        private string filePath;
        [Category("General Settings"),
        Description("Recent File Path"),
        Editor(typeof(UIFileNameEditor), typeof(UITypeEditor))]
        public string FilePath
        {
            get { return filePath; }
            set { filePath = value; }
        }

        public ToolStripMenuItem MenuItem;
    }

    public class Settings : ISettings
    {
        private string settingsAuthor;
        [Category("General Settings"),
        Description("User that created this settings file"),
        ReadOnly(true)]
        public string SettingsAuthor
        {
            get { return settingsAuthor; }
            set { settingsAuthor = value; }
        }

        private ISettingList<RecentFiles> recentFiles = new ISettingList<RecentFiles>();
        [Category("General Settings"),
        Description("Collection of recent files")]
        public ISettingList<RecentFiles> RecentFiles
        {
            get { return recentFiles; }
            set { recentFiles = value; }
        }

        private string mapFolder = "";
        [Category("General Settings"),
        Description("Location of the maps folder containg your Halo 3 Maps"),
        Editor(typeof(UIFolderNameEditor), typeof(UITypeEditor))]
        public string MapFolder
        {
            get { return mapFolder; }
            set { mapFolder = value; }
        }        

        private bool loadMapInfo = true;
        [Category("General Settings"),
        Description("Load corresponding map info (requires the info folder inside the maps directory)")]
        public bool LoadMapInfo
        {
            get { return loadMapInfo; }
            set
            {
                loadMapInfo = value;
                if (!loadMapInfo)
                    loadMapImage = false;
            }
        }

        private bool loadMapImage = true;
        [Category("General Settings"),
        Description("Load corresponding map image (requires the images folder inside the maps directory and LoadMapInfo to be enabled)")]
        public bool LoadMapImage
        {
            get
            {
                if (!loadMapInfo)
                    loadMapImage = false;
                return loadMapImage;
            }
            set
            {
                if (!loadMapInfo)
                    loadMapImage = false;
                loadMapImage = value;
            }
        }

        private bool autoCheckForUpdates = true;
        [Category("Program Update Settings"),
        Description("Automatically check for updates")]
        public bool AutoCheckForUpdates
        {
            get
            {
                if (!autoCheckForUpdates)
                    autoCheckForUpdates = false;
                return autoCheckForUpdates;
            }
            set
            {
                if (!autoCheckForUpdates)
                    autoCheckForUpdates = false;
                autoCheckForUpdates = value;
            }
        }

        public enum WhenToCheck : byte
        {
            Day = 0,
            Week = 1,
            Month = 2,
            Year = 3,
            Startup = 4,
        }

        private WhenToCheck checkEvery = WhenToCheck.Week;
        [Category("Program Update Settings"),
        Description("From this date on check every...(Auto Check Updates must be enabled to use this feature!")]
        public WhenToCheck CheckEvery
        {
            get
            {
                return checkEvery;
            }
            set
            {
                checkEvery = value;
            }
        }

        private string lastCheckedOn = DateTime.Now.ToString();
        [Category("Program Update Settings"),
        Description("Last time that a update check was preformed")]
        public string LastCheckedOn
        {
            get
            {
                return lastCheckedOn;
            }
            set
            {
                lastCheckedOn = value;
            }
        }

        private bool useSkin = false;
        [Category("Skin Settings"),
        Description("Use custom skin file")]
        public bool UseSkin
        {
            get
            {
                if (!useSkin)
                    useSkin = false;
                return useSkin;
            }
            set
            {
                if (!useSkin)
                    useSkin = false;
                useSkin = value;
            }
        }


        private string skinFile = "";
        [Category("Skin Settings"),
        Description("Location of the skin you would like to use"),
        Editor(typeof(UIFileNameEditor), typeof(UITypeEditor))]
        public string SkinFile
        {
            get { return skinFile; }
            set { skinFile = value; }
        }

        private bool showTipsAtStartup = true;
        [Category("Other"),
        Description("Do you want tips to be diplayed at startup")]
        public bool ShowTipsAtStartup
        {
            get
            {
                if (!showTipsAtStartup)
                    showTipsAtStartup = false;
                return showTipsAtStartup;
            }
            set
            {
                if (!showTipsAtStartup)
                    showTipsAtStartup = false;
                showTipsAtStartup = value;
            }
        }
    }

    public static class AppSettings
    {

        //Our Settings
        private static Settings settings = new Settings();
        public static Settings Settings
        {
            get { return settings; }
        }

        //settings location
        public static string settingsDirectory = "";
        public static string settingsName = "\\AppSettings.dat";

        public static void SaveSettings()
        {
            //Create a settings file
            FileStream fs = new FileStream(settingsDirectory + settingsName,
                FileMode.Create, FileAccess.Write);
            BinaryWriter bw = new BinaryWriter(fs);

            //Set the author of this settings
            settings.SettingsAuthor = SystemInformation.UserName;

            //Write our settings
            settings.Write(bw);

            //Close the file
            fs.Close();
        }

        public static void LoadSettings()
        {
            //Check if theres a settings file, if not then write it
            if (!File.Exists(settingsDirectory + settingsName))
                SaveSettings();

            //Open a settings file
            FileStream fs = new FileStream(settingsDirectory + settingsName,
                FileMode.Open, FileAccess.Read);
            BinaryReader br = new BinaryReader(fs);

            //read our settings
            settings.Read(br);

            //close the file
            fs.Close();

            //Find out if we are the author of this settings file
            if (settings.SettingsAuthor != SystemInformation.UserName)
            {
                DialogResult dr = MessageBox.Show("The settings file indicates that you are not user that created this Settings File. Would you like to start with a fresh Settings File?",
                      "Hmmm...", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (dr == DialogResult.Yes)
                {
                    //Delete the settings file and create a new class and file
                    File.Delete(settingsDirectory + settingsName);
                    settings = new Settings();
                    SaveSettings();
                }
                else
                {
                    //They don't want to create a new one, so lets just save it again
                    //this will change the author to them
                    SaveSettings();
                }
            }
        }

        public static void ClearSettings()
        {
            //Reset the settings data
            settings = new Settings();

            //Now Save them
            SaveSettings();
        }

        public static void DeleteSettings()
        {
            //Check if theres a settings file, then delete it
            if (File.Exists(settingsDirectory + settingsName))
                File.Delete(settingsDirectory + settingsName);
        }

    }

}