#include "SlaveAgentHandlerOfAgent.h"

#include "instruction_set_architecture.h"
#include "printError.h"
#include <stdio.h>

#include <algorithm>

#define MAXNUMOF_MOUNTEDSENSORS 100

//#define PRINT

using namespace std;

SlaveAgentHandlerOfAgent ::SlaveAgentHandlerOfAgent() {
  initSlaveAgentHandler();
}

void SlaveAgentHandlerOfAgent ::initSlaveAgentHandler() {
  maxNumOfMountedSlaveAgents = MAXNUMOF_MOUNTEDSENSORS;
}

bool SlaveAgentHandlerOfAgent ::mount_slaveAgentIntoSlaveAgentSlot(
    Channel *inputPort) {
  SlaveAgentSlotOfAgent *slaveAgentSlotOfAgent = new SlaveAgentSlotOfAgent();
  if (slaveAgentSlotOfAgent != NULL) {
    if (slaveAgentSlotOfAgent->set_comPort(inputPort)) {
      if (vMountedSlaveAgents.size() < maxNumOfMountedSlaveAgents) {
        try {
          vMountedSlaveAgents.push_back(slaveAgentSlotOfAgent);
        } catch (bad_alloc &error) {
          printError("bad_alloc caught: ", error.what());
          delete slaveAgentSlotOfAgent;
          return false;
        }
      } else {
        printError("Max number of mounted slaveAgents is already reached!");
        delete slaveAgentSlotOfAgent;
        return false;
      }
      return true;
    } else {
      printError("Input port is no set!");
      vMountedSlaveAgents.pop_back(); // TODO: check if it is right?!?!
      delete slaveAgentSlotOfAgent;
      return false;
    }
  } else {
    printError("Couldn't create SlaveAgentSlot!");
    return false;
  }
}

// TODO: what to do when 2 slaveAgentSlots have the same inputPort??!!
SlaveAgentSlotOfAgent *
SlaveAgentHandlerOfAgent ::get_slaveAgentSlotAddress(Channel *inputPort) {
  for (auto &slaveAgentSlot : vMountedSlaveAgents) {
    if (slaveAgentSlot->get_comPort() == inputPort) {
      return slaveAgentSlot;
    }
  }
  return NULL;
}

// TODO: what to do when 2 slaveAgentSlots have the same inputPort??!!
// TODO: case if slot with comPort is not in this vector
unsigned int
SlaveAgentHandlerOfAgent ::get_slaveAgentSlotNumber(Channel *inputPort) {
  unsigned int slotNumber = 0;
  for (auto &slaveAgentSlot : vMountedSlaveAgents) {
    if (slaveAgentSlot->get_comPort() == inputPort) {
      return slotNumber;
    }
    slotNumber++;
  }
  return std::numeric_limits<int>::quiet_NaN();
}

// TODO: what to do when 2 slaveAgentSlots have the same historyModule??!!
SlaveAgentSlotOfAgent *SlaveAgentHandlerOfAgent ::get_slaveAgentSlotAddress(
    HistoryModule *historyModule) {
  for (auto &slaveAgentSlot : vMountedSlaveAgents) {
    if (slaveAgentSlot->get_historyModule() == historyModule) {
      return slaveAgentSlot;
    }
  }
  return NULL;
}

// TODO: what to do when 2 slaveAgentSlots have the same confidenceModule??!!
SlaveAgentSlotOfAgent *SlaveAgentHandlerOfAgent ::get_slaveAgentSlotAddress(
    ConfidenceModule *confidenceModule) {
  for (auto &slaveAgentSlot : vMountedSlaveAgents) {
    if (slaveAgentSlot->get_confidenceModule() == confidenceModule) {
      return slaveAgentSlot;
    }
  }
  return NULL;
}

bool SlaveAgentHandlerOfAgent ::demount_slaveAgentIntoSlaveAgentSlot(
    Channel *inputPort) {
  vMountedSlaveAgents.erase(vMountedSlaveAgents.begin() +
                            get_slaveAgentSlotNumber(inputPort));
  return false;
}

// TODO: do it also for integer variables
bool SlaveAgentHandlerOfAgent ::read_slaveAgentValue(
    SlaveAgentSlotOfAgent *slaveAgentSlotOfAgent) {
  if (slaveAgentSlotOfAgent != NULL) {
    Channel *channel = slaveAgentSlotOfAgent->get_comPort();
#ifdef PRINT
    printf("Receiver: %s - ", channel->get_name().c_str());
#endif // PRINT
    if (channel != NULL) {
      int msg;
      if (channel->get_MsgUp(&msg)) {
        if (msg == ISA_SensoryData) {
          float inputValue;
          if (channel->get_MsgUp(&inputValue)) {
            slaveAgentSlotOfAgent->setSlaveAgentValue(inputValue);
#ifdef PRINT
            printf("Agent got via %s: %f\n", channel->get_name().c_str(),
                   inputValue);
#endif
            return true;
          }
        } else {
          //TODO: Error Handling!
        }
      }
    }
  }
  return false;
}

