//Copyright 2007 Asim Siddiqui
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//
//
#include <ios>
#include <iostream>
#include <fstream>
#include <string>
#include <SRF_Ztr.hh>


/*
 * ztr_read_header
 *
 * Reads a ZTR file header.
 *
 * Arguments:
 *      fp              A FILE pointer
 *      h               Where to write the header to
 *
 * Returns:
 *      Success:  0
 *      Failure: -1
 */
static int ztr_read_header(std::stringstream& ztrStr, ztr_header_t *h)
{
    ztrStr.read( reinterpret_cast<char *>(h), sizeof(*h) );

    return 0;
}

/*
 * ztr_read_chunk_hdr
 *
 * Reads a ZTR chunk header and metadata, but not the main data segment.
 *
 * Arguments:
 *      fp              A FILE pointer
 *
 * Returns:
 *      Success: a chunk pointer (malloced)
 *      Failure: NULL
 */
static ztr_chunk_t *ztr_read_chunk_hdr( std::stringstream& ztrStr ) {
    int4 bei4;
    ztr_chunk_t *chunk;

    (void)ztrStr.tellg(); /* force eof() update */
    if ( ztrStr.eof() )
    {
        return NULL;
    }
    chunk = new ztr_chunk_t;

    /* type */
    ztrStr.read( reinterpret_cast<char *>(&bei4), 4 );
    if (ztrStr.fail() || ztrStr.eof())
	return NULL;

//    if (1 != fread(&bei4, 4, 1, fp)) {
//        xfree(chunk);
//        return NULL;
//    }
    chunk->type = be_int4(bei4);

    /* metadata length */
    ztrStr.read( reinterpret_cast<char *>(&bei4), 4 );
//    if (1 != fread(&bei4, 4, 1, fp)) {
//        xfree(chunk);
//        return NULL;
//    }
    chunk->mdlength = be_int4(bei4);

    /* metadata */
    if (chunk->mdlength) 
    {
        if (NULL == (chunk->mdata = (char *)malloc(chunk->mdlength)))
            return NULL;

        ztrStr.read( reinterpret_cast<char *>(chunk->mdata), chunk->mdlength );

	//std::cout << "mdlength = " << chunk->mdlength << std::endl;

//        if (chunk->mdlength != fread(chunk->mdata, 1, chunk->mdlength, fp)) {
//            xfree(chunk->mdata);
//            xfree(chunk);
//            return NULL;
//        }
    }
    else 
    {
        chunk->mdata = NULL;
    }

    /* data length */
    ztrStr.read( reinterpret_cast<char *>(&bei4), 4 );
//    if (1 != fread(&bei4, 4, 1, fp)) {
//        xfree(chunk->mdata);
//        xfree(chunk);
//        return NULL;
//    }
    chunk->dlength = be_int4(bei4);

    return chunk;
}

/*
 * fread_ztr
 *
 * Reads a ZTR file from 'fp'. This checks for the correct magic number and
 * major version number, but not minor version number.
 *
 * FIXME: Add automatic uncompression?
 *
 * Arguments:
 *      fp              A readable FILE pointer
 *
 * Returns:
 *      Success: Pointer to a ztr_t structure (malloced)
 *      Failure: NULL
 */
