using System;
using System.Collections.Generic;
using System.IO;
using System.Drawing;
using System.Linq;
using System.Net.Mime;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using CommandLine;
using CommandLine.Text;
using LIFX;
using LifxCMD;
using log4net;
using log4net.Repository.Hierarchy;
using Microsoft.CSharp.RuntimeBinder;

namespace LifxCMD
{

    public class Options {

        [Option('l', "list",
            Required = false,
            DefaultValue = null,
            MutuallyExclusiveSet = "ListSet",
            HelpText = "List all bulbs found. Can only be run on its own."
        )]
        public bool List { get; set; }

        [Option('p', "poll",
            Required = false,
            DefaultValue = null,
            MutuallyExclusiveSet = "ListSet",
            HelpText = "Poll for bulbs every 5 seconds."
        )]
        public bool Poll { get; set; }

        [OptionList('n', "name", Separator = ',',
            Required = false,
            HelpText = "Select bulb(s) by name. Multiple bulbs can be separated by commas.")]
        public IList<string> Bulbs { get; set; }

        [Option('h', "hue",
            Required = false,
            DefaultValue = null,
            MutuallyExclusiveSet = "PowerSet",
            HelpText = "Set a bulb(s) hue between 0 and 65525."
            )]
        public String Hue { get; set; }

        [Option('s', "saturation",
            Required = false,
            DefaultValue = null,
            MutuallyExclusiveSet = "ListSet",
            HelpText = "Set a bulb(s) saturation between 0 and 65525."
            )]
        public String Saturation { get; set; }

        [Option('b', "brightness",
            Required = false,
            DefaultValue = null,
            MutuallyExclusiveSet = "ListSet",
            HelpText = "Set a bulb(s) brightness between 0 and 65525 or as a percentage e.g. 50%"
            )]
        public string Brightness { get; set; }

        [Option('k', "kelvin",
            Required = false,
            DefaultValue = null,
            MutuallyExclusiveSet = "ListSet",
            HelpText = "Set a bulb(s) kelvin between 2500 and 10000."
            )]
        public String Kelvin { get; set; }

        [Option('t', "transition",
            Required = false,
            MutuallyExclusiveSet = "ListSet",
            HelpText = "Set a bulb(s) transition to the set color in milliseconds."
            )]
        public uint Transition { get; set; }

        [Option("rgb",
            Required = false,
            DefaultValue = null,
            MutuallyExclusiveSet = "ColorSet",
            HelpText = "Set a bulb(s) RGB Color separated by commas - r,g,b."
            )]
        public string RGB { get; set; }

        [Option('c',"colour",
            Required = false,
            DefaultValue = null,
            MutuallyExclusiveSet = "ColorSet",
            HelpText = "Set a bulb(s) to a System.Drawing Color e.g. AliceBlue."
            )]
        public string Colour { get; set; }

        [HelpOption]
        public string GetUsage()
        {
            return HelpText.AutoBuild(this,
              (HelpText current) => HelpText.DefaultParsingErrorsHandler(this, current));
        }
        
    }    

    class Program
    {
        public static readonly ILog Logger = LogManager.GetLogger(typeof(Program));
        public static string applicationDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
        public static List<LifxBulb> devicelist = new List<LifxBulb>();

        //init the Lifx Network
        static LIFXNetwork Network = new LIFXNetwork();

