/*
 * The copyright in this software is being made available under the 2-clauses
 * BSD License, included below. This software may be subject to other third
 * party and contributor rights, including patent rights, and no such rights
 * are granted under this license.
 *
 * Copyright (c) 2001-2003, David Janssens
 * Copyright (c) 2002-2003, Yannick Verschueren
 * Copyright (c) 2003-2005, Francois Devaux and Antonin Descampe
 * Copyright (c) 2005, Herve Drolon, FreeImage Team
 * Copyright (c) 2002-2005, Communications and remote sensing Laboratory, Universite catholique de Louvain, Belgium
 * Copyright (c) 2005-2006, Dept. of Electronic and Information Engineering, Universita' degli Studi di Perugia, Italy
 * 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 "opj_includes.h"

#ifdef USE_JPWL

/** @defgroup JPWL JPWL - JPEG-2000 Part11 (JPWL) codestream manager */
/*@{*/

/** @name Local static variables */
/*@{*/

/** number of JPWL prepared markers */
static int jwmarker_num;
/** properties of JPWL markers to insert */
static jpwl_marker_t jwmarker[JPWL_MAX_NO_MARKERS];

/*@}*/

/*@}*/

/** @name Local static functions */
/*@{*/

/** create an EPC marker segment
@param j2k J2K compressor handle
@param esd_on true if ESD is activated
@param red_on true if RED is activated
@param epb_on true if EPB is activated
@param info_on true if informative techniques are activated
@return returns the freshly created EPC
*/
jpwl_epc_ms_t *jpwl_epc_create(opj_j2k_t *j2k, opj_bool esd_on, opj_bool red_on,
                               opj_bool epb_on, opj_bool info_on);

/*@}*/

/** create an EPC marker segment
@param j2k J2K compressor handle
@param comps considered component (-1=average, 0/1/2/...=component no.)
@param addrm addressing mode (0=packet, 1=byte range, 2=packet range, 3=reserved)
@param ad_size size of addresses (2/4 bytes)
@param senst sensitivity type
@param se_size sensitivity values size (1/2 bytes)
@param tileno tile where this ESD lies (-1 means MH)
@param svalnum number of sensitivity values (if 0, they will be automatically filled)
@param sensval pointer to an array of sensitivity values (if NULL, they will be automatically filled)
@return returns the freshly created ESD
*/
jpwl_esd_ms_t *jpwl_esd_create(opj_j2k_t *j2k, int comps,
                               unsigned char addrm, unsigned char ad_size,
                               unsigned char senst, int se_size, int tileno,
                               unsigned long int svalnum, void *sensval);

/** this function is used to compare two JPWL markers based on
their relevant wishlist position
@param arg1 pointer to first marker
@param arg2 pointer to second marker
@return 1 if arg1>arg2, 0 if arg1=arg2, -1 if arg1<arg2
*/
int jpwl_markcomp(const void *arg1, const void *arg2);

/** write an EPB MS to a buffer
@param j2k J2K compressor handle
@param epbmark pointer to the EPB MS
@param buf pointer to the memory buffer
*/
void jpwl_epb_write(opj_j2k_t *j2k, jpwl_epb_ms_t *epbmark, unsigned char *buf);

/** write an EPC MS to a buffer
@param j2k J2K compressor handle
@param epcmark pointer to the EPC MS
@param buf pointer to the memory buffer
*/
void jpwl_epc_write(opj_j2k_t *j2k, jpwl_epc_ms_t *epcmark, unsigned char *buf);

/**
 * write an ESD MS to a buffer
@param j2k J2K compressor handle
@param esd pointer to the ESD MS
@param buf pointer to the memory buffer
*/
void jpwl_esd_write(opj_j2k_t *j2k, jpwl_esd_ms_t *esdmark, unsigned char *buf);

/*-----------------------------------------------------------------*/

void jpwl_encode(opj_j2k_t *j2k, opj_cio_t *cio, opj_image_t *image)
{

    int mm;

    /* let's reset some settings */

    /* clear the existing markers */
    for (mm = 0; mm < jwmarker_num; mm++) {

        switch (jwmarker[mm].id) {

        case J2K_MS_EPB:
            opj_free(jwmarker[mm].m.epbmark);
            break;

        case J2K_MS_EPC:
            opj_free(jwmarker[mm].m.epcmark);
            break;

        case J2K_MS_ESD:
            opj_free(jwmarker[mm].m.esdmark);
            break;

        case J2K_MS_RED:
            opj_free(jwmarker[mm].m.redmark);
            break;

        default:
            break;
        }
    }

    /* clear the marker structure array */
    memset(jwmarker, 0, sizeof(jpwl_marker_t) * JPWL_MAX_NO_MARKERS);

    /* no more markers in the list */
    jwmarker_num = 0;

    /* let's begin creating a marker list, according to user wishes */
    jpwl_prepare_marks(j2k, cio, image);

    /* now we dump the JPWL markers on the codestream */
    jpwl_dump_marks(j2k, cio, image);

    /* do not know exactly what is this for,
    but it gets called during index creation */
    j2k->pos_correction = 0;

}

opj_bool j2k_add_marker(opj_codestream_info_t *cstr_info,
                        unsigned short int type, int pos, int len)
{

    if (!cstr_info) {
        return OPJ_FALSE;
    }

    /* expand the list? */
    if ((cstr_info->marknum + 1) > cstr_info->maxmarknum) {
        opj_marker_info_t* new_marker;
        cstr_info->maxmarknum += 100;
        new_marker = (opj_marker_info_t*)opj_realloc(cstr_info->marker,
                     cstr_info->maxmarknum * sizeof(opj_marker_info_t));
        if (! new_marker) {
            opj_free(cstr_info->marker);
            cstr_info->marker = 0;
            cstr_info->marknum = 0;
            cstr_info->maxmarknum = 0;
            /* opj_event_msg_v2(p_manager, EVT_ERROR, "Not enough memory to add a marker\n"); */
            /* TODO_test_add_marker_result;*/
            return OPJ_FALSE;
        }
        cstr_info->marker = new_marker;
    }

    /* add the marker */
    cstr_info->marker[cstr_info->marknum].type = type;
    cstr_info->marker[cstr_info->marknum].pos = pos;
    cstr_info->marker[cstr_info->marknum].len = len;
    cstr_info->marknum++;
    return OPJ_TRUE;

}

