using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
using EMDServer.Common;
using EMDServer.Connection.RS232;
using EMDServer.Core.Devices;
using EMDServer.Connection.Mock;
using EMDServer.Connection.Visualization;
using EMDServer.Util;


namespace EMDServer.Core
{
    /// <summary>
    /// Handles the main functionality of the EMDServer and
    /// acts as centrel controller.
    /// </summary>
    public class MainController
    {
        #region Class variables
        // ----------------------------------------
        private Dictionary<string, iEMDConnection> emdInterfaces;
        private DeviceManager devManager;
        private CommandProcessor cmdProcessor;

        private bool running;
        // ----------------------------------------
        #endregion

        #region Public methods
        // ----------------------------------------
        /// <summary>
        /// Creates a new instance of the class which implements
        /// the core functionality of the EMD application
        /// </summary>
        public MainController()
        {
            //starts needed parts to run our server
            devManager = new DeviceManager();
            cmdProcessor = new CommandProcessor();
            cmdProcessor.setMainController(this);

            //init all connected interfaces
            this.initInterfaces();

            //init all stored connections between devices
            this.initConnections();

            // run the application
            this.running = true;
            this.start();

            //Debug.WriteLine("MAINCONTROLLER - Instantiated!");
        }

        /// <summary>
        /// inits all interfaces (iEMDConnection) which are set in configuration file.
        /// all active interfaces are stored in a list.
        /// </summary>
        private void initInterfaces()
        {
            emdInterfaces = new Dictionary<string, iEMDConnection>();

            if (Properties.Settings.Default.RS232Interface)
            {
                iEMDConnection rs232Interface = new RS232Interface();
                rs232Interface.openConnection();
                rs232Interface.cmdReceived += new EventHandler<CommandHandler>(emdInterface_cmdReceived);

                emdInterfaces.Add("Rs232", rs232Interface);
            }

            if (Properties.Settings.Default.WebInterface)
            {
                EMDWebserver.Webserver webserver = new EMDWebserver.Webserver();

                if (webserver.EmdConnection != null)
                {
                    iEMDConnection webInterface = webserver.EmdConnection;
                    webserver.startServer();

                    webInterface.openConnection();
                    webInterface.cmdReceived += new EventHandler<CommandHandler>(emdInterface_cmdReceived);

                    emdInterfaces.Add("Web", webInterface);
                }
            }
            if (Properties.Settings.Default.MockInterface)
            {
                MockInterface mock = new MockInterface();
                mock.cmdReceived += new EventHandler<CommandHandler>(emdInterface_cmdReceived);
                mock.openConnection();

                emdInterfaces.Add("Mock", mock);
            }
            if (Properties.Settings.Default.DisplayInterface)
            {
                VisualizationInterface display = new VisualizationInterface();
                display.cmdReceived += new EventHandler<CommandHandler>(emdInterface_cmdReceived);
                display.openConnection();

                emdInterfaces.Add("Display", display);
            }
        }

