#include "Channel.h"
#include <stdio.h>

#define MAX_BUFFER_LENGTH 100

void Channel::init_channel() {
  maxBufferLength = MAX_BUFFER_LENGTH;
  transferRate = MAX_BUFFER_LENGTH;
}

Channel::Channel() { init_channel(); }

Channel::Channel(const char *name) {
  set_name(name);
  init_channel();
}

bool Channel::set_maxBufferLength(unsigned int maxBufferLength) {
  if (maxBufferLength <= MAX_BUFFER_LENGTH) {
    this->maxBufferLength = maxBufferLength;
    return true;
  }
  return false;
}

unsigned int Channel::get_maxBufferLength() { return maxBufferLength; }

unsigned int Channel::get_avlInputBufferUp() {
  return maxBufferLength - lInputMsgBufferUp.size();
}

unsigned int Channel::get_avlOutputBufferUp() {
  return maxBufferLength - lOutputMsgBufferUp.size();
}

unsigned int Channel::get_avlInputBufferDown() {
  return maxBufferLength - lInputMsgBufferDown.size();
}

unsigned int Channel::get_avlOutputBufferDown() {
  return maxBufferLength - lOutputMsgBufferDown.size();
}

bool Channel::set_transferRate(unsigned int transferRate) {
  if (transferRate <= MAX_BUFFER_LENGTH) {
    this->transferRate = transferRate;
    return true;
  }
  return false;
}

unsigned int Channel::get_transferRate() { return transferRate; }

bool Channel::add_msgAtBegin(list<Message *> *buffer, Message *message) {
  try {
    buffer->push_front(message);
  } catch (bad_alloc &error) {
    delete message;
    return false;
  }
  return true;
}

bool Channel::del_msgAtBegin(list<Message *> *buffer) {
  try {
    buffer->pop_front();
  } catch (bad_alloc &error) {
    return false;
  }
  return true;
}

bool Channel::add_msgAtEnd(list<Message *> *buffer, Message *message) {
  try {
    buffer->push_back(message);
  } catch (bad_alloc &error) {
    delete message;
    return false;
  }
  return true;
}

bool del_msgAtEnd(list<Message *> *buffer) {
  try {
    buffer->pop_back();
  } catch (bad_alloc &error) {
    return false;
  }
  return true;
}

bool Channel::send_MsgUp(Message *message) {
  if (message != NULL) {
    if (transferRate == 0) {
      // TODO: at the moment only one packet (in the front) gets deleted if
      // buffer is full. However, the whole message (instruction+value) should
      // be deleted in case of a full buffer
      if (lOutputMsgBufferUp.size() == maxBufferLength) {
        del_msgAtBegin(&lOutputMsgBufferUp);
      }
      return add_msgAtEnd(&lOutputMsgBufferUp, message);
    } else {
      if (lInputMsgBufferUp.size() == maxBufferLength) {
        // TODO: at the moment only one packet (in the front) gets deleted if
        // buffer is full. However, the whole message (instruction+value) should
        // be deleted in case of a full buffer
        del_msgAtBegin(&lInputMsgBufferUp);
      }
      return add_msgAtEnd(&lInputMsgBufferUp, message);
    }
  }
  // FIXME
  return false;
}

bool Channel::send_MsgUp(float msg) {
  Message *message = new Message(msg);
  return send_MsgUp(message);
}

bool Channel::send_MsgUp(int msg) {
  Message *message = new Message(msg);
  return send_MsgUp(message);
}

bool Channel::get_MsgUp(float *msg) {
  if (isThereFloatMsgUp()) {
    float tempMsg;
    if (lOutputMsgBufferUp.front()->getMsg(&tempMsg)) {
      *msg = tempMsg;
      del_msgAtBegin(&lOutputMsgBufferUp);
      return true;
    }
  }
  return false;
}

bool Channel::get_MsgUp(int *msg) {
  if (isThereIntMsgUp()) {
    int tempMsg;
    if (lOutputMsgBufferUp.front()->getMsg(&tempMsg)) {
      *msg = tempMsg;
      del_msgAtBegin(&lOutputMsgBufferUp);
      return true;
    }
  }
  return false;
}