void jpwl_prepare_marks(opj_j2k_t *j2k, opj_cio_t *cio, opj_image_t *image)
{

    unsigned short int socsiz_len = 0;
    int ciopos = cio_tell(cio), soc_pos = j2k->cstr_info->main_head_start;
    unsigned char *socp = NULL;

    int tileno, acc_tpno, tpno, tilespec, hprot, sens, pprot, packspec, lastileno,
        packno;

    jpwl_epb_ms_t *epb_mark;
    jpwl_epc_ms_t *epc_mark;
    jpwl_esd_ms_t *esd_mark;
    (void)image;

    /* find (SOC + SIZ) length */
    /* I assume SIZ is always the first marker after SOC */
    cio_seek(cio, soc_pos + 4);
    socsiz_len = (unsigned short int) cio_read(cio,
                 2) + 4; /* add the 2 marks length itself */
    cio_seek(cio, soc_pos + 0);
    socp = cio_getbp(cio); /* pointer to SOC */

    /*
     EPC MS for Main Header: if we are here it's required
    */
    /* create the EPC */
    if ((epc_mark = jpwl_epc_create(
                        j2k,
                        j2k->cp->esd_on, /* is ESD present? */
                        j2k->cp->red_on, /* is RED present? */
                        j2k->cp->epb_on, /* is EPB present? */
                        OPJ_FALSE /* are informative techniques present? */
                    ))) {

        /* Add this marker to the 'insertanda' list */
        if (epc_mark) {
            jwmarker[jwmarker_num].id = J2K_MS_EPC; /* its type */
            jwmarker[jwmarker_num].m.epcmark = epc_mark; /* the EPC */
            jwmarker[jwmarker_num].pos = soc_pos + socsiz_len; /* after SIZ */
            jwmarker[jwmarker_num].dpos = (double) jwmarker[jwmarker_num].pos +
                                          0.1; /* not so first */
            jwmarker[jwmarker_num].len = epc_mark->Lepc; /* its length */
            jwmarker[jwmarker_num].len_ready = OPJ_TRUE; /* ready */
            jwmarker[jwmarker_num].pos_ready = OPJ_TRUE; /* ready */
            jwmarker[jwmarker_num].parms_ready = OPJ_FALSE; /* not ready */
            jwmarker[jwmarker_num].data_ready = OPJ_TRUE; /* ready */
            jwmarker_num++;
        };

        opj_event_msg(j2k->cinfo, EVT_INFO,
                      "MH  EPC : setting %s%s%s\n",
                      j2k->cp->esd_on ? "ESD, " : "",
                      j2k->cp->red_on ? "RED, " : "",
                      j2k->cp->epb_on ? "EPB, " : ""
                     );

    } else {
        /* ooops, problems */
        opj_event_msg(j2k->cinfo, EVT_ERROR, "Could not create MH EPC\n");
    };

    /*
     ESD MS for Main Header
    */
    /* first of all, must MH have an ESD MS? */
    if (j2k->cp->esd_on && (j2k->cp->sens_MH >= 0)) {

        /* Create the ESD */
        if ((esd_mark = jpwl_esd_create(
                            j2k, /* this encoder handle */
                            -1, /* we are averaging over all components */
                            (unsigned char) j2k->cp->sens_range, /* range method */
                            (unsigned char) j2k->cp->sens_addr, /* sensitivity addressing */
                            (unsigned char) j2k->cp->sens_MH, /* sensitivity method */
                            j2k->cp->sens_size, /* sensitivity size */
                            -1, /* this ESD is in main header */
                            0 /*j2k->cstr_info->num*/, /* number of packets in codestream */
                            NULL /*sensval*/ /* pointer to sensitivity data of packets */
                        ))) {

            /* Add this marker to the 'insertanda' list */
            if (jwmarker_num < JPWL_MAX_NO_MARKERS) {
                jwmarker[jwmarker_num].id = J2K_MS_ESD; /* its type */
                jwmarker[jwmarker_num].m.esdmark = esd_mark; /* the EPB */
                jwmarker[jwmarker_num].pos = soc_pos +
                                             socsiz_len; /* we choose to place it after SIZ */
                jwmarker[jwmarker_num].dpos = (double) jwmarker[jwmarker_num].pos +
                                              0.2; /* not first at all! */
                jwmarker[jwmarker_num].len = esd_mark->Lesd; /* its length */
                jwmarker[jwmarker_num].len_ready = OPJ_TRUE; /* not ready, yet */
                jwmarker[jwmarker_num].pos_ready = OPJ_TRUE; /* ready */
                jwmarker[jwmarker_num].parms_ready = OPJ_TRUE; /* not ready */
                jwmarker[jwmarker_num].data_ready = OPJ_FALSE; /* not ready */
                jwmarker_num++;
            }

            opj_event_msg(j2k->cinfo, EVT_INFO,
                          "MH  ESDs: method %d\n",
                          j2k->cp->sens_MH
                         );

        } else {
            /* ooops, problems */
            opj_event_msg(j2k->cinfo, EVT_ERROR, "Could not create MH ESD\n");
        };

    }

    /*
     ESD MSs for Tile Part Headers
    */
    /* cycle through tiles */
    sens = -1; /* default spec: no ESD */
    tilespec = 0; /* first tile spec */
    acc_tpno = 0;
    for (tileno = 0; tileno < j2k->cstr_info->tw * j2k->cstr_info->th; tileno++) {

        opj_event_msg(j2k->cinfo, EVT_INFO,
                      "Tile %d has %d tile part(s)\n",
                      tileno, j2k->cstr_info->tile[tileno].num_tps
                     );

        /* for every tile part in the tile */
        for (tpno = 0; tpno < j2k->cstr_info->tile[tileno].num_tps;
                tpno++, acc_tpno++) {

            int sot_len, Psot, Psotp, mm;
            unsigned long sot_pos, post_sod_pos;

            unsigned long int left_THmarks_len;

            /******* sot_pos = j2k->cstr_info->tile[tileno].start_pos; */
            sot_pos = j2k->cstr_info->tile[tileno].tp[tpno].tp_start_pos;
            cio_seek(cio, sot_pos + 2);
            sot_len = cio_read(cio, 2); /* SOT Len */
            cio_skip(cio, 2);
            Psotp = cio_tell(cio);
            Psot = cio_read(cio, 4); /* tile length */

            /******* post_sod_pos = j2k->cstr_info->tile[tileno].end_header + 1; */
            post_sod_pos = j2k->cstr_info->tile[tileno].tp[tpno].tp_end_header + 1;
            left_THmarks_len = post_sod_pos - sot_pos;

            /* add all the lengths of the markers which are len-ready and stay within SOT and SOD */
            for (mm = 0; mm < jwmarker_num; mm++) {
                if ((jwmarker[mm].pos >= sot_pos) && (jwmarker[mm].pos < post_sod_pos)) {
                    if (jwmarker[mm].len_ready) {
                        left_THmarks_len += jwmarker[mm].len + 2;
                    } else {
                        opj_event_msg(j2k->cinfo, EVT_ERROR,
                                      "MS %x in %f is not len-ready: could not set up TH EPB\n",
                                      jwmarker[mm].id, jwmarker[mm].dpos);
                        exit(1);
                    }
                }
            }

            /******* if ((tilespec < JPWL_MAX_NO_TILESPECS) && (j2k->cp->sens_TPH_tileno[tilespec] == tileno)) */
            if ((tilespec < JPWL_MAX_NO_TILESPECS) &&
                    (j2k->cp->sens_TPH_tileno[tilespec] == acc_tpno))
                /* we got a specification from this tile onwards */
            {
                sens = j2k->cp->sens_TPH[tilespec++];
            }

            /* must this TPH have an ESD MS? */
            if (j2k->cp->esd_on && (sens >= 0)) {

                /* Create the ESD */
                if ((esd_mark = jpwl_esd_create(
                                    j2k, /* this encoder handle */
                                    -1, /* we are averaging over all components */
                                    (unsigned char) j2k->cp->sens_range, /* range method */
                                    (unsigned char) j2k->cp->sens_addr, /* sensitivity addressing size */
                                    (unsigned char) sens, /* sensitivity method */
                                    j2k->cp->sens_size, /* sensitivity value size */
                                    tileno, /* this ESD is in a tile */
                                    0, /* number of packets in codestream */
                                    NULL /* pointer to sensitivity data of packets */
                                ))) {

                    /* Add this marker to the 'insertanda' list */
                    if (jwmarker_num < JPWL_MAX_NO_MARKERS) {
                        jwmarker[jwmarker_num].id = J2K_MS_ESD; /* its type */
                        jwmarker[jwmarker_num].m.esdmark = esd_mark; /* the EPB */
                        /****** jwmarker[jwmarker_num].pos = j2k->cstr_info->tile[tileno].start_pos + sot_len + 2; */ /* after SOT */
                        jwmarker[jwmarker_num].pos = j2k->cstr_info->tile[tileno].tp[tpno].tp_start_pos
                                                     + sot_len + 2; /* after SOT */
                        jwmarker[jwmarker_num].dpos = (double) jwmarker[jwmarker_num].pos +
                                                      0.2; /* not first at all! */
                        jwmarker[jwmarker_num].len = esd_mark->Lesd; /* its length */
                        jwmarker[jwmarker_num].len_ready = OPJ_TRUE; /* ready, yet */
                        jwmarker[jwmarker_num].pos_ready = OPJ_TRUE; /* ready */
                        jwmarker[jwmarker_num].parms_ready = OPJ_TRUE; /* not ready */
                        jwmarker[jwmarker_num].data_ready = OPJ_FALSE; /* ready */
                        jwmarker_num++;
                    }

                    /* update Psot of the tile  */
                    cio_seek(cio, Psotp);
                    cio_write(cio, Psot + esd_mark->Lesd + 2, 4);

                    opj_event_msg(j2k->cinfo, EVT_INFO,
                                  /******* "TPH ESDs: tile %02d, method %d\n", */
                                  "TPH ESDs: tile %02d, part %02d, method %d\n",
                                  /******* tileno, */
                                  tileno, tpno,
                                  sens
                                 );

                } else {
                    /* ooops, problems */
                    /***** opj_event_msg(j2k->cinfo, EVT_ERROR, "Could not create TPH ESD #%d\n", tileno); */
                    opj_event_msg(j2k->cinfo, EVT_ERROR, "Could not create TPH ESD #%d,%d\n",
                                  tileno, tpno);
                };

            }

        }

    };

    /*
     EPB MS for Main Header
    */
    /* first of all, must MH have an EPB MS? */
    if (j2k->cp->epb_on && (j2k->cp->hprot_MH > 0)) {

        int mm;

        /* position of SOT */
        unsigned int sot_pos = j2k->cstr_info->main_head_end + 1;

        /* how much space is there between end of SIZ and beginning of SOT? */
        int left_MHmarks_len = sot_pos - socsiz_len;

        /* add all the lengths of the markers which are len-ready and stay within SOC and SOT */
        for (mm = 0; mm < jwmarker_num; mm++) {
            if (jwmarker[mm].pos < sot_pos) {  /* jwmarker[mm].pos >=0 since ulong */
                if (jwmarker[mm].len_ready) {
                    left_MHmarks_len += jwmarker[mm].len + 2;
                } else {
                    opj_event_msg(j2k->cinfo, EVT_ERROR,
                                  "MS %x in %f is not len-ready: could not set up MH EPB\n",
                                  jwmarker[mm].id, jwmarker[mm].dpos);
                    exit(1);
                }
            }
        }

        /* Create the EPB */
        if ((epb_mark = jpwl_epb_create(
                            j2k, /* this encoder handle */
                            OPJ_TRUE, /* is it the latest? */
                            OPJ_TRUE, /* is it packed? not for now */
                            -1, /* we are in main header */
                            0, /* its index is 0 (first) */
                            j2k->cp->hprot_MH, /* protection type parameters of data */
                            socsiz_len, /* pre-data: only SOC+SIZ */
                            left_MHmarks_len /* post-data: from SOC to SOT, and all JPWL markers within */
                        ))) {

            /* Add this marker to the 'insertanda' list */
            if (jwmarker_num < JPWL_MAX_NO_MARKERS) {
                jwmarker[jwmarker_num].id = J2K_MS_EPB; /* its type */
                jwmarker[jwmarker_num].m.epbmark = epb_mark; /* the EPB */
                jwmarker[jwmarker_num].pos = soc_pos + socsiz_len; /* after SIZ */
                jwmarker[jwmarker_num].dpos = (double)
                                              jwmarker[jwmarker_num].pos; /* first first first! */
                jwmarker[jwmarker_num].len = epb_mark->Lepb; /* its length */
                jwmarker[jwmarker_num].len_ready = OPJ_TRUE; /* ready */
                jwmarker[jwmarker_num].pos_ready = OPJ_TRUE; /* ready */
                jwmarker[jwmarker_num].parms_ready = OPJ_TRUE; /* ready */
                jwmarker[jwmarker_num].data_ready = OPJ_FALSE; /* not ready */
                jwmarker_num++;
            }

            opj_event_msg(j2k->cinfo, EVT_INFO,
                          "MH  EPB : prot. %d\n",
                          j2k->cp->hprot_MH
                         );

        } else {
            /* ooops, problems */
            opj_event_msg(j2k->cinfo, EVT_ERROR, "Could not create MH EPB\n");
        };
    }

    /*
     EPB MSs for Tile Parts
    */
    /* cycle through TPHs */
    hprot = j2k->cp->hprot_MH; /* default spec */
    tilespec = 0; /* first tile spec */
    lastileno = 0;
    packspec = 0;
    pprot = -1;
    acc_tpno = 0;
    for (tileno = 0; tileno < j2k->cstr_info->tw * j2k->cstr_info->th; tileno++) {

        opj_event_msg(j2k->cinfo, EVT_INFO,
                      "Tile %d has %d tile part(s)\n",
                      tileno, j2k->cstr_info->tile[tileno].num_tps
                     );

        /* for every tile part in the tile */
        for (tpno = 0; tpno < j2k->cstr_info->tile[tileno].num_tps;
                tpno++, acc_tpno++) {

            int sot_len, Psot, Psotp, mm, epb_index = 0, prot_len = 0;
            unsigned long sot_pos, post_sod_pos;
            unsigned long int left_THmarks_len/*, epbs_len = 0*/;
            int startpack = 0, stoppack = j2k->cstr_info->packno;
            int first_tp_pack, last_tp_pack;
            jpwl_epb_ms_t *tph_epb = NULL;

            /****** sot_pos = j2k->cstr_info->tile[tileno].start_pos; */
            sot_pos = j2k->cstr_info->tile[tileno].tp[tpno].tp_start_pos;
            cio_seek(cio, sot_pos + 2);
            sot_len = cio_read(cio, 2); /* SOT Len */
            cio_skip(cio, 2);
            Psotp = cio_tell(cio);
            Psot = cio_read(cio, 4); /* tile length */

            /* a-priori length of the data dwelling between SOT and SOD */
            /****** post_sod_pos = j2k->cstr_info->tile[tileno].end_header + 1; */
            post_sod_pos = j2k->cstr_info->tile[tileno].tp[tpno].tp_end_header + 1;
            left_THmarks_len = post_sod_pos - (sot_pos + sot_len + 2);

            /* add all the lengths of the JPWL markers which are len-ready and stay within SOT and SOD */
            for (mm = 0; mm < jwmarker_num; mm++) {
                if ((jwmarker[mm].pos >= sot_pos) && (jwmarker[mm].pos < post_sod_pos)) {
                    if (jwmarker[mm].len_ready) {
                        left_THmarks_len += jwmarker[mm].len + 2;
                    } else {
                        opj_event_msg(j2k->cinfo, EVT_ERROR,
                                      "MS %x in %f is not len-ready: could not set up TH EPB\n",
                                      jwmarker[mm].id, jwmarker[mm].dpos);
                        exit(1);
                    }
                }
            }

            /****** if ((tilespec < JPWL_MAX_NO_TILESPECS) && (j2k->cp->hprot_TPH_tileno[tilespec] == tileno)) */
            if ((tilespec < JPWL_MAX_NO_TILESPECS) &&
                    (j2k->cp->hprot_TPH_tileno[tilespec] == acc_tpno))
                /* we got a specification from this tile part onwards */
            {
                hprot = j2k->cp->hprot_TPH[tilespec++];
            }

            /* must this TPH have an EPB MS? */
            if (j2k->cp->epb_on && (hprot > 0)) {

                /* Create the EPB */
                if ((epb_mark = jpwl_epb_create(
                                    j2k, /* this encoder handle */
                                    OPJ_FALSE, /* is it the latest? in TPH, no for now (if huge data size in TPH, we'd need more) */
                                    OPJ_TRUE, /* is it packed? yes for now */
                                    tileno, /* we are in TPH */
                                    epb_index++, /* its index is 0 (first) */
                                    hprot, /* protection type parameters of following data */
                                    sot_len + 2, /* pre-data length: only SOT */
                                    left_THmarks_len /* post-data length: from SOT end to SOD inclusive */
                                ))) {

                    /* Add this marker to the 'insertanda' list */
                    if (jwmarker_num < JPWL_MAX_NO_MARKERS) {
                        jwmarker[jwmarker_num].id = J2K_MS_EPB; /* its type */
                        jwmarker[jwmarker_num].m.epbmark = epb_mark; /* the EPB */
                        /****** jwmarker[jwmarker_num].pos = j2k->cstr_info->tile[tileno].start_pos + sot_len + 2; */ /* after SOT */
                        jwmarker[jwmarker_num].pos = j2k->cstr_info->tile[tileno].tp[tpno].tp_start_pos
                                                     + sot_len + 2; /* after SOT */
                        jwmarker[jwmarker_num].dpos = (double)
                                                      jwmarker[jwmarker_num].pos; /* first first first! */
                        jwmarker[jwmarker_num].len = epb_mark->Lepb; /* its length */
                        jwmarker[jwmarker_num].len_ready = OPJ_TRUE; /* ready */
                        jwmarker[jwmarker_num].pos_ready = OPJ_TRUE; /* ready */
                        jwmarker[jwmarker_num].parms_ready = OPJ_TRUE; /* ready */
                        jwmarker[jwmarker_num].data_ready = OPJ_FALSE; /* not ready */
                        jwmarker_num++;
                    }

                    /* update Psot of the tile  */
                    Psot += epb_mark->Lepb + 2;

                    opj_event_msg(j2k->cinfo, EVT_INFO,
                                  /***** "TPH EPB : tile %02d, prot. %d\n", */
                                  "TPH EPB : tile %02d, part %02d, prot. %d\n",
                                  /***** tileno, */
                                  tileno, tpno,
                                  hprot
                                 );

                    /* save this TPH EPB address */
                    tph_epb = epb_mark;

                } else {
                    /* ooops, problems */
                    /****** opj_event_msg(j2k->cinfo, EVT_ERROR, "Could not create TPH EPB #%d\n", tileno); */
                    opj_event_msg(j2k->cinfo, EVT_ERROR, "Could not create TPH EPB in #%d,d\n",
                                  tileno, tpno);
                };

            }

            startpack = 0;
            /* EPB MSs for UEP packet data protection in Tile Parts */
            /****** for (packno = 0; packno < j2k->cstr_info->num; packno++) { */
            /*first_tp_pack = (tpno > 0) ? (first_tp_pack + j2k->cstr_info->tile[tileno].tp[tpno - 1].tp_numpacks) : 0;*/
            first_tp_pack = j2k->cstr_info->tile[tileno].tp[tpno].tp_start_pack;
            last_tp_pack = first_tp_pack + j2k->cstr_info->tile[tileno].tp[tpno].tp_numpacks
                           - 1;
            for (packno = 0; packno < j2k->cstr_info->tile[tileno].tp[tpno].tp_numpacks;
                    packno++) {

                /******** if ((packspec < JPWL_MAX_NO_PACKSPECS) &&
                        (j2k->cp->pprot_tileno[packspec] == tileno) && (j2k->cp->pprot_packno[packspec] == packno)) { */
                if ((packspec < JPWL_MAX_NO_PACKSPECS) &&
                        (j2k->cp->pprot_tileno[packspec] == acc_tpno) &&
                        (j2k->cp->pprot_packno[packspec] == packno)) {

                    /* we got a specification from this tile and packet onwards */
                    /* print the previous spec */
                    if (packno > 0) {
                        stoppack = packno - 1;
                        opj_event_msg(j2k->cinfo, EVT_INFO,
                                      /***** "UEP EPBs: tile %02d, packs. %02d-%02d (B %d-%d), prot. %d\n", */
                                      "UEP EPBs: tile %02d, part %02d, packs. %02d-%02d (B %d-%d), prot. %d\n",
                                      /***** tileno, */
                                      tileno, tpno,
                                      startpack,
                                      stoppack,
                                      /***** j2k->cstr_info->tile[tileno].packet[startpack].start_pos, */
                                      j2k->cstr_info->tile[tileno].packet[first_tp_pack + startpack].start_pos,
                                      /***** j2k->cstr_info->tile[tileno].packet[stoppack].end_pos, */
                                      j2k->cstr_info->tile[tileno].packet[first_tp_pack + stoppack].end_pos,
                                      pprot);

                        /***** prot_len = j2k->cstr_info->tile[tileno].packet[stoppack].end_pos + 1 -
                                j2k->cstr_info->tile[tileno].packet[startpack].start_pos; */
                        prot_len = j2k->cstr_info->tile[tileno].packet[first_tp_pack + stoppack].end_pos
                                   + 1 -
                                   j2k->cstr_info->tile[tileno].packet[first_tp_pack + startpack].start_pos;

                        /*
                          particular case: if this is the last header and the last packet,
                          then it is better to protect even the EOC marker
                        */
                        /****** if ((tileno == ((j2k->cstr_info->tw * j2k->cstr_info->th) - 1)) &&
                                (stoppack == (j2k->cstr_info->num - 1))) */
                        if ((tileno == ((j2k->cstr_info->tw * j2k->cstr_info->th) - 1)) &&
                                (tpno == (j2k->cstr_info->tile[tileno].num_tps - 1)) &&
                                (stoppack == last_tp_pack))
                            /* add the EOC len */
                        {
                            prot_len += 2;
                        }

                        /* let's add the EPBs */
                        Psot += jpwl_epbs_add(
                                    j2k, /* J2K handle */
                                    jwmarker, /* pointer to JPWL markers list */
                                    &jwmarker_num, /* pointer to the number of current markers */
                                    OPJ_FALSE, /* latest */
                                    OPJ_TRUE, /* packed */
                                    OPJ_FALSE, /* inside MH */
                                    &epb_index, /* pointer to EPB index */
                                    pprot, /* protection type */
                                    /****** (double) (j2k->cstr_info->tile[tileno].start_pos + sot_len + 2) + 0.0001, */ /* position */
                                    (double)(j2k->cstr_info->tile[tileno].tp[tpno].tp_start_pos + sot_len + 2) +
                                    0.0001, /* position */
                                    tileno, /* number of tile */
                                    0, /* length of pre-data */
                                    prot_len /*4000*/ /* length of post-data */
                                );
                    }

                    startpack = packno;
                    pprot = j2k->cp->pprot[packspec++];
                }

                /*printf("Tile %02d, pack %02d ==> %d\n", tileno, packno, pprot);*/

            }

            /* we are at the end: print the remaining spec */
            stoppack = packno - 1;
            if (pprot >= 0) {

                opj_event_msg(j2k->cinfo, EVT_INFO,
                              /**** "UEP EPBs: tile %02d, packs. %02d-%02d (B %d-%d), prot. %d\n", */
                              "UEP EPBs: tile %02d, part %02d, packs. %02d-%02d (B %d-%d), prot. %d\n",
                              /**** tileno, */
                              tileno, tpno,
                              startpack,
                              stoppack,
                              /***** j2k->image_info->tile[tileno].packet[startpack].start_pos,
                              j2k->image_info->tile[tileno].packet[stoppack].end_pos, */
                              j2k->cstr_info->tile[tileno].packet[first_tp_pack + startpack].start_pos,
                              j2k->cstr_info->tile[tileno].packet[first_tp_pack + stoppack].end_pos,
                              pprot);

                /***** prot_len = j2k->cstr_info->tile[tileno].packet[stoppack].end_pos + 1 -
                        j2k->cstr_info->tile[tileno].packet[startpack].start_pos; */
                prot_len = j2k->cstr_info->tile[tileno].packet[first_tp_pack + stoppack].end_pos
                           + 1 -
                           j2k->cstr_info->tile[tileno].packet[first_tp_pack + startpack].start_pos;

                /*
                  particular case: if this is the last header and the last packet,
                  then it is better to protect even the EOC marker
                */
                /***** if ((tileno == ((j2k->cstr_info->tw * j2k->cstr_info->th) - 1)) &&
                        (stoppack == (j2k->cstr_info->num - 1))) */
                if ((tileno == ((j2k->cstr_info->tw * j2k->cstr_info->th) - 1)) &&
                        (tpno == (j2k->cstr_info->tile[tileno].num_tps - 1)) &&
                        (stoppack == last_tp_pack))
                    /* add the EOC len */
                {
                    prot_len += 2;
                }

                /* let's add the EPBs */
                Psot += jpwl_epbs_add(
                            j2k, /* J2K handle */
                            jwmarker, /* pointer to JPWL markers list */
                            &jwmarker_num, /* pointer to the number of current markers */
                            OPJ_TRUE, /* latest */
                            OPJ_TRUE, /* packed */
                            OPJ_FALSE, /* inside MH */
                            &epb_index, /* pointer to EPB index */
                            pprot, /* protection type */
                            /***** (double) (j2k->cstr_info->tile[tileno].start_pos + sot_len + 2) + 0.0001,*/ /* position */
                            (double)(j2k->cstr_info->tile[tileno].tp[tpno].tp_start_pos + sot_len + 2) +
                            0.0001, /* position */
                            tileno, /* number of tile */
                            0, /* length of pre-data */
                            prot_len /*4000*/ /* length of post-data */
                        );
            }

            /* we can now check if the TPH EPB was really the last one */
            if (tph_epb && (epb_index == 1)) {
                /* set the TPH EPB to be the last one in current header */
                tph_epb->Depb |= (unsigned char)((OPJ_TRUE & 0x0001) << 6);
                tph_epb = NULL;
            }

            /* write back Psot */
            cio_seek(cio, Psotp);
            cio_write(cio, Psot, 4);

        }

    };

    /* reset the position */
    cio_seek(cio, ciopos);

}