        /// <summary>
        /// inits all connections which are made by code or by a settingfile.
        /// Normally this part will be made on the fly by the maincontroller, whenever
        /// a device sends the "addDevice" command to our server.
        /// It will then be mapped according to a saved settingsfile to its counterpart(s), or the user has
        /// to connect the device to another device manually.
        /// </summary>
        private void initConnections()
        {
            //iPhone 1 / Buttons --> Web
            this.addDevice(110, Constants.EMD_DEVICE_TYPE.BUTTON, emdInterfaces["Web"]);
            this.addDevice(111, Constants.EMD_DEVICE_TYPE.BUTTON, emdInterfaces["Web"]);
            this.addDevice(112, Constants.EMD_DEVICE_TYPE.SWITCH, emdInterfaces["Web"]);
            this.addDevice(113, Constants.EMD_DEVICE_TYPE.SWITCH, emdInterfaces["Web"]);

            //iPhone 1 / Display --> Web
            this.addDevice(114, Constants.EMD_DEVICE_TYPE.DISPLAY, emdInterfaces["Web"]);
            this.addDevice(115, Constants.EMD_DEVICE_TYPE.DISPLAY, emdInterfaces["Web"]);

            //HW Dimmer 1 / PowerMeter 1 --> Rs232
            this.addDevice(10, Constants.EMD_DEVICE_TYPE.DIMMER, emdInterfaces["Rs232"]);
            this.addDevice(11, Constants.EMD_DEVICE_TYPE.POWER_METER, emdInterfaces["Rs232"]);
            //HW Dimmer 2 / PowerMeter 2 --> Rs232
            this.addDevice(20, Constants.EMD_DEVICE_TYPE.DIMMER, emdInterfaces["Rs232"]);
            this.addDevice(21, Constants.EMD_DEVICE_TYPE.POWER_METER, emdInterfaces["Rs232"]);

            //HW Switch 1 --> Rs232
            this.addDevice(50, Constants.EMD_DEVICE_TYPE.SWITCH, emdInterfaces["Rs232"]);

            //Display / Buttons --> Remoting
            this.addDevice(210, Constants.EMD_DEVICE_TYPE.BUTTON, emdInterfaces["Display"]);
            this.addDevice(211, Constants.EMD_DEVICE_TYPE.BUTTON, emdInterfaces["Display"]);
            this.addDevice(212, Constants.EMD_DEVICE_TYPE.SWITCH, emdInterfaces["Display"]);
            this.addDevice(213, Constants.EMD_DEVICE_TYPE.SWITCH, emdInterfaces["Display"]);

            //Display / Display --> Remoting
            this.addDevice(214, Constants.EMD_DEVICE_TYPE.DISPLAY, emdInterfaces["Display"]);
            this.addDevice(215, Constants.EMD_DEVICE_TYPE.DISPLAY, emdInterfaces["Display"]);

            //iPhone 1 / Button 1 --> Dimmer 1
            this.devManager.connectDevices(110, 10, true);
            //iPhone 1 / PowerMeter 1 --> Display 1
            this.devManager.connectDevices(11, 114, false);
            //iPhone 1 / Button 2 --> Dimmer 2
            this.devManager.connectDevices(111, 20, true);
            //iPhone 1 / PowerMeter 2 --> Display 2
            this.devManager.connectDevices(21, 115, false);
            
            //iPhone 1 / Button 3 --> Dimmer 1 & Dimmer 2
            this.devManager.connectDevices(112, 10, true);
            this.devManager.connectDevices(112, 20, true);

            //iPhone 1 / Button 4 --> Switch 1
            this.devManager.connectDevices(113, 50, true);


            //Display / Button 1 --> Dimmer 1
            this.devManager.connectDevices(210, 10, true);
            //Display / PowerMeter 1 --> Display 1
            this.devManager.connectDevices(11, 214, false);
            //Display / Button 2 --> Dimmer 2
            this.devManager.connectDevices(211, 20, true);
            //Display / PowerMeter 2 --> Display 2
            this.devManager.connectDevices(21, 215, false);

            //Display / Button 3 --> Dimmer 1 & Dimmer 2
            this.devManager.connectDevices(212, 10, true);
            this.devManager.connectDevices(212, 20, true);

            //Display / Button 4 --> Switch 1
            this.devManager.connectDevices(213, 50, true);
        }

        /// <summary>
        /// Keeps the application running
        /// </summary>
        public void start()
        {
            while (this.running)
            {
                Thread.Sleep(200);
            }
        }        
              
        /// <summary>
        /// Stops the application and ends the 
        /// send & receive threads
        /// </summary>
        public void stop()
        {
            // close the connections to all used emd interfaces
            for (int i = 0; i < emdInterfaces.Count; i++)
            {
                emdInterfaces.ElementAt(i).Value.closeConnection();
            }

            // clear the interface list
            emdInterfaces.Clear();
            this.running = false;

            //Debug.WriteLine("MAINCONTROLLER - Application stopped!");
        }

        /// <summary>
        /// Adds a new Device to the list of known in- and
        /// output devices.
        /// </summary>
        /// <param name="id">int id of the Device</param>
        /// <param name="type">int type of the Device</param>
        /// <param name="gateway">iEMDConnection where this device is connected to</param>
        public void addDevice(int id, Constants.EMD_DEVICE_TYPE type, iEMDConnection gateway)
        {
            Device d = null;

            ///Creates device according to the type
            switch (type)
            {
                case Constants.EMD_DEVICE_TYPE.BUTTON:
                    d = new Button(id, gateway);
                    break;
                case Constants.EMD_DEVICE_TYPE.SWITCH:
                    d = new EMDServer.Core.Devices.Switch(id, gateway);
                    break;
                case Constants.EMD_DEVICE_TYPE.FADER:
                    //d = d = new Fader(id, type, gateway);
                    break;
                case Constants.EMD_DEVICE_TYPE.DIMMER:
                    d = new Dimmer(id, gateway);
                    break;
                case Constants.EMD_DEVICE_TYPE.POWER_METER:
                    d = new PowerMeter(id, gateway);                    
                    break;
                case Constants.EMD_DEVICE_TYPE.DISPLAY:
                    d = new Display(id, gateway);
                    // poll power meter from display to get actual power values
                    //((Display)d).startPowerValueRequest();
                    break;
                default:
                    break;
            }
             
            d.setMainController(this);
            devManager.addDevice(d);

            //Debug.WriteLine("MAINCONTROLLER - Added Device: ID=" + id + ", TYPE=" + type);
        }

