using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Acroname.BrainStem2CLI;

namespace BrainStem2CLI_Discovery_And_Connection {
    class Program {

        //Main:
        ///////////////////////////////////////////////////////////////////////
        //This example shows the various ways to discover and connect to BrainStem
        //modules/devices.
        //NOTE: Not all functions will be successful.  Many of the examples will
        //      require slight modification in order to work with your device.
        //      Please refer to the individual notes/comments in and around
        //      each function.
        ///////////////////////////////////////////////////////////////////////
        static void Main(string[] args) {

            discoverAndConnect_Example();

            sDiscover_Example();

            findFirstModule_Example();

            findModule_Example();

            connectFromSpec_Example();

            connectThroughLinkModule_Example();

            Console.WriteLine("Press enter to exit");
            Console.ReadLine();
        }
        ///////////////////////////////////////////////////////////////////////



        // discoverAndConnect_Example:
        ///////////////////////////////////////////////////////////////////////
        // This is the most common form of connection. The discovery and connection
        // process is enveloped into a single function.
        //
        // PITFALL: This function requires that the object type matches the device
        //          you are attempting to connect too and will likely require modification
        //          in order to work properly.
        ///////////////////////////////////////////////////////////////////////
        static void discoverAndConnect_Example() {

            aErr err = aErr.aErrNone;
            UInt32 serial_number = 0;

            //TODO:
            //Uncomment the specific device you are attempting to connect too.

            //USBStem stem = new USBStem();
            //EtherStem stem = new EtherStem();
            //MTMUSBStem stem = new MTMUSBStem();
            //MTMEtherStem stem = new MTMEtherStem();
            //MTMIOSerial stem = new MTMIOSerial();
            //MTMPM1 stem = new MTMPM1();
            //MTMRelay stem = new MTMRelay();
            //MTMDAQ1 stem = new MTMDAQ1();
            //MTMDAQ2 stem = new MTMDAQ2();
            //USBHub2x4 stem = new USBHub2x4();
            USBHub3p stem = new USBHub3p();
            //USBCSwitch  stem = new USBCSwitch();

            //When no serial number is provided discoverAndConnect will attempt to
            //connect to the first module it finds.  If multiple BrainStem devices
            //are connected to your machine it is unknown which device will be
            //discovered first.
            //Under the hood this function uses findFirstModule()
            Console.WriteLine("Example: discoverAndConnect(USB);");
            err = stem.module.discoverAndConnect(m_linkType.USB);
            if (err != aErr.aErrNone) {
                Console.WriteLine("Unable to find BrainStem Module: {0}, Error: {1}", stem.ToString(), err);
                Console.WriteLine("Are you using the Module/Object type?");
            }
            else {
                Console.WriteLine("Found and Connected to a {0}", stem.ToString());
                stem.system.getSerialNumber(ref serial_number);
            }
            stem.module.disconnect();
            Console.WriteLine("");

            // Put the serial number of your device here.
            UInt32 user_serial_number = serial_number;

            //discoverAndConnect has an overload which accepts a Serial Number.
            //The example immediately above will attempt to fetch the serial number
            //and attempt to use it in this example. Feel free to drop in the
            //serial number of your device.
            //Under the hood this function uses a combination of sDiscover() and
            //connectFromSpec().
            Console.WriteLine("Example: discoverAndConnect(USB, Serial_Number);");
            err = stem.module.discoverAndConnect(m_linkType.USB, user_serial_number);
            if (err != aErr.aErrNone) {
                Console.WriteLine("Unable to find BrainStem Module: {0}, Serial Number: {1:X}, Error: {2}", stem.ToString(), user_serial_number, err);
                Console.WriteLine("Are you using the Module/Object type?");
            }
            else {
                Console.WriteLine("Found and Connected to a {0}", stem.ToString());
            }
            stem.module.disconnect();

            Console.WriteLine("Finished with discoverAndConnect_Example.");
            Console.WriteLine("--------------------------------------------------------");

        }//End discoverAndConnect_Example()
        ///////////////////////////////////////////////////////////////////////