void jpwl_dump_marks(opj_j2k_t *j2k, opj_cio_t *cio, opj_image_t *image)
{

    int mm;
    unsigned long int old_size = j2k->cstr_info->codestream_size;
    unsigned long int new_size = old_size;
    int /*ciopos = cio_tell(cio),*/ soc_pos = j2k->cstr_info->main_head_start;
    unsigned char *jpwl_buf, *orig_buf;
    unsigned long int orig_pos;
    double epbcoding_time = 0.0, esdcoding_time = 0.0;
    (void)image;

    /* Order JPWL markers according to their wishlist position */
    qsort((void *) jwmarker, (size_t) jwmarker_num, sizeof(jpwl_marker_t),
          jpwl_markcomp);

    /* compute markers total size */
    for (mm = 0; mm < jwmarker_num; mm++) {
        /*printf("%x, %d, %.10f, %d long\n", jwmarker[mm].id, jwmarker[mm].pos,
                jwmarker[mm].dpos, jwmarker[mm].len);*/
        new_size += jwmarker[mm].len + 2;
    }

    /* allocate a new buffer of proper size */
    if (!(jpwl_buf = (unsigned char *) opj_malloc((size_t)(new_size + soc_pos) *
                     sizeof(unsigned char)))) {
        opj_event_msg(j2k->cinfo, EVT_ERROR,
                      "Could not allocate room for JPWL codestream buffer\n");
        exit(1);
    };

    /* copy the jp2 part, if any */
    orig_buf = jpwl_buf;
    memcpy(jpwl_buf, cio->buffer, soc_pos);
    jpwl_buf += soc_pos;

    /* cycle through markers */
    orig_pos = soc_pos + 0; /* start from the beginning */
    cio_seek(cio, soc_pos + 0); /* rewind the original */
    for (mm = 0; mm < jwmarker_num; mm++) {

        /*
        need to copy a piece of the original codestream
        if there is such
        */
        memcpy(jpwl_buf, cio_getbp(cio), jwmarker[mm].pos - orig_pos);
        jpwl_buf += jwmarker[mm].pos - orig_pos;
        orig_pos = jwmarker[mm].pos;
        cio_seek(cio, orig_pos);

        /*
        then write down the marker
        */
        switch (jwmarker[mm].id) {

        case J2K_MS_EPB:
            jpwl_epb_write(j2k, jwmarker[mm].m.epbmark, jpwl_buf);
            break;

        case J2K_MS_EPC:
            jpwl_epc_write(j2k, jwmarker[mm].m.epcmark, jpwl_buf);
            break;

        case J2K_MS_ESD:
            jpwl_esd_write(j2k, jwmarker[mm].m.esdmark, jpwl_buf);
            break;

        case J2K_MS_RED:
            memset(jpwl_buf, 0, jwmarker[mm].len + 2); /* placeholder */
            break;

        default:
            break;
        };

        /* we update the markers struct */
        if (j2k->cstr_info) {
            j2k->cstr_info->marker[j2k->cstr_info->marknum - 1].pos =
                (jpwl_buf - orig_buf);
        }

        /* we set the marker dpos to the new position in the JPWL codestream */
        jwmarker[mm].dpos = (double)(jpwl_buf - orig_buf);

        /* advance JPWL buffer position */
        jpwl_buf += jwmarker[mm].len + 2;

    }

    /* finish remaining original codestream */
    memcpy(jpwl_buf, cio_getbp(cio), old_size - (orig_pos - soc_pos));
    jpwl_buf += old_size - (orig_pos - soc_pos);
    cio_seek(cio, soc_pos + old_size);

    /*
    update info file based on added markers
    */
    if (!jpwl_update_info(j2k, jwmarker, jwmarker_num)) {
        opj_event_msg(j2k->cinfo, EVT_ERROR,
                      "Could not update OPJ cstr_info structure\n");
    }

    /* now we need to repass some markers and fill their data fields */

    /* first of all, DL and Pcrc in EPCs */
    for (mm = 0; mm < jwmarker_num; mm++) {

        /* find the EPCs */
        if (jwmarker[mm].id == J2K_MS_EPC) {

            int epc_pos = (int) jwmarker[mm].dpos, pp;
            unsigned short int mycrc = 0x0000;

            /* fix and fill the DL field */
            jwmarker[mm].m.epcmark->DL = new_size;
            orig_buf[epc_pos + 6] = (unsigned char)(jwmarker[mm].m.epcmark->DL >> 24);
            orig_buf[epc_pos + 7] = (unsigned char)(jwmarker[mm].m.epcmark->DL >> 16);
            orig_buf[epc_pos + 8] = (unsigned char)(jwmarker[mm].m.epcmark->DL >> 8);
            orig_buf[epc_pos + 9] = (unsigned char)(jwmarker[mm].m.epcmark->DL >> 0);

            /* compute the CRC field (excluding itself) */
            for (pp = 0; pp < 4; pp++) {
                jpwl_updateCRC16(&mycrc, orig_buf[epc_pos + pp]);
            }
            for (pp = 6; pp < (jwmarker[mm].len + 2); pp++) {
                jpwl_updateCRC16(&mycrc, orig_buf[epc_pos + pp]);
            }

            /* fix and fill the CRC */
            jwmarker[mm].m.epcmark->Pcrc = mycrc;
            orig_buf[epc_pos + 4] = (unsigned char)(jwmarker[mm].m.epcmark->Pcrc >> 8);
            orig_buf[epc_pos + 5] = (unsigned char)(jwmarker[mm].m.epcmark->Pcrc >> 0);

        }
    }

    /* then, sensitivity data in ESDs */
    esdcoding_time = opj_clock();
    for (mm = 0; mm < jwmarker_num; mm++) {

        /* find the ESDs */
        if (jwmarker[mm].id == J2K_MS_ESD) {

            /* remember that they are now in a new position (dpos) */
            int esd_pos = (int) jwmarker[mm].dpos;

            jpwl_esd_fill(j2k, jwmarker[mm].m.esdmark, &orig_buf[esd_pos]);

        }

    }
    esdcoding_time = opj_clock() - esdcoding_time;
    if (j2k->cp->esd_on) {
        opj_event_msg(j2k->cinfo, EVT_INFO, "ESDs sensitivities computed in %f s\n",
                      esdcoding_time);
    }

    /* finally, RS or CRC parity in EPBs */
    epbcoding_time = opj_clock();
    for (mm = 0; mm < jwmarker_num; mm++) {

        /* find the EPBs */
        if (jwmarker[mm].id == J2K_MS_EPB) {

            /* remember that they are now in a new position (dpos) */
            int nn, accum_len;

            /* let's see how many EPBs are following this one, included itself */
            /* for this to work, we suppose that the markers are correctly ordered */
            /* and, overall, that they are in packed mode inside headers */
            accum_len = 0;
            for (nn = mm; (nn < jwmarker_num) && (jwmarker[nn].id == J2K_MS_EPB) &&
                    (jwmarker[nn].pos == jwmarker[mm].pos); nn++) {
                accum_len += jwmarker[nn].m.epbmark->Lepb + 2;
            }

            /* fill the current (first) EPB with post-data starting from the computed position */
            jpwl_epb_fill(j2k, jwmarker[mm].m.epbmark, &orig_buf[(int) jwmarker[mm].dpos],
                          &orig_buf[(int) jwmarker[mm].dpos + accum_len]);

            /* fill the remaining EPBs in the header with post-data starting from the last position */
            for (nn = mm + 1; (nn < jwmarker_num) && (jwmarker[nn].id == J2K_MS_EPB) &&
                    (jwmarker[nn].pos == jwmarker[mm].pos); nn++) {
                jpwl_epb_fill(j2k, jwmarker[nn].m.epbmark, &orig_buf[(int) jwmarker[nn].dpos],
                              NULL);
            }

            /* skip all the processed EPBs */
            mm = nn - 1;
        }

    }
    epbcoding_time = opj_clock() - epbcoding_time;
    if (j2k->cp->epb_on) {
        opj_event_msg(j2k->cinfo, EVT_INFO, "EPBs redundancy computed in %f s\n",
                      epbcoding_time);
    }

    /* free original cio buffer and set it to the JPWL one */
    opj_free(cio->buffer);
    cio->cinfo = cio->cinfo; /* no change */
    cio->openmode = cio->openmode; /* no change */
    cio->buffer = orig_buf;
    cio->length = new_size + soc_pos;
    cio->start = cio->buffer;
    cio->end = cio->buffer + cio->length;
    cio->bp = cio->buffer;
    cio_seek(cio, soc_pos + new_size);

}


