/*
 * AXFQueue.cpp
 */

#include "AXFQueueInternal.hpp"
#include "AXFMutex.hpp"
#include "AXFConditionVariableInternal.hpp"
#include "AXFObjectName.hpp"

#include <new>
#include <string.h>

AXFQueueInternal::queueStatus AXFQueueInternal::allocQueue(
    AXFQueueInternal::queueInfo_t** que) {
  *que = new (std::nothrow) queueInfo_t();
  //@UTIGN new
  if (NULL == *que) {
    return QUEUE_ERROR_NOMEM;
  } else {
    (*que)->malloc_ptr = NULL;
    (*que)->link = NULL;
    return QUEUE_SUCCESS;
  }
}

void AXFQueueInternal::freeQueue(queueInfo_t** que) {
  delete *que;
  *que = NULL;
}

AXFQueueInternal::queueStatus AXFQueueInternal::allocQueueBlock(
    queueInfo_t* que, UINT32_t count, queueBlock_t** queBlk) {
  UINT32_t i;
  queueBlock_t *t, *b;

  *queBlk = NULL;

  if (count <= 0) {
    return QUEUE_ERROR_PARAM;
  }

  que->link = new (std::nothrow) queueBlockLink_t[count];
  if (que->link == NULL) {
    return QUEUE_ERROR_NOMEM;
  }

  b = t = NULL;

  for (i = 0; i < count; i++) {
    if (t == NULL) {
      b = t = &(que->link[i].que_bl);
    } else {
      b->order.next = &(que->link[i].que_bl);
      b->order.next->order.pre = b;
      b = b->order.next;
    }
  }
  t->order.pre = b;
  b->order.next = t;
  *queBlk = t;
  return QUEUE_SUCCESS;
}

void AXFQueueInternal::freeQueueBlock(queueInfo_t* que, queueBlock_t* que_bl) {
  delete[] que->link;
  que->link = NULL;
  return;
}

/* キュー・ブロックに、データ格納用のメモリを割り当てていく */
AXFQueueInternal::queueStatus AXFQueueInternal::allocQueueBlockData(
    queueInfo_t* que, UINT32_t count, UINT32_t size) {
  UINT8_t *buf;
  queueBlock_t *que_bl, *block;
  UINT32_t i;

  que_bl = que->read;

  buf = new (std::nothrow) UINT8_t[count * size];
  if (buf == NULL) {
    return QUEUE_ERROR_NOMEM;
  }

  que->malloc_ptr = buf;
  block = que_bl;
  for (i = 0; i < count; i++) {
    block->data = (UINT8_t *) &buf[size * i];
    block = block->order.next;
  }
  //@UTIGN (!) always block == que_bl (?)
  if (block != que_bl) {
    delete[] buf;
    // エラー発生
    return QUEUE_ERROR;
  }
  return QUEUE_SUCCESS;
}

/* queの、データ格納用のメモリを解放する        */
void AXFQueueInternal::freeQueueBlockData(queueInfo_t* que) {
  delete[] que->malloc_ptr;
  que->malloc_ptr = NULL;
  return;
}

int AXFQueueInternal::writeQueuePacket(queueInfo_t* que, queuePacket_t* pkt) {
  int size;

  size = MIN(que->maxSize, pkt->size);
  if (size < 0) {
    return -1;
  }

  if ((que->write->data == NULL) || (pkt->data == NULL)) {
    que->write->size = 0;
  } else {
    memcpy(que->write->data, pkt->data, size);
    que->write->size = size;
  }
  que->write->eventId = pkt->eventId;
//que->write->time = AXTIME
  que->write = que->write->order.next;
  que->useCount++;

  return size;
}

int AXFQueueInternal::readQueuePacket(queueInfo_t* que, queuePacket_t* pkt) {
  int size;

  size = MIN(que->read->size, pkt->size);
  if (size < 0) {
    return -1;
  }

  pkt->eventId = que->read->eventId;
  if ((que->read->data == NULL) || (pkt->data == NULL)) {
    pkt->size = 0;
  } else {
    memcpy(pkt->data, que->read->data, size);
    pkt->size = size;
  }

//pkt->when = que->read->time;
  que->read = que->read->order.next;
  que->useCount--;

  return size;
}