        /// <summary>
        /// Removes a device from the system (device manager)
        /// </summary>
        /// <param name="id">int id of the device to remove</param>
        public void removeDevice(int id)
        {
            devManager.removeDevice(id);
        }

        /// <summary>
        /// Adds a Device to the list of Devices on which
        /// the dimm button has been pressed.
        /// </summary>
        /// <param name="id">int id of the Device</param>
        public void buttonPressed(int id)
        {
            devManager.addButtonPressedDevice(id);

            //Debug.WriteLine("MAINCONTROLLER - Button pressed: " + id);
        }

        /// <summary>
        /// Removes a Device from the list of Devices on which
        /// the dimm button has been pressed
        /// </summary>
        /// <param name="id"></param>
        public void buttonReleased(int id)
        {
            devManager.removeButtonPressedDevice(id);

            //Debug.WriteLine("MAINCONTROLLER - Button released: " + id);
        }

        /// <summary>
        /// Adds a Device from the list of Devices on which
        /// the dimm button has been pressed
        /// </summary>
        /// <param name="id"></param>
        public void toggleSwitch(int id)
        {
            devManager.ToggleSwitch(id);

            //Debug.WriteLine("MAINCONTROLLER - Switch toggled: " + id);
        }

        /// <summary>
        /// Sends the value to each output Device connected to the 
        /// input Device with the appropriate id
        /// </summary>
        /// <param name="c">Command to send</param> 
        public void sendCommand(Command c)
        {
            List<Device> connectedDevices = devManager.getConnectedDevices(c.Id);

            foreach (Device d in connectedDevices)
            {
                // change sender id to receiver id
                Command cmd = new Command((byte)d.DeviceId, c.Cmd, c.Val);

                // call the sendfunction on the referenced interface
                // of the receiver device
                d.Gateway.sendCommand(cmd);

                // synchronize internal value of the software device
                // object
                d.DeviceValue = c.getIntValue();

                //Debug.WriteLine("MAINCONTROLLER - Command sent: ID=" + d.DeviceId + ", CMD=" + (Constants.EMD_COMMAND)c.Cmd + ", VALUE=" + c.getIntValue());
            }
        }

        // DA ISCH EPIS NO NID SO WIES SELTI SII
        /// <summary>
        /// Synchronizes the internal condition of the input device software 
        /// objects to the value of the output device software object
        /// </summary>
        /// <param name="deviceId">int ID of the output device</param>
        public void synchronizeValuesOnConnectedInputDevices(int deviceId)
        {
            int valueToSynchronize = devManager.getDevice(deviceId).DeviceValue;
            List<Device> connectedDevices = devManager.getConnectedDevices(deviceId);            

            foreach (Device d in connectedDevices)
            {
                d.DeviceValue = valueToSynchronize;

                //Debug.WriteLine("MAINCONTROLLER - Values synchronized: ID=" + deviceId + ", VALUE=" + valueToSynchronize);
            }
        }

        #region Unused
        ///// <summary>
        ///// Sends the value to each output Device connected to the 
        ///// input Device with the appropriate id
        ///// </summary>
        ///// <param name="senderId">int id of the input Device</param>
        ///// <param name="cmd">int Command from the input Device</param>
        ///// <param name="value">byte[] array to send to the connected output Devices</param>
        //public void sendCommand(int senderId, int cmd, byte[] value)
        //{
        //    List<Device> connectedDevices = devManager.getConnectedDevices(senderId);

        //    foreach (Device d in connectedDevices)
        //    {
        //        // create command with receiver id and value
        //        Command c = new Command((byte)d.DeviceId, (byte)cmd, value);
        //        //emdInterface.sendCommand(cmd);

        //        // call the sendfunction on the referenced interface
        //        // of the receiver device
        //        d.Gateway.sendCommand(c);
        //        d.DeviceValue = Utils.getInstance().ConvertByteToDecimal(value);

        //        Debug.WriteLine("MAINCONTROLLER - Command sent: SID=" + senderId + ", RID=" + d.DeviceId + ", CMD=" + (Constants.EMD_COMMAND)cmd + " VALUE=" + value);
        //    }
        //}
        #endregion
        // ----------------------------------------
        #endregion

        #region Eventhandler
        // ----------------------------------------
        /// <summary>
        /// Issues an event as soon as a new command message
        /// has been arrived
        /// </summary>
        private void emdInterface_cmdReceived(object sender, CommandHandler cmdHandler) 
        {            
            // read and process command           
            cmdProcessor.processCommand(cmdHandler.cmd, (iEMDConnection)sender);

            //Debug.WriteLine("MAINCONTROLLER - New command received: " + cmdHandler.cmd);
        }
        // ----------------------------------------
        #endregion
    }
}
