//#ifdef BUILD_IEC

#include "iec.h"

#include <cstring>
#include <memory>

#include "../../include/debug.h"
//#include "../../include/pinmap.h"
#include "../../include/cbm_defines.h"
//#include "led.h"
//#include "led_strip.h"
//#include "protocol/cpbstandardserial.h"
#include "string_utils.h"
//#include "utils.h"

//using namespace Protocol;

systemBus IEC;

static void cbm_on_attention_isr_handler(void *arg)
{
    // systemBus *b = (systemBus *)arg;

    // //b->pull(PIN_IEC_SRQ);

    // // Go to listener mode and get command
    // b->release(PIN_IEC_CLK_OUT);
    // b->pull(PIN_IEC_DATA_OUT);

    // b->flags = CLEAR;
    // b->flags |= ATN_PULLED;
    // b->bus_state = BUS_ACTIVE;

    // //b->release(PIN_IEC_SRQ);
}

/**
 * Static callback function for the interrupt rate limiting timer. It sets the interruptProceed
 * flag to true. This is set to false when the interrupt is serviced.
 */
static void onTimer(void *info)
{
    // systemBus *parent = (systemBus *)info;
    // //portENTER_CRITICAL_ISR(&parent->timerMux);
    // parent->interruptSRQ = !parent->interruptSRQ;
    // //portEXIT_CRITICAL_ISR(&parent->timerMux);
}

static void ml_iec_intr_task(void* arg)
{
    // while ( true ) 
    // {
    //     // if ( IEC.enabled )
    //     // {
    //         IEC.service();
    //         if ( IEC.bus_state < BUS_ACTIVE )
    //             taskYIELD();
    //     // }
    // }
}

void init_pin(int pin)
{
    // gpio_set_direction(pin, GPIO_MODE_INPUT);
    // gpio_set_pull_mode(pin, GPIO_PULLUP_ONLY);
    // gpio_set_level(pin, LOW);
    // return;
}


// true => PULL => LOW
void systemBus::pull ( int _pin )
{
    // int _reg = GPIO_ENABLE_REG; 
    // if (_pin > 31) 
    // { 
    //     _reg = GPIO_ENABLE1_REG, _pin -= 32; 
    // } 
    // REG_SET_BIT(_reg, 1 << _pin); // GPIO_MODE_OUTPUT
}

// false => RELEASE => HIGH
void systemBus::release ( int _pin )
{
    // int _reg = GPIO_ENABLE_REG; 
    // if (_pin > 31) 
    // { 
    //     _reg = GPIO_ENABLE1_REG, _pin -= 32; 
    // } 
    // REG_CLR_BIT(_reg, 1 << _pin); // GPIO_MODE_INPUT
}

bool systemBus::status ( int _pin )
{
// #ifndef IEC_SPLIT_LINES
//     release ( _pin );
// #endif

//     return gpio_get_level ( ( gpio_num_t ) _pin ) ? RELEASED : PULLED;
}

// int IRAM_ATTR systemBus::status()
// {
//     int data = 0;

//     gpio_config_t io_config =
//     {
//         .pin_bit_mask = BIT(PIN_IEC_CLK_IN) | BIT(PIN_IEC_DATA_IN) | BIT(PIN_IEC_SRQ) | BIT(PIN_IEC_RESET),
//         .mode = GPIO_MODE_INPUT,
//         .pull_up_en = GPIO_PULLUP_ENABLE,
//         .intr_type = GPIO_INTR_DISABLE,
//     };

//     ESP_ERROR_CHECK(gpio_config(&io_config));
//     uint64_t io_data = REG_READ(GPIO_IN_REG);

//     //data |= (0 & (io_data & (1 << PIN_IEC_ATN)));
//     data |= (1 & (io_data & (1 << PIN_IEC_CLK_IN)));
//     data |= (2 & (io_data & (1 << PIN_IEC_DATA_IN)));
//     data |= (3 & (io_data & (1 << PIN_IEC_SRQ)));
//     data |= (4 & (io_data & (1 << PIN_IEC_RESET)));

