//
//  Programe.cs
//  BrainStem2CLI-Server
//
//  Created by Acroname Inc. on 5/20/2023.
//  Copyright (c) 2023 Acroname Inc. All rights reserved.
//


//This example is intended to highlight the multi-process and
//network capabilities of BrainStem modules.  This application
//is intended to be used in conjunction with a "client" example (Any API)
//however, it will work with any application or even HubTool
//if configured properly.

//Note 1:
//  Although this is labeled as the "Server" application this
//  is a bit misleading.  The actual server is created by the
//  first process to connect to the device.
//  This means you could run the "Client" or even HubTool first in
//  which it would create/own the server and this application would be
//  a client.  This is intended to smooth over the user experience
//  but is important to remember when closing applications.  If
//  The "Server" application is closed it will result in the "Clients"
//  losing access to the device.

//Note 2:
//  This application was created with the aUSBHub3p object.  If
//  your devices differs (say a USBCSwitch) you will need to change
//  all instances of aUSBHub3p to aUSBCSwitch in order to connect to
//  the device.  This is a result of discoveryAndConnect as it will
//  only connect to a device that matches the object type.  It is
//  possible to use the generic "Module" object; however, this was avoided
//  in order to present a simplified example. For more connection types
//  see the "Discovery-and-Connection"

//Note 3:
//  If you intended to connect to a device that is not connected
//  to your local machine or on your local intranet (local network)
//  you will need to define a linkSpec with the intended ip and port
//  number of the device and then use "connectFromSpec".
//  Discovery works through broadcast and multicast packets;
//  neither of which work for the internet (global network).
//  For more connection types see the "Discovery-and-Connection"

//Note 4:
//  Additional network details are described in _configure_aEther().


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

using Acroname.BrainStem2CLI;

namespace BrainStem2CLI_USBHub3p
{
    class Program
    {


        static void Main(string[] args)
        {
            USBHub3p stem = new USBHub3p();
            //If your module is different, replace it with the appropriate module
            //Such as a USBCSwitch as shown below.
            //USBCSwitch stem = new USBCSwitch();

            ModuleClass m = stem.module;
            _configure_aEther(ref m);
            aErr err = stem.module.discoverAndConnect(m_linkType.USB, 0);

            if (err == aErr.aErrNone)
            {
                _printProgramInfo();
                _print_aEtherConfig(ref m);
                _printCLIOutput();


                while (_handleCLI(ref m)) { }
                stem.module.disconnect();
            }
            else
            {
                _printFailedConnection();
            }

        }


        static void _configure_aEther(ref ModuleClass m)
        {
            //NOTE: Network configuration MUST be done before connecting.
            aEtherConfig config = new aEtherConfig();
            aErr err = m.getConfig(config);
            if (err == aErr.aErrNone)
            {
                //Controls the exposure of the device.  By default,
                //the device is only exposed on the localhost.
                //True = localhost(default); False = Public;
                //config.localOnly = false;  //uncomment to test non-default values

                //Controls how strictly we honor the linkType (USB, NETWORK).
                //Fallback allows for a smoother user experience when getting
                //familiar with the device; however, it might be helpful to disable
                //this so that you can control who is the server and who is the client.
                //For instance if stem.discoverAndConnect(USB) fails it will automatically
                //try stem.discoverAndConnect(Network) and vise-versa.
                //True = fallback (default); False = NO fallback
                //config.fallback = false;  //uncomment to test non-default values

                //Controls if the Client-Server model is used.  If you prefer to restrict
                //the device to a single process you can disable this capability.
                //By default the stem is enabled for multi-process use.
                //True = Client-Server (default); False = Direct connection (not multi-process)
                //config.enabled = false;  //uncomment to test non-default values

                //Allows the user to select what network interface the stem will be exposed to.
                //Default = LOCALHOST_IP_ADDRESS; (0 is also accepted for LOCALHOST_IP_ADDRESS);
                //Available interfaces can be found with aDiscovery_GetIPv4Interfaces
                //NOTE: If config.localOnly == true; This value is ignored.
                //      If config.localOnly == false; The stem will automatically pick
                //      the highest priority network interface and will ignore 0 and
                //      LOCALHOST_IP_ADDRESS values. However, you may override this value.
                //config.networkInterface = LOCALHOST_IP_ADDRESS;

                //Apply the configuration.
                err = m.setConfig(config);
                if (err != aErr.aErrNone) { Console.WriteLine("setConfig Error: {0}", err); }
            }
        }

        static bool _handleCLI(ref ModuleClass m)
        {
            string s = Console.ReadLine();
            string[] words = s.Split(' ');

            bool commandFound = true;
            SystemClass sys = new SystemClass();
            sys.init(m, 0);

            if (words.Length == 1)
            {
                if (words[0] == "exit") { return false; }
                else if (words[0] == "led")
                {
                    byte led = 0;
                    aErr err = sys.getLED(ref led);
                    Console.WriteLine("Get LED value: {0} - error: {1}\n", led, err);
                }
                else if (words[0] == "voltage")
                {
                    UInt32 voltage = 0;
                    aErr err = sys.getInputVoltage(ref voltage);
                    Console.WriteLine("Voltage: {0,0:F4}", (double)voltage/1000000);
                }
                else { commandFound = false; }
            }
            else if (words.Length == 2)
            {
                if (words[0] == "led")
                {
                    byte led = Byte.Parse(words[1]);
                    aErr err = sys.setLED(led);
                    Console.WriteLine("Set LED value: {0} - error: {1}\n", led, err);
                }
                else { commandFound = false; }
            }
            else { commandFound = false; }

            if (!commandFound)
            {
                Console.WriteLine("Unknown command with ({0}) parameters\n", words.Length);
                _printCLIOutput();
            }

            return true;
        }


        static void _print_aEtherConfig(ref ModuleClass m)
        {
            aEtherConfig config = new aEtherConfig();
            aErr err = m.getConfig(config);

            Console.WriteLine("Current aEther Config (error: {0}):", err);
            Console.WriteLine("\t Local Only: {0}", config.localOnly);
            Console.WriteLine("\t Fallback: {0}", config.fallback);
            Console.WriteLine("\t Server Enabled: {0}", config.enabled);
            Console.WriteLine("\t Assigned Port: {0}", config.assignedPort);
            Console.WriteLine("\t Network Interface: {0}", config.networkInterface);
        }


        static void _printProgramInfo()
        {
            Console.WriteLine("The aUSBHub3p server has been started.");
            Console.WriteLine("You can now access this device from another process.");
            Console.WriteLine("Build and run the \"client\" example to see it in action.");
            Console.WriteLine("Additionally you can still issue commands to this process.");
        }

        static void _printCLIOutput()
        {
            Console.WriteLine("Commands: ");
            Console.WriteLine("\t exit - Exits this application");
            Console.WriteLine("\t voltage - Gets the system input voltage");
            Console.WriteLine("\t led - Gets the current led value ");
            Console.WriteLine("\t led <value> - Sets the led to the defined value (0/1)");
        }

        static void _printFailedConnection()
        {
            Console.WriteLine("Failed to discover Module");
            Console.WriteLine(" - Confirm device is connected to your machine");
            Console.WriteLine(" - Confirm object type.  This examples default is \"aUSBHub3p\"");
        }


    }
}