        // sDiscover_Example:
        ///////////////////////////////////////////////////////////////////////
        // Highlights how to discover and interrogate multiple BrainStem devices
        // without connecting to them.
        // This is especially helpful for device agnostic applications.
        ///////////////////////////////////////////////////////////////////////
        static void sDiscover_Example() {

            Console.WriteLine("Example: ModuleClass.sDiscover(specList);");

            List<m_linkSpec> specList = new List<m_linkSpec>();

            //sDiscover: USB
            ///////////////////////////////////////////////////////////////////
            specList.Clear();
            ModuleClass.sDiscover(m_linkType.USB, specList);
            for (int i = 0; i < specList.Count(); i++) {
                Console.WriteLine("");
                Console.WriteLine("Model: {0}", specList[i].model);
                Console.WriteLine("Module: {0}", specList[i].module);
                Console.WriteLine("Serial Number: {0:X}", specList[i].serial_num);
                Console.WriteLine("Router: {0}", specList[i].router);
                Console.WriteLine("Router Serial Number: {0}", specList[i].router_serial_num);
                Console.WriteLine("USB ID: {0}", specList[i].t.usb.usb_id);
                Console.WriteLine("");
            }
            ///////////////////////////////////////////////////////////////////

            //sDiscover: TCPIP
            ///////////////////////////////////////////////////////////////////
            specList.Clear();
            ModuleClass.sDiscover(m_linkType.TCPIP, specList);
            for (int i = 0; i < specList.Count(); i++) {
                Console.WriteLine("");
                Console.WriteLine("Model: {0}", specList[i].model);
                Console.WriteLine("Module: {0}", specList[i].module);
                Console.WriteLine("Serial Number: {0:X}", specList[i].serial_num);
                Console.WriteLine("Router: {0}", specList[i].router);
                Console.WriteLine("Router Serial Number: {0}", specList[i].router_serial_num);
                Console.WriteLine("IP Address: {0}", specList[i].t.ip.ip_address);
                Console.WriteLine("Port: {0}", specList[i].t.ip.ip_port);
                Console.WriteLine("");
            }
            ///////////////////////////////////////////////////////////////////

            Console.WriteLine("Finished with sDiscover_Example.\n");
            Console.WriteLine("--------------------------------------------------------");
        }//End sDiscoverExample()
        ///////////////////////////////////////////////////////////////////////


        // findFirstModule_Example:
        ///////////////////////////////////////////////////////////////////////
        // This example is similar to Discover and Connect, except it connects
        // the first BrainStem it finds, rather than connecting to a specific
        // device type.
        ///////////////////////////////////////////////////////////////////////
        static void findFirstModule_Example() {

            m_linkSpec spec;

            Console.WriteLine("Example: findFirstModule(USB);");
            spec = new m_linkSpec();    //Reset
            spec = ModuleClass.findFirstModule(m_linkType.USB);
            if (spec.serial_num != 0) {
                Console.WriteLine("Model: {0}", spec.model);
                Console.WriteLine("Module: {0}", spec.module);
                Console.WriteLine("Serial Number: {0:X}", spec.serial_num);
                Console.WriteLine("Router: {0}", spec.router);
                Console.WriteLine("Router Serial Number: {0}", spec.router_serial_num);
                Console.WriteLine("USB ID: {0}", spec.t.usb.usb_id);
                Console.WriteLine("");
            }
            else { Console.WriteLine("No USB BrainStem device found"); }

            spec = new m_linkSpec();    //Reset

            Console.WriteLine("Example: findFirstModule(TCPIP);");
            spec = ModuleClass.findFirstModule(m_linkType.TCPIP);
            if (spec.serial_num != 0) {
                Console.WriteLine("Model: {0}", spec.model);
                Console.WriteLine("Module: {0}", spec.module);
                Console.WriteLine("Serial Number: {0:X}", spec.serial_num);
                Console.WriteLine("Router: {0}", spec.router);
                Console.WriteLine("Router Serial Number: {0}", spec.router_serial_num);
                Console.WriteLine("USB ID: {0}", spec.t.usb.usb_id);
                Console.WriteLine("");
            }
            else { Console.WriteLine("No TCPIP BrainStem device found"); }

            Console.WriteLine("Finished with findFirstModule_Example.");
            Console.WriteLine("--------------------------------------------------------");
        }//End findFirstModule_Example();
        ///////////////////////////////////////////////////////////////////////