//     return data;
// }

void systemBus::setup()
{
//     Debug_printf("IEC systemBus::setup()\r\n");

//     flags = CLEAR;
//     protocol = selectProtocol();
//     release(PIN_IEC_CLK_OUT);
//     release(PIN_IEC_DATA_OUT);
//     release(PIN_IEC_SRQ);

//     // initial pin modes in GPIO
//     init_pin(PIN_IEC_ATN);
//     init_pin(PIN_IEC_CLK_OUT);
//     init_pin(PIN_IEC_DATA_OUT);
//     init_pin(PIN_IEC_SRQ);
// #ifdef IEC_HAS_RESET
//     init_pin(PIN_IEC_RESET);
// #endif

//     // Start task
//     //xTaskCreate(ml_iec_intr_task, "ml_iec_intr_task", 2048, NULL, 10, NULL);
//     xTaskCreatePinnedToCore(ml_iec_intr_task, "ml_iec_intr_task", 4096, NULL, 20, NULL, 1);

//     // Setup interrupt for ATN
//     gpio_config_t io_conf = {
//         .pin_bit_mask = ( 1ULL << PIN_IEC_ATN ),    // bit mask of the pins that you want to set
//         .mode = GPIO_MODE_INPUT,                    // set as input mode
//         .pull_up_en = GPIO_PULLUP_DISABLE,          // disable pull-up mode
//         .pull_down_en = GPIO_PULLDOWN_DISABLE,      // disable pull-down mode
//         .intr_type = GPIO_INTR_NEGEDGE              // interrupt of falling edge
//     };
//     //configure GPIO with the given settings
//     gpio_config(&io_conf);
//     gpio_isr_handler_add((gpio_num_t)PIN_IEC_ATN, cbm_on_attention_isr_handler, this);

//     // Start SRQ timer service
//     timer_start();
}