void j2k_read_epc(opj_j2k_t *j2k)
{
    unsigned long int DL, Lepcp, Pcrcp, l;
    unsigned short int Lepc, Pcrc = 0x0000;
    unsigned char Pepc;
    opj_cio_t *cio = j2k->cio;
    const char *ans1;

    /* Simply read the EPC parameters */
    Lepcp = cio_tell(cio);
    Lepc = cio_read(cio, 2);
    Pcrcp = cio_tell(cio);
    cio_skip(cio, 2); /* Pcrc */
    DL = cio_read(cio, 4);
    Pepc = cio_read(cio, 1);

    /* compute Pcrc */
    cio_seek(cio, Lepcp - 2);

    /* Marker */
    jpwl_updateCRC16(&Pcrc, (unsigned char) cio_read(cio, 1));
    jpwl_updateCRC16(&Pcrc, (unsigned char) cio_read(cio, 1));

    /* Length */
    jpwl_updateCRC16(&Pcrc, (unsigned char) cio_read(cio, 1));
    jpwl_updateCRC16(&Pcrc, (unsigned char) cio_read(cio, 1));

    /* skip Pcrc */
    cio_skip(cio, 2);

    /* read all remaining */
    for (l = 4; l < Lepc; l++) {
        jpwl_updateCRC16(&Pcrc, (unsigned char) cio_read(cio, 1));
    }

    /* check Pcrc with the result */
    cio_seek(cio, Pcrcp);
    ans1 = (Pcrc == (unsigned short int) cio_read(cio, 2)) ? "crc-ok" : "crc-ko";

    /* now we write them to screen */
    opj_event_msg(j2k->cinfo, EVT_INFO,
                  "EPC(%u,%d): %s, DL=%d%s %s %s\n",
                  Lepcp - 2,
                  Lepc,
                  ans1,
                  DL, /* data length this EPC is referring to */
                  (Pepc & 0x10) ? ", esd" : "", /* ESD is present */
                  (Pepc & 0x20) ? ", red" : "", /* RED is present */
                  (Pepc & 0x40) ? ", epb" : ""); /* EPB is present */

    cio_seek(cio, Lepcp + Lepc);
}

