//
//  main.cpp
//  BrainStem2-Client-Cpp-Example
//
//  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 BrainStem2-Server-Cpp-Example
//however, it will work with any application or even HubTool
//if configured properly.

//Note 1:
//  The actual server is created by the first process to connect to the device.
//  This means if you run this application first it would create/own
//  the server.  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.

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

///////////////////////////////////////////////////////////////////////////////
// Includes
///////////////////////////////////////////////////////////////////////////////

#include "BrainStem2/BrainStem-all.h"


///////////////////////////////////////////////////////////////////////////////
// Private Macros
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
// Private Types
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
// Private Function Prototypes
///////////////////////////////////////////////////////////////////////////////

void _configure_aEther(Acroname::BrainStem::Module& m);
void _print_aEtherConfig(Acroname::BrainStem::Module& m);
void _printProgramInfo(void);
void _printFailedConnection(void);

///////////////////////////////////////////////////////////////////////////////
// Public Data
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
// Private Data
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
// Public Function Implementations
///////////////////////////////////////////////////////////////////////////////


int main(int argc, const char * argv[]) {
    aErr e = aErrNone;
    
    aUSBHub3p stem;
    //If your module is different, replace it with the appropriate module
    //Such as a USBCSwitch as shown below.
    //aUSBCSwitch stem;

    _configure_aEther(stem);
    e = stem.discoverAndConnect(USB);
    
    if(aErrNone == e) {
        _printProgramInfo(); printf("\n");
        _print_aEtherConfig(stem);
        
        //Loop for a long time so that we can see the interactions
        //of the client and server.
        static const uint32_t NUM_LOOPS = 10000;
        for(uint32_t x = 0; x < NUM_LOOPS; x++) {
            if(x % 20 == 0) { printf("Loop: %d\n", x); }
            
            uint32_t voltage = 0;
            e = stem.system.getInputVoltage(&voltage);
            printf("System Input Voltage: %0.3f VDC - error: %d\n",
                   double(voltage)/1000000, e);
        }

        stem.disconnect();
    }
    else { _printFailedConnection(); }
    
    return 0;
}


///////////////////////////////////////////////////////////////////////////////
// Private Function Implementations
///////////////////////////////////////////////////////////////////////////////


void
_printProgramInfo(void) {
    printf("The aUSBHub3p client has been started.\n");
}

void
_configure_aEther(Acroname::BrainStem::Module& m) {
    //NOTE: Network configuration MUST be done before connecting.
    Acroname::BrainStem::aEtherConfig config;
    aErr err = m.getConfig(&config);
    if(err == 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 1:
        //  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.
        aErr err = m.setConfig(config);
        if(err) { printf("setConfig Error: %d\n", err); }
    }
}

void
_print_aEtherConfig(Acroname::BrainStem::Module& m) {
    Acroname::BrainStem::aEtherConfig config;
    aErr err = m.getConfig(&config);
    
    char sInterface[INET_ADDRSTRLEN];
    aDiscovery_ConvertIPv4Interface(config.networkInterface, sInterface, INET_ADDRSTRLEN);
    
    printf("Current aEther Config (error: %d):\n", err);
    printf("\t Local Only: %d\n"       \
           "\t Fallback: %d\n"        \
           "\t Server Enabled: %d\n"  \
           "\t Assigned Port: %d\n"     \
           "\t Network Interface: %d (%s)\n", \
           config.localOnly,
           config.fallback,
           config.enabled,
           config.assignedPort,
           config.networkInterface,
           sInterface);
}


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