void systemBus::service()
{
//     // pull( PIN_IEC_SRQ );

//     // Disable Interrupt
//     // gpio_intr_disable((gpio_num_t)PIN_IEC_ATN);

//     // TODO IMPLEMENT

//     if (bus_state < BUS_ACTIVE)
//     {
//         // Handle SRQ for devices
//         for (auto devicep : _daisyChain)
//             {
//             for (unsigned char i=0;i<16;i++)
//                 devicep->poll_interrupt(i);
//         }

//         return;
//     }

// #ifdef IEC_HAS_RESET

//     // Check if CBM is sending a reset (setting the RESET line high). This is typically
//     // when the CBM is reset itself. In this case, we are supposed to reset all states to initial.
//     bool pin_reset = status(PIN_IEC_RESET);
//     if (pin_reset == PULLED)
//     {
//         if (status(PIN_IEC_ATN) == PULLED)
//         {
//             // If RESET & ATN are both PULLED then CBM is off
//             bus_state = BUS_OFFLINE;
//             // gpio_intr_enable((gpio_num_t)PIN_IEC_ATN);
//             return;
//         }

//         Debug_printf("IEC Reset! reset[%d]\r\n", pin_reset);
//         data.init(); // Clear bus data
//         releaseLines();
//         bus_state = BUS_IDLE;
//         Debug_printv("bus init");

//         // Reset virtual devices
//         reset_all_our_devices();
//         // gpio_intr_enable((gpio_num_t)PIN_IEC_ATN);
//         return;
//     }

// #endif

//     // Command or Data Mode
//     do
//     {
//         // Exit if bus is offline
//         if (bus_state == BUS_OFFLINE)
//             break;

//         if (bus_state == BUS_ACTIVE)
//         {
//             pull ( PIN_IEC_SRQ );

//             //release ( PIN_IEC_CLK_OUT );
//             //pull ( PIN_IEC_DATA_OUT );

//             //flags = CLEAR;

//             // Read bus command bytes
//             //Debug_printv("command");
//             read_command();

//             release ( PIN_IEC_SRQ );
//         }

//         if (bus_state == BUS_PROCESS)
//         {
//             // Sometimes ATN isn't released immediately. Wait for ATN to be
//             // released before trying to read payload. Long ATN delay (>1.5ms)
//             // seems to occur more frequently with VIC-20.
//             // protocol->timeoutWait(PIN_IEC_ATN, RELEASED, FOREVER, false);

//             //Debug_printv("data");
//             pull ( PIN_IEC_SRQ );
//             if (data.secondary == IEC_OPEN || data.secondary == IEC_REOPEN)
//             {
//                 // Switch to detected protocol
//                 //pull ( PIN_IEC_SRQ );
//                 protocol = selectProtocol();
//                 //release ( PIN_IEC_SRQ );
//             }

//             // Data Mode - Get Command or Data
//             if (data.primary == IEC_LISTEN)
//             {
//                 //Debug_printv("calling deviceListen()\r\n");
//                 deviceListen();
//             }
//             else if (data.primary == IEC_TALK)
//             {
//                 //Debug_printv("calling deviceTalk()\r\n");
//                 deviceTalk();
//             }

//             // Queue control codes and command in specified device
//             device_state_t device_state = deviceById(data.device)->queue_command(data);

//             fnLedManager.set(eLed::LED_BUS, true);

//             //Debug_printv("bus[%d] device[%d]", bus_state, device_state);
//             device_state = deviceById(data.device)->process();
//             if ( device_state < DEVICE_ACTIVE )
//             {
//                 // for (auto devicep : _daisyChain)
//                 // {
//                 //     if ( devicep->device_state > DEVICE_IDLE )
//                 //         devicep->process();
//                 // }
//                 data.init();
//                 //Debug_printv("bus init");
//             }

//             //Debug_printv("bus[%d] device[%d] flags[%d]", bus_state, device_state, flags);
//             bus_state = BUS_IDLE;

//             // Switch back to standard serial
//             detected_protocol = PROTOCOL_SERIAL;
//             protocol = selectProtocol();
//             release ( PIN_IEC_SRQ );
//         }

//         if ( status ( PIN_IEC_ATN ) )
//             bus_state = BUS_ACTIVE;

//     } while( bus_state > BUS_IDLE );

//     // Cleanup and Re-enable Interrupt
//     releaseLines();
//     //gpio_intr_enable((gpio_num_t)PIN_IEC_ATN);

//     //Debug_printv ( "primary[%.2X] secondary[%.2X] bus[%d] flags[%d]", data.primary, data.secondary, bus_state, flags );
//     //Debug_printv ( "device[%d] channel[%d]", data.device, data.channel);

//     Debug_printv("exit");
//     Debug_printv("bus[%d] flags[%d]", bus_state, flags);
//     //release( PIN_IEC_SRQ );

//     //fnLedStrip.stopRainbow();
//     //fnLedManager.set(eLed::LED_BUS, false);
}

