/*
 * AXCRingBuf.cpp
 */

#include "AXCRingBuf.hpp"

#include <string.h>

AXCRingBuf::ringBufStatus AXCRingBuf::init() {
  m_offset[ RINGBUF_READ ] = 0;
  m_offset[ RINGBUF_WRITE ] = 0;
  return RINGBUF_SUCCESS;
}

int AXCRingBuf::dataSize() {
  int n = getOffset(RINGBUF_WRITE) - getOffset(RINGBUF_READ);
  return n >= 0 ? n : n + m_size;
}

int AXCRingBuf::freeSize() {
  return m_size - 1 - dataSize();
}

int AXCRingBuf::read(char* data, int size) {

  // @ToDo
  // size の範囲チェックが必要

  ringBufRW rw = RINGBUF_READ;
  if (lock(rw) != RINGBUF_SUCCESS) return -1;
  int dt, sz, r, sz1, sz2;
  if ((dt = dataSize()) <= 0) {
    if (waitFor(rw) != RINGBUF_SUCCESS) {
      sz = -2;
      goto error;
    }
    if((dt = dataSize()) <= 0) {
      sz = 0;
      goto error;
    }
  }

  sz = dt < size ? dt : size;
  r = getOffset(rw);
  sz1 = m_size - r;
  if (sz1 > sz) sz1 = sz;
  memcpy(data, m_buf + r, sz1);
  sz2 = sz - sz1;
  if (sz2 > 0) memcpy(data + sz1, m_buf, sz2);

  r += sz;
  if (r >= m_size) r -= m_size;
  setOffset(rw, r);
  if (signalTo(RINGBUF_WRITE) != RINGBUF_SUCCESS) {
    // @ToDo
    // readの処理自体は成功しているが、
    // バッファに空が出来た事を、
    // write側へ通知するのに失敗してる状態
    sz = -3;
    goto error;
  }
 error:
  if (unlock(rw) != RINGBUF_SUCCESS) return -4;
  return sz;
}

int AXCRingBuf::write(char* data, int size) {

  // @ToDo
  // size の範囲チェックが必要

  ringBufRW rw = RINGBUF_WRITE;
  if (lock(rw) != RINGBUF_SUCCESS) return -1;
  int rest = size;
  while (rest > 0) {
    int fr;
    if ((fr = freeSize()) <= 0) {
      if (waitFor(rw) != RINGBUF_SUCCESS) {
        // @ToDo
        // 途中までwriteした段階でバッファが満杯になった場合も、
        // ここでバッファに空が出来るのを待機する。
        //
        // その際の待機処理でエラーが返った場合、
        // バッファの状態は、更新が途中まで成功している
        // 中途半端な状態となる。
	size = -2;
	goto error;
      }
      if ((fr = freeSize()) <= 0) {
        size -= rest;
        goto error;
      }
    }
    int sz = rest < fr ? rest : fr;
    int w = getOffset(rw);
    int sz1 = m_size - w;
    if (sz1 > sz) sz1 = sz;
    memcpy(m_buf + w, data, sz1);
    int sz2 = sz - sz1;
    if (sz2 > 0) memcpy(m_buf, data + sz1, sz2);

    w += sz;
    if (w >= m_size) w -= m_size;
    setOffset(rw, w);
    if (signalTo(RINGBUF_READ) != RINGBUF_SUCCESS) {
      // @ToDo
      // writeの処理自体は成功しており、
      // バッファにデータが追加された事を、
      // read側へ通知するのに失敗してる状態
      size = -3;
      goto error;
    }
    data += sz;
    rest -= sz;
  }
 error:
  if (unlock(rw) != RINGBUF_SUCCESS) return -4;
  return size;
}