#if 0
void j2k_write_epc(opj_j2k_t *j2k)
{

    unsigned long int DL, Lepcp, Pcrcp, l;
    unsigned short int Lepc, Pcrc;
    unsigned char Pepc;

    opj_cio_t *cio = j2k->cio;

    cio_write(cio, J2K_MS_EPC, 2);  /* EPC */
    Lepcp = cio_tell(cio);
    cio_skip(cio, 2);

    /* CRC-16 word of the EPC */
    Pcrc = 0x0000; /* initialize */
    Pcrcp = cio_tell(cio);
    cio_write(cio, Pcrc, 2); /* Pcrc placeholder*/

    /* data length of the EPC protection domain */
    DL = 0x00000000; /* we leave this set to 0, as if the information is not available */
    cio_write(cio, DL, 4);   /* DL */

    /* jpwl capabilities */
    Pepc = 0x00;
    cio_write(cio, Pepc, 1); /* Pepc */

    /* ID section */
    /* no ID's, as of now */

    Lepc = (unsigned short)(cio_tell(cio) - Lepcp);
    cio_seek(cio, Lepcp);
    cio_write(cio, Lepc, 2); /* Lepc */

    /* compute Pcrc */
    cio_seek(cio, Lepcp - 2);

    /* Marker */
    jpwl_updateCRC16(&Pcrc, (unsigned char) cio_read(cio, 1));
    jpwl_updateCRC16(&Pcrc, (unsigned char) cio_read(cio, 1));

    /* Length */
    jpwl_updateCRC16(&Pcrc, (unsigned char) cio_read(cio, 1));
    jpwl_updateCRC16(&Pcrc, (unsigned char) cio_read(cio, 1));

    /* skip Pcrc */
    cio_skip(cio, 2);

    /* read all remaining */
    for (l = 4; l < Lepc; l++) {
        jpwl_updateCRC16(&Pcrc, (unsigned char) cio_read(cio, 1));
    }

    /* fill Pcrc with the result */
    cio_seek(cio, Pcrcp);
    cio_write(cio, Pcrc, 2);

    cio_seek(cio, Lepcp + Lepc);

    /* marker struct update */
    j2k_add_marker(j2k->cstr_info, J2K_MS_EPC, Lepcp - 2, Lepc + 2);

}
#endif

