// =================================================================================================
// eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus
//               MIT license - see license.md for details
// =================================================================================================
#include "ModbusClient.h"
#undef LOCAL_LOG_LEVEL
#include "Logging.h"

uint16_t ModbusClient::instanceCounter = 0;

// Default constructor: set the default timeout to 2000ms, zero out all other 
ModbusClient::ModbusClient() :
  messageCount(0),
  errorCount(0),
  #if HAS_FREERTOS
  worker(NULL),
  #elif IS_LINUX
  worker(0),
  #endif
  onData(nullptr),
  onError(nullptr),
  onResponse(nullptr) { instanceCounter++; }

// onDataHandler: register callback for data responses
bool ModbusClient::onDataHandler(MBOnData handler) {
  if (onData) {
    LOG_W("onData handler was already claimed\n");
  } else if (onResponse) {
    LOG_E("onData handler is unavailable with an onResponse handler\n");
    return false;
  }
  onData = handler;
  return true;
}

// onErrorHandler: register callback for error responses
bool ModbusClient::onErrorHandler(MBOnError handler) {
  if (onError) {
    LOG_W("onError handler was already claimed\n");
  } else if (onResponse) {
    LOG_E("onError handler is unavailable with an onResponse handler\n");
    return false;
  } 
  onError = handler;
  return true;
}

// onResponseHandler: register callback for error responses
bool ModbusClient::onResponseHandler(MBOnResponse handler) {
  if (onError || onData) {
    LOG_E("onResponse handler is unavailable with an onData or onError handler\n");
    return false;
  } 
  onResponse = handler;
  return true;
}

// getMessageCount: return message counter value
uint32_t ModbusClient::getMessageCount() {
  return messageCount;
}

// getErrorCount: return error counter value
uint32_t ModbusClient::getErrorCount() {
  return errorCount;
}

// resetCounts: Set both message and error counts to zero
void ModbusClient::resetCounts() {
  {
    LOCK_GUARD(cntLock, countAccessM);
    messageCount = 0;
    errorCount = 0;
  }
}

// waitSync: wait for response on syncRequest to arrive
ModbusMessage ModbusClient::waitSync(uint8_t serverID, uint8_t functionCode, uint32_t token) {
  ModbusMessage response;
  unsigned long lostPatience = millis();
 
  // Default response is TIMEOUT
  response.setError(serverID, functionCode, TIMEOUT);

  // Loop 60 seconds, if unlucky
  while (millis() - lostPatience < 60000) {
    {
      LOCK_GUARD(lg, syncRespM);
      // Look for the token
      auto sR = syncResponse.find(token);
      // Is it there?
      if (sR != syncResponse.end()) {
        // Yes. get the response, delete it from the map and return
        response = sR->second;
        syncResponse.erase(sR);
        break;
      }
    }
    // Give the watchdog time to act
    delay(10);
  }
  return response;
}