bool SlaveAgentHandlerOfAgent ::read_allSlaveAgentValues() {
  bool flag_readSlaveAgent = true;
  for (auto &slaveAgentSlot : vMountedSlaveAgents) {
    if (!read_slaveAgentValue(slaveAgentSlot)) {
      flag_readSlaveAgent = false;
    }
  }

#ifdef PRINT
  printf("\n");
#endif // PRINT

  return flag_readSlaveAgent;
}

bool SlaveAgentHandlerOfAgent ::attach_historyModule(
    Channel *inputPort, HistoryModule *historyModule) {
  SlaveAgentSlotOfAgent *slaveAgentSlotOfAgent =
      get_slaveAgentSlotAddress(inputPort);
  if (slaveAgentSlotOfAgent != NULL) {
    return slaveAgentSlotOfAgent->set_historyModule(historyModule);
  }
  return false;
}

bool SlaveAgentHandlerOfAgent ::detach_historyModule(
    SlaveAgentSlotOfAgent *slaveAgentSlotOfAgent) {
  if (slaveAgentSlotOfAgent != NULL) {
    return slaveAgentSlotOfAgent->del_historyModule();
  }
  return false;
}

bool SlaveAgentHandlerOfAgent ::detach_historyModule(Channel *inputPort) {
  SlaveAgentSlotOfAgent *slaveAgentSlotOfAgent =
      get_slaveAgentSlotAddress(inputPort);
  return detach_historyModule(slaveAgentSlotOfAgent);
}

bool SlaveAgentHandlerOfAgent ::detach_historyModule(
    HistoryModule *historyModule) {
  SlaveAgentSlotOfAgent *slaveAgentSlotOfAgent =
      get_slaveAgentSlotAddress(historyModule);
  return detach_historyModule(slaveAgentSlotOfAgent);
}

HistoryModule *SlaveAgentHandlerOfAgent ::get_historyModuleOfSlaveAgentSlot(
    Channel *inputPort) {
  SlaveAgentSlotOfAgent *slaveAgentSlotOfAgent =
      get_slaveAgentSlotAddress(inputPort);
  return slaveAgentSlotOfAgent->get_historyModule();
}

bool SlaveAgentHandlerOfAgent ::attach_confidenceModule(
    Channel *inputPort, ConfidenceModule *confidenceModule) {
  SlaveAgentSlotOfAgent *slaveAgentSlotOfAgent =
      get_slaveAgentSlotAddress(inputPort);
  if (slaveAgentSlotOfAgent != NULL) {
    return slaveAgentSlotOfAgent->set_confidenceModule(confidenceModule);
  }
  return false;
}

bool SlaveAgentHandlerOfAgent ::detach_confidenceModule(
    SlaveAgentSlotOfAgent *slaveAgentSlotOfAgent) {
  if (slaveAgentSlotOfAgent != NULL) {
    return slaveAgentSlotOfAgent->del_confidenceModule();
  }
  return false;
}

bool SlaveAgentHandlerOfAgent ::detach_confidenceModule(Channel *inputPort) {
  SlaveAgentSlotOfAgent *slaveAgentSlotOfAgent =
      get_slaveAgentSlotAddress(inputPort);
  return detach_confidenceModule(slaveAgentSlotOfAgent);
}

bool SlaveAgentHandlerOfAgent ::detach_confidenceModule(
    ConfidenceModule *confidenceModule) {
  SlaveAgentSlotOfAgent *slaveAgentSlotOfAgent =
      get_slaveAgentSlotAddress(confidenceModule);
  return detach_confidenceModule(slaveAgentSlotOfAgent);
}

ConfidenceModule *
SlaveAgentHandlerOfAgent ::get_confidenceModuleOfSlaveAgentSlot(
    Channel *inputPort) {
  SlaveAgentSlotOfAgent *slaveAgentSlotOfAgent =
      get_slaveAgentSlotAddress(inputPort);
  return slaveAgentSlotOfAgent->get_confidenceModule();
}

vector<SlaveAgentSlotOfAgent *> *
SlaveAgentHandlerOfAgent ::get_vMountedSlaveAgents() {
  return &vMountedSlaveAgents;
}

bool SlaveAgentHandlerOfAgent::saveValueInHistory(
    SlaveAgentSlotOfAgent *slaveAgentSlotOfAgent) {
  if (slaveAgentSlotOfAgent != NULL) {
    // TODO: change this hardcoded value //Zeitfenster Sliding Window!!!!
    while (slaveAgentSlotOfAgent->getHistoryLength() >= 10) {
      slaveAgentSlotOfAgent->deleteOldestHistoryEntry();
    }

    // JUST FOR TESTING
    // slaveAgentSlotOfAgent->printHistory();

    return slaveAgentSlotOfAgent->saveValueInHistory();
  }
  return false;
}

bool SlaveAgentHandlerOfAgent::saveAllValuesInHistory() {
  bool flagSavingSuccesful = true;
  for (auto &slaveAgentSlot : vMountedSlaveAgents) {
    if (!saveValueInHistory(slaveAgentSlot)) {
      flagSavingSuccesful = false;
    }
  }
  return flagSavingSuccesful;
}