void j2k_read_epb(opj_j2k_t *j2k)
{
    unsigned long int LDPepb, Pepb;
    unsigned short int Lepb;
    unsigned char Depb;
    char str1[25] = "";
    opj_bool status;
    static opj_bool first_in_tph = OPJ_TRUE;
    int type, pre_len, post_len;
    static unsigned char *redund = NULL;

    opj_cio_t *cio = j2k->cio;

    /* B/W = 45, RGB = 51 */
    /*           SIZ   SIZ_FIELDS     SIZ_COMPS               FOLLOWING_MARKER */
    int skipnum = 2  +     38     + 3 * j2k->cp->exp_comps  +         2;

    if (j2k->cp->correct) {

        /* go back to EPB marker value */
        cio_seek(cio, cio_tell(cio) - 2);

        /* we need to understand where we are */
        if (j2k->state == J2K_STATE_MH) {
            /* we are in MH */
            type = 0; /* MH */
            pre_len = skipnum; /* SOC+SIZ */
            post_len = -1; /* auto */

        } else if ((j2k->state == J2K_STATE_TPH) && first_in_tph) {
            /* we are in TPH */
            type = 1; /* TPH */
            pre_len = 12; /* SOC+SIZ */
            first_in_tph = OPJ_FALSE;
            post_len = -1; /* auto */

        } else {
            /* we are elsewhere */
            type = 2; /* other */
            pre_len = 0; /* nada */
            post_len = -1; /* auto */

        }

        /* call EPB corrector */
        /*printf("before %x, ", redund);*/
        status = jpwl_epb_correct(j2k,      /* J2K decompressor handle */
                                  cio->bp,  /* pointer to EPB in codestream buffer */
                                  type,     /* EPB type: MH */
                                  pre_len,  /* length of pre-data */
                                  post_len, /* length of post-data: -1 means auto */
                                  NULL,     /* do everything auto */
                                  &redund
                                 );
        /*printf("after %x\n", redund);*/

        /* Read the (possibly corrected) EPB parameters */
        cio_skip(cio, 2);
        Lepb = cio_read(cio, 2);
        Depb = cio_read(cio, 1);
        LDPepb = cio_read(cio, 4);
        Pepb = cio_read(cio, 4);

        if (!status) {

            opj_event_msg(j2k->cinfo, EVT_ERROR,
                          "JPWL correction could not be performed\n");

            /* advance to EPB endpoint */
            cio_skip(cio, Lepb + 2);

            return;
        }

        /* last in current header? */
        if (Depb & 0x40) {
            redund = NULL; /* reset the pointer to L4 buffer */
            first_in_tph = OPJ_TRUE;
        }

        /* advance to EPB endpoint */
        cio_skip(cio, Lepb - 11);

    } else {

        /* Simply read the EPB parameters */
        Lepb = cio_read(cio, 2);
        Depb = cio_read(cio, 1);
        LDPepb = cio_read(cio, 4);
        Pepb = cio_read(cio, 4);

        /* What does Pepb tells us about the protection method? */
        if (((Pepb & 0xF0000000) >> 28) == 0) {
            sprintf(str1, "pred");    /* predefined */
        } else if (((Pepb & 0xF0000000) >> 28) == 1) {
            sprintf(str1, "crc-%lu", 16 * ((Pepb & 0x00000001) + 1));    /* CRC mode */
        } else if (((Pepb & 0xF0000000) >> 28) == 2) {
            sprintf(str1, "rs(%lu,32)", (Pepb & 0x0000FF00) >> 8);    /* RS mode */
        } else if (Pepb == 0xFFFFFFFF) {
            sprintf(str1, "nometh");    /* RS mode */
        } else {
            sprintf(str1, "unknown");    /* unknown */
        }

        /* Now we write them to screen */
        opj_event_msg(j2k->cinfo, EVT_INFO,
                      "EPB(%d): (%sl, %sp, %u), %lu, %s\n",
                      cio_tell(cio) - 13,
                      (Depb & 0x40) ? "" : "n", /* latest EPB or not? */
                      (Depb & 0x80) ? "" : "n", /* packed or unpacked EPB? */
                      (Depb & 0x3F), /* EPB index value */
                      LDPepb, /*length of the data protected by the EPB */
                      str1); /* protection method */

        cio_skip(cio, Lepb - 11);
    }
}