        static void Main(string[] args)
        {
            //init log4net
            log4net.Config.XmlConfigurator.Configure();
            try
            {
                //start the lifx network
                Network.Start();
                // Wait 1 second to get at least first bulb
                Thread.Sleep(1000);


                var options = new Options();
                if (CommandLine.Parser.Default.ParseArguments(args, options))
                {
                    if (options.List)
                    {
                        Logger.Info("Discovering Lifx Bulbs..");
                        if (Network.bulbs.Count > 0)
                        {
                            Logger.Info("Found " + Network.bulbs.Count + " bulbs");
                            Logger.Info("------------------------------------------------");
                            foreach (LIFXBulb lifxBulb in Network.bulbs)
                            {
                                Logger.Info(string.Format("Label: {0}", lifxBulb.Label));
                                Logger.Info(string.Format("{1} - MAC: {2} - [ H:{3} S:{4} B:{5} K:{6} ]",
                                    lifxBulb.Label, lifxBulb.BulbEndpoint.Address, convertmac(lifxBulb.BulbMac),
                                    lifxBulb.Hue,
                                    lifxBulb.Saturation, lifxBulb.Brightness, lifxBulb.Kelvin));
                                Logger.Info("---");
                            }
                        }
                        else
                        {
                            Logger.Error("No bulbs found on network.");
                        }
                    }

                    if (options.Poll)
                    {
                        while (Network.State == NetworkState.Initialized)
                        {
                            Console.Clear();
                            Logger.Info("Polling Lifx Bulbs..");
                            Logger.Info(DateTime.Now);
                            Network.Inventory();
                            if (Network.bulbs.Count > 0)
                            {
                                Logger.Info("Found " + Network.bulbs.Count + " bulbs");
                                Logger.Info("------------------------------------------------");
                                foreach (LIFXBulb lifxBulb in Network.bulbs)
                                {
                                    Logger.Info(string.Format("Label: {0}", lifxBulb.Label));
                                    Logger.Info(string.Format("{1} - MAC: {2} - [ H:{3} S:{4} B:{5} K:{6} ]",
                                        lifxBulb.Label, lifxBulb.BulbEndpoint.Address, convertmac(lifxBulb.BulbMac),
                                        lifxBulb.Hue,
                                        lifxBulb.Saturation, lifxBulb.Brightness, lifxBulb.Kelvin));
                                    Logger.Info("---");
                                }
                            }
                            else
                            {
                                Logger.Error("No bulbs found on network.");
                            }
                            //Network.Inventory();
                            Thread.Sleep(5000);
                        }
                    }

                    if (options.Bulbs != null && options.Bulbs.Count > 0)
                    {
                        foreach (var device in options.Bulbs)
                        {
                            LIFXBulb bulb;
                            if (device.Contains("."))
                            {
                                bulb = Network.bulbs.Find(x => x.BulbEndpoint.Address.ToString() == device.ToLower());
                            }
                            else
                            {
                                bulb = Network.bulbs.Find(x => x.Label.ToLower() == device.ToLower());
                            }

                            if (bulb != null)
                            {
                                UInt16 hue;
                                UInt16 saturation;
                                UInt16 brightness;

                                if (options.Colour != null)
                                {
                                    Color c = Color.FromName(options.Colour);
                                    float huefloat = (c.GetHue()*182);
                                    float satfloat = (c.GetSaturation()*65535);
                                    float brifloat = c.GetBrightness()*65535;
                                    hue = Convert.ToUInt16(huefloat);
                                    saturation = Convert.ToUInt16(satfloat);
                                    brightness = Convert.ToUInt16(brifloat);
                                }
                                else if (options.RGB != null && options.RGB.Split(',').Count() == 3)
                                {
                                    string[] rgb = options.RGB.Split(',');
                                    Color c = Color.FromArgb(Convert.ToInt16(rgb[0]), Convert.ToInt16(rgb[1]),
                                        Convert.ToInt16(rgb[2]));
                                    float huefloat = c.GetHue()*255;
                                    float satfloat = c.GetSaturation()*65535;
                                    float brifloat = c.GetBrightness()*65535;
                                    hue = Convert.ToUInt16(huefloat);
                                    saturation = Convert.ToUInt16(satfloat);
                                    brightness = Convert.ToUInt16(brifloat);
                                }
                                else
                                {
                                    hue = (options.Hue == null) ? bulb.Hue : Convert.ToUInt16(options.Hue);
                                    saturation = (options.Saturation == null)
                                        ? bulb.Saturation
                                        : Convert.ToUInt16(options.Saturation);
                                    if (options.Brightness.Contains("%"))
                                    {
                                        options.Brightness = options.Brightness.Replace("%", "");
                                        double percentage = Convert.ToDouble(options.Brightness)/100;
                                        brightness = (ushort) (65535*percentage);
                                    }
                                    else
                                    {
                                        brightness = (options.Brightness == null)
                                            ? bulb.Brightness
                                            : Convert.ToUInt16(options.Brightness);
                                    }
                                }

                                UInt16 kelvin = (options.Kelvin == null)
                                    ? bulb.Kelvin
                                    : Convert.ToUInt16(options.Kelvin);

                                Logger.Info(string.Format("Setting '{0}' to [ H:{1} S:{2} B:{3} K:{4} ]..", device, hue,
                                    saturation, brightness, kelvin));

                                //set the bulb
                                Network.SetBulbValues(hue, saturation, brightness, kelvin, options.Transition, bulb);
                            }
                            else
                            {
                                Logger.Error("Bulb '" + device + "' not found!");
                            }
                        }
                    }

                }                
            }
            catch (Exception ex)
            {
               Logger.Error("Error occurred: " + ex.Message);
            }
        }


        static void LoadSettings()
        {
            
        }

        static string convertmac(byte[] mac)
        {
            return BitConverter.ToString(mac);
        }

    }
}
