//proto_simple_vio.c:

/*
 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2009-2014
 *
 *  This file is part of RoarAudio PlayList Daemon,
 *  a playlist management daemon for RoarAudio.
 *  See README for details.
 *
 *  This file is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 3
 *  as published by the Free Software Foundation.
 *
 *  RoarAudio is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this software; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 *
 */

#include "rpld.h"

int     rpld_proto_simple_vio_open    (struct roar_vio_calls * vio, struct roar_vio_calls * dst) {
 struct rpld_proto_simple_inst * self;

 ROAR_DBG("rpld_proto_simple_vio_open(vio=%p, dst=%p) = ?", vio, dst);

 if ( vio == NULL || dst == NULL )
  return -1;

 self = roar_mm_malloc(sizeof(struct rpld_proto_simple_inst));

 if ( self == NULL )
  return -1;

 memset(self, 0, sizeof(struct rpld_proto_simple_inst));
 memset(vio,  0, sizeof(struct roar_vio_calls));

 self->vio         = dst;
 self->input_dots  = -1;
 self->output_dots = -1;
 self->eof         =  0;

 vio->refc     = 1;
 vio->flags    = ROAR_VIO_FLAGS_NONE;
 vio->inst     = self;
 vio->close    = rpld_proto_simple_vio_close;
 vio->read     = rpld_proto_simple_vio_read;
 vio->write    = rpld_proto_simple_vio_write;
 vio->sync     = rpld_proto_simple_vio_sync;
// vio->ctl      = rpld_proto_simple_vio_ctl;

 ROAR_DBG("rpld_proto_simple_vio_open(vio=%p, dst=%p) = 0", vio, dst);
 return 0;
}

int     rpld_proto_simple_vio_close   (struct roar_vio_calls * vio) {
 struct rpld_proto_simple_inst * self = vio->inst;

 ROAR_DBG("rpld_proto_simple_vio_close(vio=%p) = ?", vio);

 if ( self->iobuffer != NULL )
  roar_buffer_free(self->iobuffer);

 roar_mm_free(self);

 ROAR_DBG("rpld_proto_simple_vio_close(vio=%p) = 0", vio);
 return 0;
}

ssize_t rpld_proto_simple_vio_read    (struct roar_vio_calls * vio, void *buf, size_t count) {
 struct rpld_proto_simple_inst * self = vio->inst;
 size_t  buflen;
 size_t  done = 0;
 ssize_t ret;
 void  * bufdata;

 ROAR_DBG("rpld_proto_simple_vio_read(vio=%p, buf=%p, count=%lu) = ?", vio, buf, (unsigned long)count);

 if ( count == 0 )
  return 0;

 ROAR_DBG("rpld_proto_simple_vio_read(vio=%p, buf=%p, count=%lu) = ?", vio, buf, (unsigned long)count);

 if ( self->iobuffer == NULL ) {
  if ( roar_buffer_new(&(self->iobuffer), RPLD_PROTO_SIMPLE_VIO_BUFSIZE) == -1 )
   return -1;

  if ( roar_buffer_set_len(self->iobuffer, 0) == -1 ) {
   roar_buffer_free(self->iobuffer);
   self->iobuffer = NULL;
   return -1;
  }
 }

 if ( roar_buffer_get_len(self->iobuffer, &buflen) == -1 )
  return -1;

 // optimize a bit:
 if ( buflen >= count ) {
  if ( roar_buffer_get_data(self->iobuffer, &bufdata) == -1 )
   return -1;

  memcpy(buf, bufdata, count);

  _LIBROAR_IGNORE_RET(roar_buffer_set_offset(self->iobuffer, count));

 ROAR_DBG("rpld_proto_simple_vio_read(vio=%p, buf=%p, count=%lu) = %lu", vio, buf, (unsigned long)count, (unsigned long) count);
  return count;
 }

 ROAR_DBG("rpld_proto_simple_vio_read(*): entering main loop...");

 while ( done < count ) {
  ROAR_DBG("rpld_proto_simple_vio_read(*): we still need %lu bytes", (unsigned long) (count - done));

  if ( roar_buffer_get_len(self->iobuffer, &buflen) == -1 )
   return -1;

  if ( buflen ) {
   ROAR_DBG("rpld_proto_simple_vio_read(*): we got %lu bytes from buffer", (unsigned long) buflen);

   if ( roar_buffer_get_data(self->iobuffer, &bufdata) == -1 )
    break;

   if ( buflen > (count - done) )
    buflen = count - done;

   memcpy(buf, bufdata, buflen);

   _LIBROAR_IGNORE_RET(roar_buffer_set_offset(self->iobuffer, buflen));

   buf  += buflen;
   done += buflen;
  }

  if ( done == count )
   break;

  if ( self->eof )
   break;

  ROAR_DBG("rpld_proto_simple_vio_read(*): we need a new buffer");

  if ( roar_buffer_set_len(self->iobuffer, RPLD_PROTO_SIMPLE_VIO_BUFSIZE) == -1 )
   break;

  if ( roar_buffer_get_data(self->iobuffer, &bufdata) == -1 )
   break;

  ret = roar_vio_read(self->vio, bufdata, RPLD_PROTO_SIMPLE_VIO_BUFSIZE);

  ROAR_DBG("rpld_proto_simple_vio_read(*): got %li bytes from network", (long) ret);

  if ( ret == -1 || ret == 0 )
   break;

  if ( self->input_dots == 0 ) {
   if ( ret >= 2 ) {
    if ( ((char*)bufdata)[0] == '.' && ((char*)bufdata)[1] == '\n' ) {
     self->eof  = 1;
     ret  = 0;
    } else {
     self->input_dots = -1;
    }
   } else {
    self->input_dots = -1;
   }
  }

  if ( ret >= 3 ) {
   if ( !strncmp(bufdata+ret-3, "\n.\n", 3) ) {
    self->eof  = 1;
    ret -= 3;
   }
  }

  if ( ret >= 1 ) {
   if ( *((char*)bufdata+ret-1) == '\n' )
    self->input_dots = 0;
  }

  // very bad if this goes wrong:
  if ( roar_buffer_set_len(self->iobuffer, ret) == -1 )
   return -1;
 }

 ROAR_DBG("rpld_proto_simple_vio_read(vio=%p, buf=%p, count=%lu) = %lu", vio, buf, (unsigned long)count, (unsigned long) done);

 return done;
}

ssize_t rpld_proto_simple_vio_write   (struct roar_vio_calls * vio, void *buf, size_t count) {
 struct rpld_proto_simple_inst * self = vio->inst;

 return roar_vio_write(self->vio, buf, count);
}
//off_t   rpld_proto_simple_vio_lseek   (struct roar_vio_calls * vio, off_t offset, int whence);
//int     rpld_proto_simple_vio_nonblock(struct roar_vio_calls * vio, int state);
int     rpld_proto_simple_vio_sync    (struct roar_vio_calls * vio) {
 (void)vio;
 return 0;
}

/*
int     rpld_proto_simple_vio_ctl     (struct roar_vio_calls * vio, int cmd, void * data) {
 return -1;
}
*/

//ll
