#
# This file is part of the BrainStem development package.
# See file LICENSE or go to https://acroname.com/software/brainstem-development-kit for full license details.
import brainstem
from brainstem.result import Result
from brainstem.link import aEtherConfig
from brainstem import _BS_C #BrainStem lib handle, for aProtocolDefs.h values only!

import sys

#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" example

#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().#


#Global stem object
stem = brainstem.stem.USBHub3p()
#If your module is different, replace it with the appropriate module
#Such as a USBCSwitch as shown below.
#stem = brainstem.stem.USBCSwitch()


def printCLIOutput():
    print("Commands: ");
    print("\t exit - Exits this application");
    print("\t voltage - Gets the system input voltage");
    print("\t led - Gets the current led value");
    print("\t led <value> - Sets the led to the defined value (0/1)");


def printProgramInfo():
    print("The aUSBHub3p server has been started.");
    print("You can now access this device from another process.");
    print("Build and run the \"client\" example to see it in action.");
    print("Additionally you can still issue commands to this process.");


def printFailedConnection():
    print("Failed to discover Module");
    print(" - Confirm device is connected to your machine");
    print(" - Confirm object type.  This examples default is \"aUSBHub3p\"");


def print_aEtherConfig():
    print(stem.getConfig().value)
    

def handleCLI():
    while True:
        s = input("cmd: ")

        args = s.split()

        if len(args) == 1:
            if args[0] == "exit":
                break;
            elif args[0] == "led":
                result = stem.system.getLED()
                print("Get LED value %d -  error: %d" % (result.value, result.error))
            elif args[0] == "voltage":
                result = stem.system.getInputVoltage()
                print("System Input Voltage: %0.3f -  error: %d" % (result.value/1000000, result.error))

        elif len(args) == 2:
            if args[0] == "led":
                e = stem.system.setLED(int(args[1]))
                print("Set LED value: %d - error: %d" % (int(args[1]), e))

        else:
            printCLIOutput()


def configure_aEther():
    #NOTE: Network configuration MUST be done before connecting.
    res = stem.getConfig()
    config = res.value

    #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 brainstem.discover.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 = _BS_C.LOCALHOST_IP_ADDRESS;

    e = stem.setConfig(config)
    if e is not Result.NO_ERROR:
        print("setConfig Error: ", e)


def main(argv):
    configure_aEther()
    e = stem.discoverAndConnect(brainstem.link.Spec.USB)
    if e == Result.NO_ERROR:
        printProgramInfo()
        print("")
        print_aEtherConfig()
        print("")
        printCLIOutput()

        handleCLI() #This function will block until "exit" is issued.

        stem.disconnect()
    else:
        printFailedConnection()

    


if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))
