/*
 * $Id$
 *
 * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
 * Copyright (c) 2002-2014, Professor Benoit Macq
 * Copyright (c) 2010-2011, Kaori Hagihara
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include "cachemodel_manager.h"
#include "faixbox_manager.h"
#include "opj_inttypes.h"

#ifdef SERVER
#include "fcgi_stdio.h"
#define logstream FCGI_stdout
#else
#define FCGI_stdout stdout
#define FCGI_stderr stderr
#define logstream stderr
#endif /*SERVER*/


cachemodellist_param_t * gene_cachemodellist(void)
{
    cachemodellist_param_t *cachemodellist;

    cachemodellist = (cachemodellist_param_t *)opj_malloc(sizeof(
                         cachemodellist_param_t));

    cachemodellist->first = NULL;
    cachemodellist->last  = NULL;

    return cachemodellist;
}

cachemodel_param_t * gene_cachemodel(cachemodellist_param_t *cachemodellist,
                                     target_param_t *target, OPJ_BOOL reqJPP)
{
    cachemodel_param_t *cachemodel;
    faixbox_param_t *tilepart;
    faixbox_param_t *precpacket;
    size_t numOfelem;
    Byte8_t numOftiles;
    int i;

    cachemodel = (cachemodel_param_t *)opj_malloc(sizeof(cachemodel_param_t));

    refer_target(target, &cachemodel->target);

    if (reqJPP) {
        if (target->jppstream) {
            cachemodel->jppstream = OPJ_TRUE;
        } else {
            cachemodel->jppstream = OPJ_FALSE;
        }
    } else { /* reqJPT */
        if (target->jptstream) {
            cachemodel->jppstream = OPJ_FALSE;
        } else {
            cachemodel->jppstream = OPJ_TRUE;
        }
    }

    cachemodel->mhead_model = OPJ_FALSE;

    tilepart = target->codeidx->tilepart;
    numOftiles = get_m(tilepart);
    numOfelem = get_nmax(tilepart) * numOftiles;
    cachemodel->tp_model = (OPJ_BOOL *)opj_calloc(1, numOfelem * sizeof(OPJ_BOOL));
    cachemodel->th_model = (OPJ_BOOL *)opj_calloc(1, numOftiles * sizeof(OPJ_BOOL));
    cachemodel->pp_model = (OPJ_BOOL **)opj_malloc(target->codeidx->SIZ.Csiz *
                           sizeof(OPJ_BOOL *));
    for (i = 0; i < target->codeidx->SIZ.Csiz; i++) {
        precpacket = target->codeidx->precpacket[i];
        cachemodel->pp_model[i] = (OPJ_BOOL *)opj_calloc(1,
                                  get_nmax(precpacket) * get_m(precpacket) * sizeof(OPJ_BOOL));
    }
    cachemodel->next = NULL;

    if (cachemodellist) {
        if (cachemodellist->first) { /* there are one or more entries */
            cachemodellist->last->next = cachemodel;
        } else {               /* first entry */
            cachemodellist->first = cachemodel;
        }
        cachemodellist->last = cachemodel;
    }

#ifndef SERVER
    fprintf(logstream, "local log: cachemodel generated\n");
#endif

    return cachemodel;
}