AXFQueueInternal::AXFQueueInternal()
    : m_cond(NULL), m_mutex(NULL) {
}

AXFQueueInternal::~AXFQueueInternal() {
  delete m_cond;
  delete m_mutex;
}

AXFQueueInternal::queueStatus AXFQueueInternal::createQueue(
    int count, int size, queueInfo_t** que, AXFConditionVariableInternal* cond,
    AXFMutex* mutex) {
  struct queueBlock_t* que_bl;
  queueStatus stat;

  m_cond = cond;
  m_mutex = mutex;

  *que = NULL;

  stat = allocQueue(que);
  //@UTIGN new
  if (QUEUE_SUCCESS != stat) {
    return stat;
  }

  stat = allocQueueBlock(*que, count, &que_bl);
  if (QUEUE_SUCCESS != stat) {
    deleteQueue(que);
    return stat;
  }

  if (NULL == cond) {
    deleteQueue(que);
    return QUEUE_ERROR_NOMEM;
  }
  if (AXFConditionVariableInternal::COND_SUCCESS != cond->init()) {
    deleteQueue(que);
    return QUEUE_ERROR_COND;
  }

  if (NULL == mutex) {
    deleteQueue(que);
    return QUEUE_ERROR_NOMEM;
  }
  if (AXFMutex::MUTEX_SUCCESS != mutex->init()) {
    deleteQueue(que);
    return QUEUE_ERROR_MUTEX;
  }

  (*que)->maxCount = count;
  (*que)->maxSize = size;
  (*que)->useCount = 0;
  (*que)->read = que_bl;
  (*que)->write = que_bl;

  stat = allocQueueBlockData(*que, count, size);
  if (QUEUE_SUCCESS != stat) {
    deleteQueue(que);
    return stat;
  }

  return QUEUE_SUCCESS;
}

AXFQueueInternal::queueStatus AXFQueueInternal::deleteQueue(queueInfo_t** que) {
  if (*que == NULL) {
    return QUEUE_SUCCESS;
  }

  /*
   * キューに未読データがあっても、とりあえず削除できるようにしておく
   */
#ifdef NEVER
  if ((*que)->useCount != 0) {
    return QUEUE_ERROR;
  }
#endif /* NEVER */

  freeQueueBlockData(*que); /* intr: seq-start */
  freeQueueBlock(*que, (*que)->read);

  freeQueue(que);

  return QUEUE_SUCCESS;
}

int AXFQueueInternal::writeQueue(queueInfo_t* que, queuePacket_t* pkt) {
  int wsize;

#ifdef NEVER
  if (pkt->timeout < -1)
  return -1;
#endif /* NEVER */

  if (AXFMutex::MUTEX_SUCCESS != m_mutex->lock()) {
    return -2;
  }

  if (que->useCount < que->maxCount) {
    /* write success */
    wsize = writeQueuePacket(que, pkt);

    m_mutex->unlock();

    if (wsize == -1) {
      return -3;
    }

    if (AXFConditionVariableInternal::COND_SUCCESS != m_cond->signal()) {
      return -4;
    }

    return wsize;
  }

  m_mutex->unlock();

  return -5;
}

int AXFQueueInternal::readQueue(queueInfo_t* que, queuePacket_t* pkt) {
  int rsize;

#ifdef NEVER
  if (pkt->timeout < -1)
  return -1;
#endif /* NEVER */

  for (;;) {

    if (AXFMutex::MUTEX_SUCCESS != m_mutex->lock()) {
      return -2;
    }

    if (que->useCount != 0) {
      /* read success */
      rsize = readQueuePacket(que, pkt);

      m_mutex->unlock();

      return rsize;
    }

    m_mutex->unlock();

    if (AXFConditionVariableInternal::COND_SUCCESS != m_cond->wait()) {
      return -3;
    }
  }
}

int AXFQueueInternal::peekQueue(queueInfo_t* que) {
  int size;

  if (que->useCount == 0) {
    return -1;
  }
  size = que->read->size;
  return size;
}

int AXFQueueInternal::npeekQueue(queueInfo_t* que) {
  return que->useCount;
}