void j2k_write_epb(opj_j2k_t *j2k)
{
    unsigned long int LDPepb, Pepb, Lepbp;
    unsigned short int Lepb;
    unsigned char Depb;

    opj_cio_t *cio = j2k->cio;

    cio_write(cio, J2K_MS_EPB, 2);  /* EPB */
    Lepbp = cio_tell(cio);
    cio_skip(cio, 2);

    /* EPB style */
    Depb = 0x00; /* test */
    cio_write(cio, Depb, 1);   /* Depb */

    /* length of the data to be protected by this EPB */
    LDPepb = 0x00000000; /* test */
    cio_write(cio, LDPepb, 4);   /* LDPepb */

    /* next error correction tool */
    Pepb = 0x00000000; /* test */
    cio_write(cio, Pepb, 4);   /* Pepb */

    /* EPB data */
    /* no data, as of now */

    Lepb = (unsigned short)(cio_tell(cio) - Lepbp);
    cio_seek(cio, Lepbp);
    cio_write(cio, Lepb, 2);                /* Lepb */

    cio_seek(cio, Lepbp + Lepb);

    /* marker struct update */
    j2k_add_marker(j2k->cstr_info, J2K_MS_EPB, Lepbp - 2, Lepb + 2);
}

void j2k_read_esd(opj_j2k_t *j2k)
{
    unsigned short int Lesd, Cesd;
    unsigned char Pesd;

    int cesdsize = (j2k->image->numcomps >= 257) ? 2 : 1;

    char str1[4][4] = {"p", "br", "pr", "res"};
    char str2[8][8] = {"res", "mse", "mse-r", "psnr", "psnr-i", "maxerr", "tse", "res"};

    opj_cio_t *cio = j2k->cio;

    /* Simply read the ESD parameters */
    Lesd = cio_read(cio, 2);
    Cesd = cio_read(cio, cesdsize);
    Pesd = cio_read(cio, 1);

    /* Now we write them to screen */
    opj_event_msg(j2k->cinfo, EVT_INFO,
                  "ESD(%d): c%d, %s, %s, %s, %s, %s\n",
                  cio_tell(cio) - (5 + cesdsize),
                  Cesd, /* component number for this ESD */
                  str1[(Pesd & (unsigned char) 0xC0) >> 6], /* addressing mode */
                  str2[(Pesd & (unsigned char) 0x38) >> 3], /* sensitivity type */
                  ((Pesd & (unsigned char) 0x04) >> 2) ? "2Bs" : "1Bs",
                  ((Pesd & (unsigned char) 0x02) >> 1) ? "4Ba" : "2Ba",
                  (Pesd & (unsigned char) 0x01) ? "avgc" : "");

    cio_skip(cio, Lesd - (3 + cesdsize));
}