        // findModule_Example:
        ///////////////////////////////////////////////////////////////////////
        // This example will connect to any BrainStem device given its serial
        // number. It will not connect without a SN.
        ///////////////////////////////////////////////////////////////////////
        static void findModule_Example() {

            //TODO:
            //Plug in the serial number of your device. This function will not be
            //successful in finding a BrainStem device otherwise.
            UInt32 serial_number = 0xFEEDBEEF; //Replace with your devices Serial Number.
            m_linkSpec spec;

            Console.WriteLine("Example: findModule(USB, Serial_Number);");
            spec = new m_linkSpec();    //Reset the spec object
            spec = ModuleClass.findModule(m_linkType.USB, serial_number);
            if (spec.serial_num != 0) {
                Console.WriteLine("Model: {0}", spec.model);
                Console.WriteLine("Module: {0}", spec.module);
                Console.WriteLine("Serial Number: {0:X}", spec.serial_num);
                Console.WriteLine("Router: {0}", spec.router);
                Console.WriteLine("Router Serial Number: {0}", spec.router_serial_num);
                Console.WriteLine("USB ID: {0}", spec.t.usb.usb_id);
                Console.WriteLine("");
            }
            else { Console.WriteLine("No USB BrainStem device with Serial Number: {0:X} was found.", serial_number); }

            //For TCP/IP devices.  Will not be successful with USB based devices.
            Console.WriteLine("Example: findModule(TCPIP, Serial_Number);");
            spec = new m_linkSpec();    //Reset the spec object
            spec = ModuleClass.findModule(m_linkType.TCPIP, serial_number);
            if (spec.serial_num != 0) {
                Console.WriteLine("Model: {0}", spec.model);
                Console.WriteLine("Module: {0}", spec.module);
                Console.WriteLine("Serial Number: {0:X}", spec.serial_num);
                Console.WriteLine("Router: {0}", spec.router);
                Console.WriteLine("Router Serial Number: {0}", spec.router_serial_num);
                Console.WriteLine("USB ID: {0}", spec.t.usb.usb_id);
                Console.WriteLine("");
            }
            else { Console.WriteLine("No TCPIP BrainStem device with Serial Number: {0:X} was found.", serial_number); }

            Console.WriteLine("Finished with findModule_Example.");
            Console.WriteLine("--------------------------------------------------------");
        }//End findModule_Example();
         ///////////////////////////////////////////////////////////////////////

        // connectFromSpec_Example:
        ///////////////////////////////////////////////////////////////////////
        // Many of the discovery functions will return a m_linkSpec object.
        // This function shows how to use that object to connect to a BrainStem
        // device.
        // The benefit  of this connection method is that it does not care
        // about which BrainStem object you use.
        // i.e. you can connect to a USBHub3p from a USBStem object. However,
        // the USBStem object does not possess a USB Entity and therefore  will
        // not be able to control the USBHub3p correctly. This is typically not
        // recommended.
        ///////////////////////////////////////////////////////////////////////
        static void connectFromSpec_Example() {

            aErr err = aErr.aErrNone;
            USBStem stem = new USBStem();

            Console.WriteLine("Example: connectFromSpec(linkSpec);");
            m_linkSpec spec = ModuleClass.findFirstModule(m_linkType.USB);
            if (spec.serial_num != 0) {

                err = stem.module.connectFromSpec(spec);
                if (err != aErr.aErrNone) {
                    Console.WriteLine("Unable to connect to BrianStem Module. Error: {0}", err);
                }
                else {
                    Console.WriteLine("Found and Connected to BrainStem Module");
                    stem.module.disconnect();
                }
            }
            else { Console.WriteLine("No BrainStem devices were found."); }

            Console.WriteLine("Finished with connectFromSpec_Example.");
            Console.WriteLine("--------------------------------------------------------");
        }//End connectFromSpec_Example()
        ///////////////////////////////////////////////////////////////////////