ztr_t *read_ztr( std::stringstream& ztrStr )
{
    ztr_t *ztr;
    ztr_chunk_t *chunk;
    int sections = read_sections(0);

    /* Allocate */
    if (NULL == (ztr = new_ztr()))
        return NULL;

    /* Read the header */
    if (-1 == ztr_read_header(ztrStr, &ztr->header))
        return NULL;

    /* Check magic number and version */
    if (memcmp(ztr->header.magic, ZTR_MAGIC, 8) != 0)
        return NULL;

    if (ztr->header.version_major != ZTR_VERSION_MAJOR)
        return NULL;

    /* Load chunks */
    while (chunk = ztr_read_chunk_hdr(ztrStr)) {
        /*
        char str[5];

        fprintf(stderr, "Read chunk %.4s %08x length %d\n",
                ZTR_BE2STR(chunk->type, str), chunk->type, chunk->dlength);
        */
        switch(chunk->type) {
        case ZTR_TYPE_HEADER:
            /* End of file */
            return ztr;

        case ZTR_TYPE_SAMP:
        case ZTR_TYPE_SMP4:
            if (! (sections & READ_SAMPLES)) {
                ztrStr.seekg( chunk->dlength, std::ios_base::cur );
//                fseek(ztrStr, chunk->dlength, SEEK_CUR);
                delete chunk;
                chunk = NULL;
                continue;
            }
            break;


        case ZTR_TYPE_BASE:
        case ZTR_TYPE_BPOS:
        case ZTR_TYPE_CNF4:
        case ZTR_TYPE_CNF1:
        case ZTR_TYPE_CSID:
            if (! (sections & READ_BASES)) {
                ztrStr.seekg( chunk->dlength, std::ios_base::cur );
                delete chunk;
                chunk = NULL;
//                fseek(ztrStr, chunk->dlength, SEEK_CUR);
//                xfree(chunk);
                continue;
            }
            break;

        case ZTR_TYPE_TEXT:
            if (! (sections & READ_COMMENTS)) {
                ztrStr.seekg( chunk->dlength, std::ios_base::cur );
                delete chunk;
                chunk = NULL;
//                fseek(ztrStr, chunk->dlength, SEEK_CUR);
//                xfree(chunk);
                continue;
            }
            break;

        case ZTR_TYPE_CLIP:
        case ZTR_TYPE_FLWO:
        case ZTR_TYPE_FLWC:
            break;

            /*
        default:
            fprintf(stderr, "Unknown chunk type '%s': skipping\n",
                    ZTR_BE2STR(chunk->type,str));
            ztrStr.seekg( chunk->dlength, std::ios_base::cur );
            delete chunk;
            chunk = NULL;
//            fseek(ztrStr, chunk->dlength, SEEK_CUR);
//            xfree(chunk);
            continue;
            */
        }

        chunk->data = (char *)malloc(chunk->dlength);
	chunk->ztr_owns = 1;
        ztrStr.read( reinterpret_cast<char *>(chunk->data), chunk->dlength );
//        if (chunk->dlength != fread(chunk->data, 1, chunk->dlength, ztrStr))
//            return NULL;

        ztr->nchunks++;
        ztr->chunk = (ztr_chunk_t *)realloc(ztr->chunk, ztr->nchunks *
                                             sizeof(ztr_chunk_t));
        memcpy(&ztr->chunk[ztr->nchunks-1], chunk, sizeof(*chunk));
        delete chunk;
    }

    return ztr;
}

uint32_t
SRF_calcSizeZTR( const ztr_t* ztr )
{
    if ( ztr == NULL )
    {
        return 0;
    }
    uint32_t size = sizeof( ztr->header );
    for (int i = 0; i < ztr->nchunks; i++)
    {
        size += 12 + ztr->chunk[i].mdlength + ztr->chunk[i].dlength;
    }

    return size;
}
 
int
SRF_writeZtr( const ztr_t* ztr, std::fstream& file )
{
//    fwrite_ztr( outputFile.file(), ztr );
    file.write(reinterpret_cast<const char*>(&(ztr->header)), sizeof(ztr->header));

    /* Write the chunks */
    for (int i = 0; i < ztr->nchunks; i++)
    {
        ztr_chunk_t* chunk = &ztr->chunk[i];
        /* type */
        int4 bei4 = be_int4(chunk->type);
        file.write(reinterpret_cast<const char*>(&bei4), 4);

        /* metadata length */
        bei4 = be_int4(chunk->mdlength);
        file.write(reinterpret_cast<const char*>(&bei4), 4);

        /* metadata */
        if (chunk->mdlength)
            file.write(reinterpret_cast<const char*>(chunk->mdata), chunk->mdlength);

        /* data length */
        bei4 = be_int4(chunk->dlength);
        file.write(reinterpret_cast<const char*>(&bei4), 4);

        /* data */
        if ( chunk->data )
             file.write(reinterpret_cast<const char*>(chunk->data), chunk->dlength);

    }
}