bool Channel::isThereFloatMsgUp() {
  if (lOutputMsgBufferUp.size() > 0) {
    if (lOutputMsgBufferUp.front() != NULL) {
      return lOutputMsgBufferUp.front()->isMsgFloat();
    }
  }
  return false;
}

bool Channel::isThereIntMsgUp() {
  if (lOutputMsgBufferUp.size() > 0) {
    if (lOutputMsgBufferUp.front() != NULL) {
      return lOutputMsgBufferUp.front()->isMsgInt();
    }
  }
  return false;
}

bool Channel::send_MsgDown(Message *message) {
  if (message != NULL) {
    if (transferRate == 0) {
      if (lOutputMsgBufferDown.size() == maxBufferLength) {
        del_msgAtBegin(&lOutputMsgBufferDown);
      }
      return add_msgAtEnd(&lOutputMsgBufferDown, message);
    } else {
      if (lInputMsgBufferDown.size() == maxBufferLength) {
        del_msgAtBegin(&lInputMsgBufferDown);
      }
      return add_msgAtEnd(&lInputMsgBufferDown, message);
    }
  }
  // FIXME
  return false;
}

bool Channel::send_MsgDown(float msg) {
  Message *message = new Message(msg);
  return send_MsgDown(message);
}

bool Channel::send_MsgDown(int msg) {
  Message *message = new Message(msg);
  return send_MsgDown(message);
}

bool Channel::get_MsgDown(float *msg) {
  if (isThereFloatMsgDown()) {
    float tempMsg;
    if (lOutputMsgBufferDown.front()->getMsg(&tempMsg)) {
      *msg = tempMsg;
      del_msgAtBegin(&lOutputMsgBufferDown);
      return true;
    }
  }
  return false;
}

bool Channel::get_MsgDown(int *msg) {
  if (isThereIntMsgDown()) {
    int tempMsg;
    if (lOutputMsgBufferDown.front()->getMsg(&tempMsg)) {
      *msg = tempMsg;
      del_msgAtBegin(&lOutputMsgBufferDown);
      return true;
    }
  }
  return false;
}

bool Channel::isThereFloatMsgDown() {
  if (lOutputMsgBufferDown.size() > 0) {
    if (lOutputMsgBufferDown.front() != NULL) {
      return lOutputMsgBufferDown.front()->isMsgFloat();
    }
  }
  return false;
}

bool Channel::isThereIntMsgDown() {
  if (lOutputMsgBufferDown.size() > 0) {
    if (lOutputMsgBufferDown.front() != NULL) {
      return lOutputMsgBufferDown.front()->isMsgInt();
    }
  }
  return false;
}

bool Channel::transferMsgs(list<Message *> *dest_buffer,
                           list<Message *> *src_buffer) {
  unsigned int NumOfMsgsToMove;

  if (transferRate <= src_buffer->size()) {
    NumOfMsgsToMove = transferRate;
  } else {
    NumOfMsgsToMove = src_buffer->size();
  }

  if (NumOfMsgsToMove <= maxBufferLength - dest_buffer->size()) {
    for (unsigned int i = 0; i < NumOfMsgsToMove; i++) {
      if (add_msgAtEnd(dest_buffer, src_buffer->front())) {
        if (!del_msgAtBegin(src_buffer)) {
          return false;
        }
      } else {
        return false;
      }
    }
    return true;
  }
  return false;
}

bool Channel::trigger() {
  bool flag_worked = transferMsgs(&lOutputMsgBufferUp, &lInputMsgBufferUp) &&
                     transferMsgs(&lOutputMsgBufferUp, &lInputMsgBufferUp);
  printf("Channel %s: in_up %zu, out_up %zu, in_dn %zu, out_dn %zu,\n", name,
         lInputMsgBufferUp.size(), lOutputMsgBufferUp.size(),
         lInputMsgBufferDown.size(), lOutputMsgBufferDown.size());
  return flag_worked;
}
