/*
*Copyright(c)2016, Jeffrey Lee
*Allrightsreserved.
*
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met: 
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
*/

#include <stdlib.h>
#include <string.h>
#include "circular.h"
#include "debug.h"

bool circular_init(circular_buffer *buf, int size)
{
  circular_free(buf);
  buf->buffer = malloc(size);
  if (!buf->buffer)
  {
    return false;
  }
  buf->size = size;
  return true;
}

void circular_free(circular_buffer *buf)
{
  if (buf->buffer)
  {
    free(buf->buffer);
  }
  memset(buf,0,sizeof(circular_buffer));
}

bool circular_write(circular_buffer *buf, const char *data, int size)
{
  if (buf->used+size > buf->size)
  {
    return false;
  }
  buf->used += size;
  if (buf->end + size >= buf->size)
  {
    int chunk = buf->size - buf->end;
    memcpy(buf->buffer+buf->end, data, chunk);
    data += chunk;
    size -= chunk;
    buf->end = 0;
  }
  if (size)
  {
    memcpy(buf->buffer+buf->end, data, size);
    buf->end += size;
  }
  if (buf->used > buf->hwm)
  {
    buf->hwm = buf->used;
  }
  return true;
}

os_error *circular_tx(circular_buffer *buf, socket_s socket, int size)
{
  if (size > buf->used)
  {
    size = buf->used;
  }
  if (!size)
  {
    return NULL;
  }
  if (buf->start + size >= buf->size)
  {
    int chunk = buf->size - buf->start;
    os_error *err = xsocket_send(socket, (byte const*) (buf->buffer+buf->start), chunk, 0, &chunk);
    if (err || !chunk)
    {
      return err;
    }
    buf->start += chunk;
    buf->used -= chunk;
    if (buf->start == buf->size)
    {
      buf->start = 0;
    }
    else
    {
      /* Partial send */
      return NULL;
    }
    size -= chunk;
  }
  if (size)
  {
    os_error *err = xsocket_send(socket, (byte const*) (buf->buffer+buf->start), size, 0, &size);
    if (err || !size)
    {
      return err;
    }
    buf->start += size;
    buf->used -= size;
  }
  return NULL;
}

void circular_reset(circular_buffer *buf)
{
  buf->used = buf->start = buf->end = 0;
}