void systemBus::read_command()
{
//     // ATN was pulled, read bus command bytes
//     // Sometimes the C64 pulls ATN but doesn't pull CLOCK right away
//     //pull( PIN_IEC_SRQ );
//     if ( protocol->timeoutWait ( PIN_IEC_CLK_IN, PULLED, TIMING_DELAY, false ) == TIMED_OUT )
//     {
//         Debug_printv ( "ATN/Clock delay" );
//         return; // return error because timeout
//     }
//     //release( PIN_IEC_SRQ );

//     //pull( PIN_IEC_SRQ );
//     uint8_t c = receiveByte();
//     //release( PIN_IEC_SRQ );

//     // Check for error
//     if ( flags & ERROR )
//     {
//         Debug_printv("Error reading command. flags[%d]", flags);
//         if (c == 0xFFFFFFFF)
//             bus_state = BUS_OFFLINE;
//         else
//             bus_state = BUS_ERROR;
//     }
//     else if ( flags & EMPTY_STREAM)
//     {
//         bus_state = BUS_IDLE;
//     }
//     else
//     {
//         if ( flags & JIFFYDOS_ACTIVE )
//         {
//             Debug_printf("   IEC: [JD][%.2X]", c);
//             detected_protocol = PROTOCOL_JIFFYDOS;
//         }
//         else
//         {
//             Debug_printf("   IEC: [%.2X]", c);
//         }

//         // Decode command byte
//         uint8_t command = c & 0x60;
//         if (c == IEC_UNLISTEN)
//             command = IEC_UNLISTEN;
//         if (c == IEC_UNTALK)
//             command = IEC_UNTALK;

//         //Debug_printv ( "device[%d] channel[%d]", data.device, data.channel);
//         //Debug_printv ("command[%.2X]", command);

//         switch (command)
//         {
//         // case IEC_GLOBAL:
//         //     data.primary = IEC_GLOBAL;
//         //     data.device = c ^ IEC_GLOBAL;
//         //     bus_state = BUS_IDLE;
//         //     Debug_printf(" (00 GLOBAL %.2d COMMAND)\r\n", data.device);
//         //     break;

//         case IEC_LISTEN:
//             data.primary = IEC_LISTEN;
//             data.device = c ^ IEC_LISTEN;
//             data.secondary = IEC_REOPEN; // Default secondary command
//             data.channel = CHANNEL_COMMAND;  // Default channel
//             data.payload = "";
//             bus_state = BUS_ACTIVE;
//             Debug_printf(" (20 LISTEN %.2d DEVICE)\r\n", data.device);
//             break;

//         case IEC_UNLISTEN:
//             data.primary = IEC_UNLISTEN;
//             bus_state = BUS_PROCESS;
//             Debug_printf(" (3F UNLISTEN)\r\n");
//             break;

//         case IEC_TALK:
//             data.primary = IEC_TALK;
//             data.device = c ^ IEC_TALK;
//             data.secondary = IEC_REOPEN; // Default secondary command
//             data.channel = CHANNEL_COMMAND;  // Default channel
//             bus_state = BUS_ACTIVE;
//             Debug_printf(" (40 TALK   %.2d DEVICE)\r\n", data.device);
//             break;

//         case IEC_UNTALK:
//             data.primary = IEC_UNTALK;
//             data.secondary = 0x00;
//             bus_state = BUS_PROCESS;
//             Debug_printf(" (5F UNTALK)\r\n");
//             break;

//         default:

//             //pull ( PIN_IEC_SRQ );
//             std::string secondary;
//             bus_state = BUS_PROCESS;

//             command = c & 0xF0;
//             switch ( command )
//             {
//             case IEC_OPEN:
//                 data.secondary = IEC_OPEN;
//                 data.channel = c ^ IEC_OPEN;
//                 secondary = "OPEN";
//                 break;

//             case IEC_REOPEN:
//                 data.secondary = IEC_REOPEN;
//                 data.channel = c ^ IEC_REOPEN;
//                 secondary = "DATA";
//                 break;

//             case IEC_CLOSE:
//                 data.secondary = IEC_CLOSE;
//                 data.channel = c ^ IEC_CLOSE;
//                 secondary = "CLOSE";
//                 break;

//             default:
//                 bus_state = BUS_IDLE;
//             }

//             // *** IMPORTANT! This helps keep us in sync!
//             protocol->wait( TIMING_SYNC, false);
//             //release ( PIN_IEC_SRQ );

//             Debug_printf(" (%.2X %s  %.2d CHANNEL)\r\n", data.secondary, secondary.c_str(), data.channel);
//         }
//     }

//     // Is this command for us?
//     if ( !isDeviceEnabled( data.device ) )
//     // if (!deviceById(data.device) || !deviceById(data.device)->device_active)
//     {
//         bus_state = BUS_IDLE;
//     }

//     // If the bus is idle then release the lines
//     if ( bus_state < BUS_ACTIVE )
//     {
//         data.init();
//         releaseLines();
//         return;
//     }

// #ifdef PARALLEL_BUS
//     // Switch to Parallel if detected
//     if ( PARALLEL.bus_state == PBUS_PROCESS )
//     {
//         if ( data.primary == IEC_LISTEN || data.primary == IEC_TALK )
//             detected_protocol = PROTOCOL_SPEEDDOS;
//         else if ( data.primary == IEC_OPEN || data.primary == IEC_REOPEN )
//             detected_protocol = PROTOCOL_DOLPHINDOS;

//         // Switch to parallel protocol
//         protocol = selectProtocol();

//         if ( data.primary == IEC_LISTEN )
//             PARALLEL.setMode( MODE_RECEIVE );
//         else
//             PARALLEL.setMode( MODE_SEND );

//         // Acknowledge parallel mode
//         PARALLEL.handShake();
//     }
// #endif

//     //Debug_printv ( "code[%.2X] primary[%.2X] secondary[%.2X] bus[%d] flags[%d]", c, data.primary, data.secondary, bus_state, flags );
//     //Debug_printv ( "device[%d] channel[%d]", data.device, data.channel);

//     // Delay to stabalize bus
//     // protocol->wait( TIMING_STABLE, false );

//     //release( PIN_IEC_SRQ );
}

