/*
 * Copyright (C) 2007 Loren Merritt <lorenm@u.washington.edu>
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "common.h"

static int read_stream_header( xlsc_t *h, uint8_t *header, int header_size )
{
    if( header_size != 8 )
        return -1;
    if( memcmp( header, "XLSC", 4 ) != 0 )
        return -1;

    h->param.width  = header[4]<<8 | header[5];
    h->param.height = header[6]<<8 | header[7];

    return 0;
}

xlsc_t *xlsc_decoder_open( xlsc_param_t *param, uint8_t *header, int header_size )
{
    xlsc_t *h = malloc( sizeof(xlsc_t) );
    memset( h, 0, sizeof(xlsc_t) );

    h->decoding = 1;

    if( read_stream_header( h, header, header_size ) )
    {
        fprintf( stderr, "xlsc [error]: invalid stream header\n" );
        free( h );
        return NULL;
    }

    *param = h->param;

    xlsc_dsp_init( h );

    h->mb_width  = (h->param.width  + 15) / 16;
    h->mb_height = (h->param.height + 15) / 16;
    h->mb_count = h->mb_width * h->mb_height;
    h->stride = 16*h->mb_width + 2*FRAME_PAD;

    h->fdec = xlsc_frame_new( h );
    h->fref = xlsc_frame_new( h );

    xlsc_macroblock_init( h );
    xlsc_build_rac_states( &h->out.c, 0.05*(1LL<<32), 256-8 );

    return h;
}

int xlsc_decoder_close( xlsc_t *h )
{
    xlsc_frame_free( h->fdec );
    xlsc_frame_free( h->fref );
    free( h );
    return 0;
}

static void read_frame_header( xlsc_t *h, uint8_t *buffer, int size )
{
    h->fdec->intra = buffer[0] >> 7;
    h->fdec->qp = h->qp = buffer[0] & 0x7f;
    if( h->fdec->intra )
        memset( &h->model, 128, sizeof(h->model) );
    xlsc_init_range_decoder( &h->out.c, buffer+1, size-1 );
}

static void update_reference_list( xlsc_t *h )
{
    xlsc_frame_expand_border( h, h->fdec );
    XCHG( xlsc_frame_t*, h->fref, h->fdec );
}

int xlsc_decoder_decode( xlsc_t *h, uint8_t *buffer, int size, xlsc_picture_t *pic )
{
    read_frame_header( h, buffer, size );
    xlsc_macroblock_frame_start( h );

    int x, y;
    for( y = 0; y < h->mb_height; y++ )
        for( x = 0; x < h->mb_width; x++ )
        {
            xlsc_macroblock_load( h, x, y );
            xlsc_macroblock_read( h );
            xlsc_macroblock_decode( h );
        }

    xlsc_frame_to_pic( h, h->fdec, pic );
    update_reference_list( h );

    return 0;
}
