/* 
 * This software is licensed under terms of the GNU GENERAL PUBLIC LICENSE
 *
 * This software is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This software  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.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Source code is available at http://ppcenter.free.fr
 */


#include "scd_common.h"


//----------------------------------------------------------------------
// Circular buffer stuff.
// 
//----------------------------------------------------------------------
/*--- Saturn-side functions. ---*/
/**
 * Init Circular buffer.
**/
void scd_cbinit(scd_data_t* d)
{
    memset((void*)d, 0, sizeof(scd_data_t));
    /* Reset Circular buffer pointers. */
    d->readptr  = 0;
    d->writeptr = d->readptr;

    /* Set structure size and version */
    d->version[0] = SCD_VERSION_MAJOR;
    d->version[1] = SCD_VERSION_MINOR;
    d->structsz   = sizeof(scd_data_t);
    /* Set internals. */
    d->start_address = (unsigned long)d->rbuffer;
    d->end_address   = d->start_address + SCD_CIRCBUFF_SIZE - 1;
    d->buffer_size   = SCD_CIRCBUFF_SIZE;
}


/**
 * Write type+datalen+data in Circular buffer.
 * It modifies Circular buffer's write pointer.
**/
void scd_cbwrite(scd_data_t* d, unsigned char* data, unsigned long datalen)
{
    if(d->writeptr >= d->readptr)
    {
        /* Enough free space ? */
        if((datalen + 1) > (d->buffer_size - d->writeptr + d->readptr - 1)) return;

        unsigned long end_space = d->buffer_size - d->writeptr;

        if(end_space >= datalen)
        { /* Enough space until the end of the buffer. */
            memcpy((void*)(d->start_address+d->writeptr), 
                   (void*)(data), 
                   datalen);
        }
        else
        { /* Need to write the data in 2 times: end of buffer, and the remaining from the buffer start */
            memcpy((void*)(d->start_address+d->writeptr), 
                   (void*)(data), 
                   end_space);

            memcpy((void*)(d->start_address), 
                   (void*)(data+end_space), 
                   datalen - end_space);
        }
    }
    else
    {
        /* Enough free space ? */
        if((datalen + 1) > (d->readptr - d->writeptr - 1)) return;

        memcpy((void*)(d->start_address+d->writeptr), 
               (void*)(data), 
               datalen);
    }

    /* Update write pointer. */
    unsigned long ptr = d->writeptr + datalen;
    d->writeptr = (ptr >= d->buffer_size ? ptr - d->buffer_size : ptr);
}


/*
 * Read function cannot be used as-is, because we need to receive
 * data from Saturn, and not simply memcpy it.
 * Hence, the circular buffer read function is directly implemented 
 * in SatLink -> ProcessThread.cpp::ReadCircularBufferData function.
 */
#if 0
/*--- PC-side functions. ---*/
/**
 * Read all the data in the Circular buffer.
 * Note: need at most SCD_CircularBUFF_SIZE bytes in output buffer.
 * It modifies Circular buffer's read pointer.
**/
unsigned long scd_cbread(scd_data_t* d, unsigned char* data)
{
    unsigned long read_size=0;

    /* Circular buffer is empty. */
    if(d->writeptr == d->readptr) return read_size;

    if(d->writeptr > d->readptr)
    {
        read_size = d->writeptr - d->readptr;
        memcpy((void*)(data), 
               (void*)(d->start_address+d->readptr), 
               read_size);
    }
    else
    {
        read_size = d->buffer_size - d->readptr;
        memcpy((void*)(data), 
               (void*)(d->start_address+d->readptr), 
               read_size);

        memcpy((void*)(data+read_size), 
               (void*)(d->start_address), 
               d->writeptr);
        read_size += d->writeptr;
    }

    /* Update read pointer. */
    d->readptr = d->writeptr;

    return read_size;
}
#endif