void systemBus::read_payload()
{
    // // Record the command string until ATN is PULLED
    // std::string listen_command = "";

    // // ATN might get pulled right away if there is no command string to send
    // //pull ( PIN_IEC_SRQ );

    // // Sometimes ATN isn't released immediately. Wait for ATN to be
    // // released before trying to read payload. Long ATN delay (>1.5ms)
    // // seems to occur more frequently with VIC-20.
    // protocol->timeoutWait(PIN_IEC_ATN, RELEASED, FOREVER, false);

    // while (IEC.status(PIN_IEC_ATN) != PULLED)
    // {
    //     //pull ( PIN_IEC_SRQ );
    //     int8_t c = protocol->receiveByte();
    //     //Debug_printv("c[%2X]", c);
    //     //release ( PIN_IEC_SRQ );

    //     if (flags & EMPTY_STREAM || flags & ERROR)
    //     {
    //         Debug_printv("flags[%2X]", flags);
    //         bus_state = BUS_ERROR;
    //         return;
    //     }

    //     if (c != 0xFFFFFFFF && c != 0x0D)
    //     {
    //         listen_command += (uint8_t)c;
    //     }

    //     if (flags & EOI_RECVD)
    //     {
    //         data.payload = listen_command;
    //         break;
    //     }
    // }

    // bus_state = BUS_IDLE;
}

/**
 * Start the Interrupt rate limiting timer
 */
void systemBus::timer_start()
{
    // esp_timer_create_args_t tcfg;
    // tcfg.arg = this;
    // tcfg.callback = onTimer;
    // tcfg.dispatch_method = esp_timer_dispatch_t::ESP_TIMER_TASK;
    // tcfg.name = nullptr;
    // esp_timer_create(&tcfg, &rateTimerHandle);
    // esp_timer_start_periodic(rateTimerHandle, timerRate * 1000);
}

/**
 * Stop the Interrupt rate limiting timer
 */
void systemBus::timer_stop()
{
    // // Delete existing timer
    // if (rateTimerHandle != nullptr)
    // {
    //     Debug_println("Deleting existing rateTimer\r\n");
    //     esp_timer_stop(rateTimerHandle);
    //     esp_timer_delete(rateTimerHandle);
    //     rateTimerHandle = nullptr;
    // }
}

std::shared_ptr<std::string> systemBus::selectProtocol() 
{
//     //Debug_printv("protocol[%d]", detected_protocol);
    
//     switch(detected_protocol)
//     {
// #ifdef MEATLOAF_MAX
//         case PROTOCOL_SAUCEDOS:
//         {
//             auto p = std::make_shared<SauceDOS>();
//             return std::static_pointer_cast<IECProtocol>(p);
//         }
// #endif
//         case PROTOCOL_JIFFYDOS:
//         {
//             auto p = std::make_shared<JiffyDOS>();
//             return std::static_pointer_cast<IECProtocol>(p);
//         }
// #ifdef PARALLEL_BUS
//         case PROTOCOL_DOLPHINDOS:
//         {
//             auto p = std::make_shared<DolphinDOS>();
//             return std::static_pointer_cast<IECProtocol>(p);
//         }
// #endif
//         default:
//         {
// #ifdef PARALLEL_BUS
//             PARALLEL.bus_state = PBUS_IDLE;
// #endif
//             auto p = std::make_shared<CPBStandardSerial>();
//             return std::static_pointer_cast<IECProtocol>(p);
//         }
//     }
}

