#include "decoder.h"


decoder::indata::indata( void* d, int l ){
    data = malloc( l );
    len = l;
    memcpy( data, d, l );
    pos = 0;
}
decoder::indata::indata( const char* fname ){
    FILE* f = fopen( fname, "rb" );
    int blen = 1024;
    data = malloc( blen );
    len = 0;
    char tm = fgetc( f );
    while( !feof( f ) ){
        ((unsigned char*)data)[len++] = tm;
        tm = fgetc( f );
        if( len >= blen ){
            blen *= 2;
            //printf("BLEN = %i\n", blen );
            data = realloc( data, blen );
        }
    }
    pos = 0;
    fclose( f );
}
decoder::indata::~indata(){
    free( data );
}
void decoder::indata::seek( int whence, int offset ){
    switch( whence ){
        case SEEK_SET: pos = offset; break;
        case SEEK_CUR: pos += offset; break;
        case SEEK_END: pos = len + offset; break;
    }
}
void decoder::indata::seek( void* position ){
    pos = ((int)position) - ((int)data);
}
void* decoder::indata::get(int length, int* lremaining){
    int t = len - pos;
    //if( pos + length > len ) t = len-pos;
    if( lremaining != 0 ) *lremaining = t;
    void* ret = data + pos;
    pos += length;
    if( pos > len ) pos = len;
    return ret;
}
void* decoder::indata::get(void* buff, int length, int* lremaining){
    int t = len - pos;
    //if( pos + length > len ) t = len-pos;
    if( lremaining != 0 ) *lremaining = t;
    void* ret = data + pos;
    if( length + pos > len ) length = t;
    memcpy( buff, ret, length );
    pos += length;
    return ret;
}

bool decoder::indata::eof(){
    return pos == len;
}

decoder::outcb::outcb( void* u, int(*i)(void*,const void*,int) ){
    udat = u; cb = i;
}

int decoder::outcb::operator()( const void* s, int l ){
    if( cb == 0 ) return -1;
    return cb( udat, s, l );
}

decoder::decoder( info a ){
    __init__(a);
}
decoder::decoder(){}

decoder::~decoder(){
    __destroy__();

}

void decoder::__init__(info a){
    output = new outcb( a.output_userdata, a.output_callback );
    switch( a.input_type ){
        case info::TYPE_FILE: data = new indata( a.input_filename ); break;
        case info::TYPE_MEM:  data = new indata( a.input_data, a.input_length ); break;
        default: data = new indata(); break;
    }
    samplerate = a.desired_samplerate;
    bps = a.desired_bps;
    sblen = a.desired_sblen;
    chan = a.desired_channels;

}
void decoder::__destroy__(){

    delete output;
    delete data;
}
void decoder::decode(){}

int64_t decoder::mixsamples( int64_t a, int64_t b, uint8_t factor){
    uint64_t diff = b - a;
    return a + diff * factor / 0xFF;
}
int decoder::getfactor( int orate ){
    return 0xFF * samplerate / orate;
}


void decoder::samples::PRIVATERESET(){
    free( data );
    len = 1024;
    data = malloc(len);
    readpos = writepos = 0;
    doreset = false;
    resetprotected = 0;
}
void decoder::samples::lock(){
    SDL_mutexP( writing );
}
void decoder::samples::unlock(){
    SDL_mutexV( writing );
}
void decoder::samples::preventreset(){
    lock();
        resetprotected++;
    unlock();
}
void decoder::samples::permitreset(){
    lock();
        resetprotected--;
        if( resetprotected <= 0 && doreset ){
            PRIVATERESET();
        }
    unlock();
}

decoder::samples::samples(){
    writing = SDL_CreateMutex();
    lock();
        len = 1024;
        data = malloc(len);
        readpos = writepos = 0;
        doreset = false;
        resetprotected = 0;
    unlock();
}
decoder::samples::~samples(){
    lock();
        free( data );
    unlock();
    SDL_DestroyMutex( writing );
}
void decoder::samples::reserve(int amnt){
    lock();
        if( amnt > len ){
            len = amnt;
            data = realloc( data, len );
        }
    unlock();
}
void decoder::samples::push( const void* buff, int length ){
    //lock();
        while( writepos + length > len ){
                //printf("SLOW%i\t", len);
            reserve( len * 2 );
        }
        memcpy( data + writepos, buff, length );
        writepos += length;
    //unlock();
}
int decoder::samples::pull( void* buff, int length ){
    //lock();
        int l = length < writepos - readpos ? length : writepos - readpos;
        memcpy( buff, data + readpos, l );
        readpos += l;
    //unlock();
    return l;
}
int decoder::samples::pullfrom( void* buff, int length, int p ){
    //lock();
        int l = length < writepos - p ? length : writepos - p;
        memcpy( buff, data + p, l );
        p += l;
    //unlock();
    return l;
}

int decoder::samples::pullreadonly( void** buff, int length ){
    //lock();
        int l = length < writepos - readpos ? length : writepos - readpos;
        readpos += l;
        if( buff != 0 ) *buff = data + readpos;
    //unlock();
    return l;
}
void decoder::samples::rewind(){
    lock();
        readpos = 0;
    unlock();
}
bool decoder::samples::eof(){
    return readpos == writepos;
}
void decoder::samples::reset(){
    lock();
        doreset = true;
        if( resetprotected <= 0 ){
            PRIVATERESET();
        }
    unlock();
}
int decoder::samples::totallength(){
    return writepos;
}
int decoder::samples::pos(){
    return readpos;
}