void print_cachemodel(cachemodel_param_t cachemodel)
{
    target_param_t *target;
    Byte8_t TPnum; /* num of tile parts in each tile */
    Byte8_t Pmax; /* max num of packets per tile */
    Byte8_t i, j, k;
    int n; /* FIXME: Is this large enough ? */

    target = cachemodel.target;

    fprintf(logstream, "target: %s\n", target->targetname);
    fprintf(logstream, "\t main header model: %d\n", cachemodel.mhead_model);

    fprintf(logstream, "\t tile part model:\n");
    TPnum = get_nmax(target->codeidx->tilepart);

    for (i = 0, n = 0; i < target->codeidx->SIZ.YTnum; i++) {
        for (j = 0; j < target->codeidx->SIZ.XTnum; j++) {
            for (k = 0; k < TPnum; k++) {
                fprintf(logstream, "%d", cachemodel.tp_model[n++]);
            }
            fprintf(logstream, " ");
        }
        fprintf(logstream, "\n");
    }

    fprintf(logstream, "\t tile header and precinct packet model:\n");
    for (i = 0; i < target->codeidx->SIZ.XTnum * target->codeidx->SIZ.YTnum; i++) {
        fprintf(logstream, "\t  tile.%" PRIu64 "  %d\n", i, cachemodel.th_model[i]);
        for (j = 0; j < target->codeidx->SIZ.Csiz; j++) {
            fprintf(logstream, "\t   compo.%" PRIu64 ": ", j);
            Pmax = get_nmax(target->codeidx->precpacket[j]);
            for (k = 0; k < Pmax; k++) {
                fprintf(logstream, "%d", cachemodel.pp_model[j][i * Pmax + k]);
            }
            fprintf(logstream, "\n");
        }
    }
}

cachemodel_param_t * search_cachemodel(target_param_t *target,
                                       cachemodellist_param_t *cachemodellist)
{
    cachemodel_param_t *foundcachemodel;

    foundcachemodel = cachemodellist->first;

    while (foundcachemodel != NULL) {

        if (foundcachemodel->target == target) {
            return foundcachemodel;
        }

        foundcachemodel = foundcachemodel->next;
    }
    return NULL;
}

void delete_cachemodellist(cachemodellist_param_t **cachemodellist)
{
    cachemodel_param_t *cachemodelPtr, *cachemodelNext;

    cachemodelPtr = (*cachemodellist)->first;
    while (cachemodelPtr != NULL) {
        cachemodelNext = cachemodelPtr->next;
        delete_cachemodel(&cachemodelPtr);
        cachemodelPtr = cachemodelNext;
    }
    opj_free(*cachemodellist);
}

void delete_cachemodel(cachemodel_param_t **cachemodel)
{
    int i;

    unrefer_target((*cachemodel)->target);

    opj_free((*cachemodel)->tp_model);
    opj_free((*cachemodel)->th_model);

    for (i = 0; i < (*cachemodel)->target->codeidx->SIZ.Csiz; i++) {
        opj_free((*cachemodel)->pp_model[i]);
    }
    opj_free((*cachemodel)->pp_model);

#ifndef SERVER
    fprintf(logstream, "local log: cachemodel deleted\n");
#endif
    opj_free(*cachemodel);
}

OPJ_BOOL is_allsent(cachemodel_param_t cachemodel)
{
    target_param_t *target;
    Byte8_t TPnum; /* num of tile parts in each tile */
    Byte8_t Pmax; /* max num of packets per tile */
    Byte8_t i, j, k;
    int n; /* FIXME: is this large enough ? */

    target = cachemodel.target;

    if (!cachemodel.mhead_model) {
        return OPJ_FALSE;
    }

    TPnum = get_nmax(target->codeidx->tilepart);

    if (cachemodel.jppstream) {
        for (i = 0; i < target->codeidx->SIZ.XTnum * target->codeidx->SIZ.YTnum; i++) {
            if (!cachemodel.th_model[i]) {
                return OPJ_FALSE;
            }

            for (j = 0; j < target->codeidx->SIZ.Csiz; j++) {
                Pmax = get_nmax(target->codeidx->precpacket[j]);
                for (k = 0; k < Pmax; k++)
                    if (!cachemodel.pp_model[j][i * Pmax + k]) {
                        return OPJ_FALSE;
                    }
            }
        }
        return OPJ_TRUE;
    } else {
        for (i = 0, n = 0; i < target->codeidx->SIZ.YTnum; i++)
            for (j = 0; j < target->codeidx->SIZ.XTnum; j++)
                for (k = 0; k < TPnum; k++)
                    if (!cachemodel.tp_model[n++]) {
                        return OPJ_FALSE;
                    }
        return OPJ_TRUE;
    }
}