systemBus virtualDevice::get_bus()
{
    return IEC;
}

device_state_t virtualDevice::process()
{
    // switch ((bus_command_t)commanddata.primary)
    // {
    // case bus_command_t::IEC_LISTEN:
    //     device_state = DEVICE_LISTEN;
    //     break;
    // case bus_command_t::IEC_UNLISTEN:
    //     device_state = DEVICE_PROCESS;
    //     break;
    // case bus_command_t::IEC_TALK:
    //     device_state = DEVICE_TALK;
    //     break;
    // default:
    //     break;
    // }

    // switch ((bus_command_t)commanddata.secondary)
    // {
    // case bus_command_t::IEC_OPEN:
    //     payload = commanddata.payload;
    //     break;
    // case bus_command_t::IEC_CLOSE:
    //     payload.clear();
    //     std::queue<std::string>().swap(response_queue);
    //     pt.clear();
    //     pt.shrink_to_fit();
    //     break;
    // case bus_command_t::IEC_REOPEN:
    //     if (device_state == DEVICE_TALK)
    //     {
    //     }
    //     else if (device_state == DEVICE_LISTEN)
    //     {
    //         payload = commanddata.payload;
    //     }
    //     break;
    // default:
    //     break;
    // }

    // return device_state;
}

void virtualDevice::iec_talk_command_buffer_status()
{
    // char reply[80];
    // std::string s;

    // //fnSystem.delay_microseconds(100);

    // if (!status_override.empty())
    // {
    //     Debug_printv("sending explicit response.");
    //     IEC.sendBytes(status_override, true);
    //     status_override.clear();
    //     status_override.shrink_to_fit();
    // }
    // else
    // {
    //     snprintf(reply, 80, "%u,%s,%u,%u", iecStatus.error, iecStatus.msg.c_str(), iecStatus.connected, iecStatus.channel);
    //     s = std::string(reply);
    //     // s = mstr::toPETSCII2(s);
    //     Debug_printv("sending status: %s\r\n", reply);
    //     IEC.sendBytes(s, true);
    // }
}

void virtualDevice::dumpData()
{
    Debug_printf("%9s: %02X\r\n", "Primary", commanddata.primary);
    Debug_printf("%9s: %02u\r\n", "Device", commanddata.device);
    Debug_printf("%9s: %02X\r\n", "Secondary", commanddata.secondary);
    Debug_printf("%9s: %02u\r\n", "Channel", commanddata.channel);
    Debug_printf("%9s: %s\r\n", "Payload", commanddata.payload.c_str());
}

void systemBus::assert_interrupt()
{
    // if (interruptSRQ)
    //     IEC.pull(PIN_IEC_SRQ);
    // else
    //     IEC.release(PIN_IEC_SRQ);
}

int8_t systemBus::receiveByte()
{
    int8_t b = 0; //protocol->receiveByte();
#ifdef DATA_STREAM
    Debug_printf("%.2X ", (uint8_t)b);
#endif
    if (b == -1)
    {
        if (!(IEC.flags & ATN_PULLED))
        {
            IEC.flags |= ERROR;
            Debug_printv("error");
        }
    }
    return b;
}

std::string systemBus::receiveBytes()
{
    std::string s;

    // do
    // {
    //     int8_t b = receiveByte();
    //     if(b > -1)
    //         s += b;
    // }while(!(flags & EOI_RECVD));
    return s;
}