void j2k_read_red(opj_j2k_t *j2k)
{
    unsigned short int Lred;
    unsigned char Pred;
    char str1[4][4] = {"p", "br", "pr", "res"};

    opj_cio_t *cio = j2k->cio;

    /* Simply read the RED parameters */
    Lred = cio_read(cio, 2);
    Pred = cio_read(cio, 1);

    /* Now we write them to screen */
    opj_event_msg(j2k->cinfo, EVT_INFO,
                  "RED(%d): %s, %dc, %s, %s\n",
                  cio_tell(cio) - 5,
                  str1[(Pred & (unsigned char) 0xC0) >> 6], /* addressing mode */
                  (Pred & (unsigned char) 0x38) >> 3, /* corruption level */
                  ((Pred & (unsigned char) 0x02) >> 1) ? "4Ba" : "2Ba", /* address range */
                  (Pred & (unsigned char) 0x01) ? "errs" : "free"); /* error free? */

    cio_skip(cio, Lred - 3);
}

opj_bool jpwl_check_tile(opj_j2k_t *j2k, opj_tcd_t *tcd, int tileno)
{

#ifdef oerhgierhgvhreit4u
    /*
       we navigate through the tile and find possible invalid parameters:
    this saves a lot of crashes!!!!!
     */
    int compno, resno, precno, /*layno,*/ bandno, blockno;
    int numprecincts, numblocks;

    /* this is the selected tile */
    opj_tcd_tile_t *tile = &(tcd->tcd_image->tiles[tileno]);

    /* will keep the component */
    opj_tcd_tilecomp_t *comp = NULL;

    /* will keep the resolution */
    opj_tcd_resolution_t *res;

    /* will keep the subband */
    opj_tcd_band_t *band;

    /* will keep the precinct */
    opj_tcd_precinct_t *prec;

    /* will keep the codeblock */
    opj_tcd_cblk_t *block;

    /* check all tile components */
    for (compno = 0; compno < tile->numcomps; compno++) {
        comp = &(tile->comps[compno]);

        /* check all component resolutions */
        for (resno = 0; resno < comp->numresolutions; resno++) {
            res = &(comp->resolutions[resno]);
            numprecincts = res->pw * res->ph;

            /* check all the subbands */
            for (bandno = 0; bandno < res->numbands; bandno++) {
                band = &(res->bands[bandno]);

                /* check all the precincts */
                for (precno = 0; precno < numprecincts; precno++) {
                    prec = &(band->precincts[precno]);
                    numblocks = prec->ch * prec->cw;

                    /* check all the codeblocks */
                    for (blockno = 0; blockno < numblocks; blockno++) {
                        block = &(prec->cblks[blockno]);

                        /* x-origin is invalid */
                        if ((block->x0 < prec->x0) || (block->x0 > prec->x1)) {
                            opj_event_msg(j2k->cinfo, JPWL_ASSUME ? EVT_WARNING : EVT_ERROR,
                                          "JPWL: wrong x-cord of block origin %d => x-prec is (%d, %d)\n",
                                          block->x0, prec->x0, prec->x1);
                            if (!JPWL_ASSUME) {
                                return OPJ_FALSE;
                            }
                        };
                    }
                }
            }
        }
    }

#else
    (void)j2k;
    (void)tcd;
    (void)tileno;
#endif

    return OPJ_TRUE;
}

/*@}*/

#endif /* USE_JPWL */


#ifdef USE_JPSEC

/** @defgroup JPSEC JPSEC - JPEG-2000 Part 8 (JPSEC) codestream manager */
/*@{*/


/** @name Local static functions */
/*@{*/

void j2k_read_sec(opj_j2k_t *j2k)
{
    unsigned short int Lsec;

    opj_cio_t *cio = j2k->cio;

    /* Simply read the SEC length */
    Lsec = cio_read(cio, 2);

    /* Now we write them to screen */
    opj_event_msg(j2k->cinfo, EVT_INFO,
                  "SEC(%d)\n",
                  cio_tell(cio) - 2
                 );

    cio_skip(cio, Lsec - 2);
}

void j2k_write_sec(opj_j2k_t *j2k)
{
    unsigned short int Lsec = 24;
    int i;

    opj_cio_t *cio = j2k->cio;

    cio_write(cio, J2K_MS_SEC, 2);  /* SEC */
    cio_write(cio, Lsec, 2);

    /* write dummy data */
    for (i = 0; i < Lsec - 2; i++) {
        cio_write(cio, 0, 1);
    }
}

void j2k_read_insec(opj_j2k_t *j2k)
{
    unsigned short int Linsec;

    opj_cio_t *cio = j2k->cio;

    /* Simply read the INSEC length */
    Linsec = cio_read(cio, 2);

    /* Now we write them to screen */
    opj_event_msg(j2k->cinfo, EVT_INFO,
                  "INSEC(%d)\n",
                  cio_tell(cio) - 2
                 );

    cio_skip(cio, Linsec - 2);
}


/*@}*/

/*@}*/

#endif /* USE_JPSEC */