        // connectThroughLinkModule_Example():
        ///////////////////////////////////////////////////////////////////////
        // This function allows a device to share the connection of another device.
        // This feature is only available for Acroname's MTM and 40pin devices.
        //
        // In this example we have a MTMUSBStem and a MTMDAQ2 connected to a BrainStem
        // development board.  The board is powered and ONLY the MTMUSBStem is connected
        // to the computer via USB cable.  The MTMDAQ2 will connect to the PC through the
        // MTMUSBStem via the BrainStem Network (I2C) which is wired through the
        // development board.
        ///////////////////////////////////////////////////////////////////////
        static void connectThroughLinkModule_Example() {

            aErr err = aErr.aErrNone;

            // Create the devices required for this example
            MTMUSBStem mtmstem = new MTMUSBStem();
            MTMDAQ2 mtmdaq2 = new MTMDAQ2();

            err = mtmstem.module.discoverAndConnect(m_linkType.USB);
            if (err != aErr.aErrNone) {
                Console.WriteLine("Unable to connect to MTMUSBStem Module. Error: {0}", err);
            }
            else {
                Console.WriteLine("Found and Connected to MTMUSBStem Module");

                // Each module has a "router" address.  This address defines the I2C network.
                // By default this value is set to the devices module address.  In order
                // for devices to communicate on the BrainStem Network all devices must have
                // the router address of the link stem.  In this example the MTMUSBStem is the
                // the link stem.  When the routeToMe function is called the device will broadcast
                // to all devices on the network.  0 = default configuration, 1 = Instructs all modules
                // to change their router address to that of the broadcaster.
                err = mtmstem.system.routeToMe(1);
                if (err == aErr.aErrNone) {
                    // Now that the MTMUSBStem connection is up and running we can
                    // use its connection to connect to the MTMDAQ2
                    err = mtmdaq2.module.connectThroughLinkModule(mtmstem.module);
                    if (err != aErr.aErrNone) {
                        Console.WriteLine("Unable to connect to MTMDAQ2 Module. Error: {0}", err);
                    }
                    else {
                        Console.WriteLine("Connected to MTMDAQ2 Module");
                        byte LED;
                        string LEDStatus;

                        // Once connected you can use the devices normally.
                        LED = 0;
                        err = mtmstem.system.getLED(ref LED);
                        if (err == aErr.aErrNone) {
                            LEDStatus = (LED == 0 ? "Off" : "On");
                            Console.WriteLine("MTMUSBStem's User LED: {0}, Error: {1}", LEDStatus, err);
                        }

                        LED = 0;
                        err = mtmdaq2.system.getLED(ref LED);
                        if (err == aErr.aErrNone) {
                            LEDStatus = (LED == 0 ? "Off" : "On");
                            Console.WriteLine("MTMDAQ2's User LED: {0}, Error: {1}", LEDStatus, err);
                        }

                        // You should disconnect in the reverse order in which you connected.
                        mtmdaq2.module.disconnect();
                    }
                }
                //Reset all network routers back to their default configurations.
                mtmstem.system.routeToMe(0);
                mtmstem.module.disconnect();
            }
            Console.WriteLine("Finished with connectThroughLinkModule_Example.");
            Console.WriteLine("--------------------------------------------------------");
        }
    }
}