bool systemBus::sendByte(const char c, bool eoi)
{
    // if (!protocol->sendByte(c, eoi))
    // {
    //     if (!(IEC.flags & ATN_PULLED))
    //     {
    //         IEC.flags |= ERROR;
    //         Debug_printv("error");
    //         return false;
    //     }
    // }

//#ifdef DATA_STREAM
    if (eoi)
        Debug_printf("%.2X[eoi] ", c);
    else
        Debug_printf("%.2X ", c);
//#endif

    return true;
}

bool systemBus::sendBytes(const char *buf, size_t len, bool eoi)
{
    bool success = false;

    for (size_t i = 0; i < len; i++)
    {
        if (i == (len - 1) && eoi)
            success = sendByte(buf[i], true);
        else
            success = sendByte(buf[i], false);
    }

    return success;
}

bool systemBus::sendBytes(std::string s, bool eoi)
{
    return sendBytes(s.c_str(), s.size(), eoi);
}

void systemBus::process_cmd()
{
    // fnLedManager.set(eLed::LED_BUS, true);

    // TODO implement

    // fnLedManager.set(eLed::LED_BUS, false);
}

void systemBus::process_queue()
{
    // TODO IMPLEMENT
}

void systemBus::deviceListen()
{
    // // If the command is SECONDARY and it is not to expect just a small command on the command channel, then
    // // we're into something more heavy. Otherwise read it all out right here until UNLISTEN is received.
    // if (data.secondary == IEC_REOPEN && data.channel != CHANNEL_COMMAND)
    // {
    //     // A heapload of data might come now, too big for this context to handle so the caller handles this, we're done here.
    //     // Debug_printf(" (%.2X SECONDARY) (%.2X CHANNEL)\r\n", data.primary, data.channel);
    //     Debug_printf("REOPEN on non-command channel.\r\n");
    //     bus_state = BUS_ACTIVE;
    // }

    // // OPEN or DATA
    // else if (data.secondary == IEC_OPEN || data.secondary == IEC_REOPEN)
    // {
    //     read_payload();
    //     Debug_printf("{%s}\r\n", data.payload.c_str());
    // }

    // // CLOSE Named Channel
    // else if (data.secondary == IEC_CLOSE)
    // {
    //     // Debug_printf(" (E0 CLOSE) (%d CHANNEL)\r\n", data.channel);
    //     bus_state = BUS_PROCESS;
    // }

    // // Unknown
    // else
    // {
    //     Debug_printf(" OTHER (%.2X COMMAND) (%.2X CHANNEL) ", data.secondary, data.channel);
    //     bus_state = BUS_ERROR;
    // }
}

void systemBus::deviceTalk(void)
{
    // // Now do bus turnaround
    // //pull(PIN_IEC_SRQ);
    // if (!turnAround())
    // {
    //     Debug_printv("error flags[%d]", flags);
    //     bus_state = BUS_ERROR;
    //     return;
    // }
    // //release(PIN_IEC_SRQ);

    // We have recieved a CMD and we should talk now:
    bus_state = BUS_PROCESS;
}

bool systemBus::turnAround()
{
    /*
    TURNAROUND
    An unusual sequence takes place following ATN if the computer wishes the remote device to
    become a talker. This will usually take place only after a Talk command has been sent.
    Immediately after ATN is RELEASED, the selected device will be behaving like a listener. After all, it's
    been listening during the ATN cycle, and the computer has been a talker. At this instant, we
    have "wrong way" logic; the device is holding down the Data line, and the computer is holding the
    Clock line. We must turn this around. Here's the sequence:
    the computer quickly realizes what's going on, and pulls the Data line to true (it's already there), as
    well as releasing the Clock line to false. The device waits for this: when it sees the Clock line go
    true [sic], it releases the Data line (which stays true anyway since the computer is now holding it down)
    and then pulls down the Clock line. We're now in our starting position, with the talker (that's the
    device) holding the Clock true, and the listener (the computer) holding the Data line true. The
    computer watches for this state; only when it has gone through the cycle correctly will it be ready
    to receive data. And data will be signalled, of course, with the usual sequence: the talker releases
    the Clock line to signal that it's ready to send.
    */
    // Debug_printf("IEC turnAround: ");

    // // Wait until the computer releases the ATN line
    // if (protocol->timeoutWait(PIN_IEC_ATN, RELEASED, FOREVER) == TIMED_OUT)
    // {
    //     Debug_printf("Wait until the computer releases the ATN line");
    //     flags |= ERROR;
    //     return false; // return error because timeout
    // }
    // release ( PIN_IEC_DATA_OUT );

    // // Delay after ATN is RELEASED
    // protocol->wait( ( TIMING_Ttk + TIMING_Tda ), 0, false );
    // pull ( PIN_IEC_CLK_OUT );

    // // Debug_println("turnaround complete");
    return true;
} // turnAround

void systemBus::reset_all_our_devices()
{
    // TODO iterate through our bus and send reset to each device.
}

void systemBus::setBitTiming(std::string set, int p1, int p2, int p3, int p4)
{
    //  uint8_t i = 0; // Send
    // if (mstr::equals(set, "r")) i = 1;
    // if (p1) protocol->bit_pair_timing[i][0] = p1;
    // if (p2) protocol->bit_pair_timing[i][1] = p2;
    // if (p3) protocol->bit_pair_timing[i][2] = p3;
    // if (p4) protocol->bit_pair_timing[i][3] = p4;

    // Debug_printv("i[%d] timing[%d][%d][%d][%d]", i,
    //                 protocol->bit_pair_timing[i][0], 
    //                 protocol->bit_pair_timing[i][1], 
    //                 protocol->bit_pair_timing[i][2], 
    //                 protocol->bit_pair_timing[i][3]);
}

void systemBus::releaseLines(bool wait)
{
    // // Release lines
    // release(PIN_IEC_CLK_OUT);
    // release(PIN_IEC_DATA_OUT);

    // // Wait for ATN to release and quit
    // if (wait)
    // {
    //     Debug_printv("Waiting for ATN to release");
    //     protocol->timeoutWait(PIN_IEC_ATN, RELEASED, FOREVER);
    // }
}

void systemBus::senderTimeout()
{
    // releaseLines();
    // this->bus_state = BUS_ERROR;

    // protocol->wait( TIMING_EMPTY );
} // senderTimeout

void systemBus::addDevice(virtualDevice *pDevice, int device_id)
{
    if (!pDevice)
    {
        Debug_printf("systemBus::addDevice() pDevice == nullptr! returning.\r\n");
        return;
    }

    // TODO, add device shortcut pointer logic like others
    Debug_printf("Device #%02d Ready!\r\n", device_id);

    pDevice->_devnum = device_id;
    _daisyChain.push_front(pDevice);
    enabledDevices |= 1UL << device_id;
}

void systemBus::remDevice(virtualDevice *pDevice)
{
    if (!pDevice)
    {
        Debug_printf("system Bus::remDevice() pDevice == nullptr! returning\r\n");
        return;
    }

    _daisyChain.remove(pDevice);
    enabledDevices &= ~ ( 1UL << pDevice->_devnum );
}

bool systemBus::isDeviceEnabled ( const uint8_t device_id )
{
    return ( enabledDevices & ( 1 << device_id ) );
} // isDeviceEnabled



void systemBus::changeDeviceId(virtualDevice *pDevice, int device_id)
{
    if (!pDevice)
    {
        Debug_printf("systemBus::changeDeviceId() pDevice == nullptr! returning.\r\n");
        return;
    }

    for (auto devicep : _daisyChain)
    {
        if (devicep == pDevice)
            devicep->_devnum = device_id;
    }
}

virtualDevice *systemBus::deviceById(int device_id)
{
    for (auto devicep : _daisyChain)
    {
        if (devicep->_devnum == device_id)
            return devicep;
    }
    return nullptr;
}

void systemBus::shutdown()
{
    shuttingDown = true;

    for (auto devicep : _daisyChain)
    {
        Debug_printf("Shutting down device #%02d\r\n", devicep->id());
        devicep->shutdown();
    }
    Debug_printf("All devices shut down.\r\n");
}


//#endif /* BUILD_IEC */
