/************************************************************************
 * demultiplexing of blocks                                             *
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include "jpeg.h"
#include "processframe.h"

#define my_read(ptr, len) \
    { \
      memcpy(ptr, p->local->fptr, len); \
      p->local->fptr += len; \
      p->local->fLen -= len; \
    };

int load_huff_tables(DOLProcess *p);
int unpack_block2(unsigned char block[8][8], cd_t *comp, DOLProcess *p);
int get_bits2(int number, unsigned long *res, DOLProcess *p);
int get_symbol2(int select, unsigned char *res, DOLProcess *p);
int get_one_bit2(unsigned char *res, DOLProcess *p);
unsigned int get_next_MK2(DOLProcess *p);
unsigned int get_size2(DOLProcess *p);
void skip_segment2(DOLProcess *p);

typedef struct _vld_state {
    int mx_size;
    int my_size; //picture size in units of MCUs
    int x_size;
    int y_size; //picture size in pixels
    unsigned char DC_Table0[MAX_SIZE(DC_CLASS)];
    unsigned char DC_Table1[MAX_SIZE(DC_CLASS)];
    unsigned char AC_Table0[MAX_SIZE(AC_CLASS)];
    unsigned char AC_Table1[MAX_SIZE(AC_CLASS)];
    unsigned char *HTable[4];
    int MinCode[4][16];
    int MaxCode[4][16];
    int ValPtr[4][16];
    unsigned char window;
    unsigned char bit_count; //available bits in the window
    unsigned char QTable[64];
} VLD_State;

void processframe_init(DOLProcess *p) {
    p->local->vld.HTable[0] = p->local->vld.DC_Table0;
    p->local->vld.HTable[1] = p->local->vld.DC_Table1;
    p->local->vld.HTable[2] = p->local->vld.AC_Table0;
    p->local->vld.HTable[3] = p->local->vld.AC_Table1;
    p->local->num_iter = 0;
}

int processframe_fire(DOLProcess *p) {
    char * ptr;
    int done;
    unsigned int aux, mark;
    unsigned char buf, waste;
    int in_frame; //frame started? current component?
    int found_MK; //if marker found while read data found_MK = 1
    int x_size, y_size;  //picture size in pixel units
    int nblock; //picture size in number of MCUs

    // read frame size and frame
    DOL_read((void*)PORT_IN1, &(p->local->fLen), sizeof(p->local->fLen), p);
    DOL_read((void*)PORT_IN2, p->local->frame, sizeof(p->local->frame), p);
    p->local->fptr = p->local->frame;

    //now process segments as they appear first find the SOI marker
    do {
        aux = get_next_MK2(p);
    } while (aux != SOI_MK);

    dbgprintf(VERBOSE, "PROCESSFRAME\tFound the SOI marker !\n");
    dbgprintf(VERBOSE, "PROCESSFRAME\tStart picture\n");

    found_MK = 0; //marker already found
    done = 1;
    while (done) {
        if (found_MK == 0)  {
            mark = get_next_MK2(p);
        }
        switch (mark) {
        case SOF_MK:

            dbgprintf(VERBOSE, "PROCESSFRAME\tFound the SOF marker\n");

            in_frame = 1;
            found_MK = 0;
            //header size, don't care
            get_size2(p);

            //precision, 8bit, don't care
            my_read(&waste, 1*(sizeof(waste)));

            //load basic image parameters
            y_size = get_size2(p);
            x_size = get_size2(p);

            //send y_size & x_size to mergestream
            DOL_write((void*)PORT_OUT2, &x_size, 1*(sizeof(x_size)), p);
            DOL_write((void*)PORT_OUT2, &y_size, 1*(sizeof(y_size)), p);

            p->local->vld.x_size = x_size;
            p->local->vld.y_size = y_size;
            p->local->vld.mx_size = intceil(x_size, MCU_sx);
            p->local->vld.my_size = intceil(y_size, MCU_sy);

            dbgprintf(VERBOSE, "\tVLD\tpicture size: y_size=%d, x_size=%d\n",
                    y_size,x_size);
            dbgprintf(VERBOSE, "\tVLD\tpicture size: my_size=%d, mx_size=%d\n",
                   p->local->vld.my_size, p->local->vld.mx_size);

            //total number of MCU in picture
            nblock = (y_size/MCU_sy)*(x_size/MCU_sx);

            dbgprintf(VERBOSE, "\tPROCESSFRAME Number of blocks in picture is %d \n", nblock);
            dbgprintf(VERBOSE, "\tPROCESSFRAME Picture size is %d by %d\n", x_size, y_size);
            dbgprintf(VERBOSE, "\tPROCESSFRAME Monochrome JPEG picture!\n");

            //number of components,don't care
            my_read(&waste, 1*(sizeof(waste)));

            //component order
            my_read(&buf, 1*(sizeof(buf)));

            //sampling factor, don't care
            my_read(&buf, 1*(sizeof(buf)));

            //quantization table index,don't care for jfif
            my_read(&buf, 1*(sizeof(buf)));
            break;
        case DHT_MK:
            dbgprintf(VERBOSE, "PROCESSFRAME\tDefining Huffman Tables\n");
            //VLD: loading Huffman table
            load_huff_tables(p);

            //remove the rest
            while ((ptr = (char *)memchr(p->local->fptr,0xFF,p->local->fLen))
                   != NULL) {
                int offset;
                unsigned char pot_mark = *(ptr+1);
                if ((pot_mark != 0) && (pot_mark != 0xD8)) {
                    offset = ptr - p->local->fptr + 2;
                    p->local->fLen -= offset;
                    p->local->fptr += offset;
                    mark = (0xFF00|(unsigned int)pot_mark);
                    dbgprintf(VERBOSE, "\tPROCESSFRAME\tfound marker while in vld=%x! 2\n",mark);
                    found_MK = 1;
                    break;
                }

                offset = ptr - p->local->fptr + 2;
                p->local->fLen -= offset;
                p->local->fptr += offset;
            }

            break;

        case DQT_MK:
            dbgprintf(VERBOSE, "PROCESSFRAME\tDefining Quantization Tables\n");
            {
                unsigned char aux;
                unsigned int size, n, i;

                size = get_size2(p); //this is the table's size
                my_read(&aux, 1*(sizeof(aux)));
                memcpy(p->local->vld.QTable, p->local->fptr, sizeof(p->local->vld.QTable));
                p->local->fptr += sizeof(p->local->vld.QTable);
            }
            break;

        case DRI_MK:
            //skip size
            get_size2(p);
            get_size2(p);
            break;

        case SOS_MK:
            cd_t comp;    // descriptors for 3 components
            int leftover; // RST check
            dbgprintf(VERBOSE, "PROCESSFRAME\tFound the SOS marker\n");
            get_size2(p); // don't care
            get_size2(p); // don't care

            my_read(&buf, sizeof(buf));
            comp.DC_HT = first_quad(buf);
            comp.AC_HT = second_quad(buf);

            get_size2(p); // don't care
            my_read(&buf, sizeof(buf));

            p->local->vld.bit_count = 0; // initialise vld decoder
            comp.PRED = 0; //initialise vld predictor
            leftover = p->local->vld.mx_size * p->local->vld.my_size;


            {
                int goodrows, goodcolumns;
                unsigned char *ColorBuffer = p->local->ColorBuffer;
                unsigned char *LineBuffer  = p->local->LineBuffer;
                int *dataBuff = p->local->dataBuff;

                goodrows = MCU_sy;
                goodcolumns = MCU_sx;

                //piece-wise processing
                for (int j = 0; j < p->local->vld.my_size; j++) {
                    for (int k = 0; k < p->local->vld.mx_size; k++) {
                        unsigned char block[8][8];
                        unpack_block2(block, &comp, p);
                        for (int l = 0; l < goodrows; l++) {
                            memcpy(LineBuffer + k * MCU_sx + l * p->local->vld.x_size,
                                   &block[l][0], goodcolumns);
                        }
                    }
                    for (int l = 0; l < (p->local->vld.x_size * MCU_sy) / 4; l++) {
                        dataBuff[l] =(*(LineBuffer + 4 * l))
                            + (*(LineBuffer + 4 * l + 1) << 8)
                            + (*(LineBuffer + 4 * l + 2) << 16)
                            + (*(LineBuffer + 4 * l + 3) << 24);
                    }
                    DOL_write((void*)PORT_OUT1, dataBuff, p->local->vld.x_size * MCU_sy, p);
                }
            }


            //if picture end normally, EOI marker is send to VLD
            //get_size2((void *)PORT_IN1, p); // don't care

            //remove the rest
            while ((ptr = (char *)memchr(p->local->fptr,0xFF,p->local->fLen))
                   != NULL) {
                int offset;
                unsigned char pot_mark = *(ptr+1);
                if ((pot_mark != 0) && (pot_mark != 0xD8)) {
                    offset = ptr - p->local->fptr + 2;
                    p->local->fLen -= offset;
                    p->local->fptr += offset;
                    mark = (0xFF00|(unsigned int)pot_mark);
                    dbgprintf(VERBOSE, "\tPROCESSFRAME\t found marker in data stream to vld:%x\n",
                            mark);
                    found_MK = 1;
                    break;
                }
                offset = ptr - p->local->fptr + 2;
                p->local->fLen -= offset;
                p->local->fptr += offset;
            }

            in_frame = 0;
            break;


        case EOI_MK:
            dbgprintf(VERBOSE, "PROCESSFRAME\tpicture end\n");
            done = 0;
            break;
        case COM_MK:
            dbgprintf(VERBOSE, "PROCESSFRAME\tSkipping comments\n");
            skip_segment2(p);
            break;

        case EOF:
            dbgprintf(VERBOSE | CASS, "ERROR PROCESSFRAME Ran out of input data !\n");
            exit(0);
        default:
            if ((mark & MK_MSK) == APP_MK) {
                dbgprintf(VERBOSE, "PROCESSFRAME\tSkipping application data\n");
                skip_segment2(p);
                break;
            }
            if (RST_MK(mark)) {
                dbgprintf(VERBOSE, "PROCESSFRAME\tfound RST Marker\n");
                break;
            }
            done = 0;
            break;
        }
    }

    //end this process
    p->local->num_iter++;
    if (p->local->num_iter == NUMBER_OF_FRAMES) {
        DOL_detach(p);
        return 0;
    }

    return  0;
}

int unpack_block2(unsigned char block[8][8], cd_t *comp, DOLProcess *p) {
    unsigned long temp;
    unsigned int i, run, cat;
    int value;
    unsigned char symbol;
    int T[64];
    memset((void *)T, 0, sizeof(T)); //zeroize block
    int block2[8][8];

    //first get the DC coefficient
    get_symbol2(HUFF_ID(DC_CLASS,comp->DC_HT), &symbol, p);
    get_bits2(symbol, &temp, p);

    value = reformat(temp, symbol);
    value += comp->PRED;
    comp->PRED = value;

    //reoganize and unquantify -> move to ZZ and IQ
    T[0] = value ;

    //then the AC ones
    //if symbol found is EOB and process not finish, missing values are
    //replaced by zero
    for (i = 1; i < 64; i++) {
        get_symbol2(HUFF_ID(AC_CLASS, comp->AC_HT), &symbol, p);

        if (symbol == 0x00) break;
        if (symbol == 0xF0) {
            i += 15;
            continue;
        }
        cat = symbol & 15;
        run = (symbol >> 4) & 15;
        i += run;
        get_bits2(cat, &temp, p);
        value = reformat(temp, cat);
        T[i] = value ;

        //63 is to exit without EOB if last coef non-zero
        if (i == 63) break;
    }
    unquantify(T, p->local->vld.QTable);
    unZigZag((int*)block2, T);
    IDCT(block2, block);

    return 0;
}

//utility and counter to return the number of bits from file
//right aligned, masked, first bit towards MSB's
int get_bits2(int number, unsigned long *res, DOLProcess *p) {
    int i, newbit;
    unsigned long result = 0;
    unsigned char aux, wwindow;

    *(res) = 0;
    if (!number) return 0;
    for (i = 0; i < number; i++) {
        if (p->local->vld.bit_count == 0) {
            my_read(&wwindow, sizeof(wwindow));
            if (wwindow == 0xFF) {
                my_read(&aux, sizeof(aux));
                p->local->vld.bit_count = 0;
            }
            p->local->vld.bit_count = 8;
        } else
            wwindow = p->local->vld.window;
        newbit = (wwindow >> 7) & 1;
        p->local->vld.window = wwindow << 1;
        p->local->vld.bit_count--;
        result = (result << 1) | newbit;
    }
    *(res)= result;
    return 0;
}

/*-----------------------------------*/
/* extract a single symbol from file */
/* using specified huffman table ... */
/*-----------------------------------*/
int get_symbol2(int select, unsigned char *res, DOLProcess *p) {
    unsigned char temp;
    long code = 0;
    int length;
    int index;

    for (length = 0; length < 16; length++) {
        get_one_bit2(&temp, p);

        code = (2 * code) | temp;
        if (code <= p->local->vld.MaxCode[select][length])
            break;
    }
    index = p->local->vld.ValPtr[select][length] + code -
        p->local->vld.MinCode[select][length];
    if (index < MAX_SIZE(select / 2)) {
        *(res)=p->local->vld.HTable[select][index];
        return 0;
    }
#ifndef CASS
    printf("\tWARNING:\tOverflowing symbol table !\n");
#endif
    return 1;
}

int get_one_bit2(unsigned char *res, DOLProcess *p) {
    int newbit;
    unsigned char aux, wwindow;

    *(res) = 0;
    if (p->local->vld.bit_count == 0) {
        my_read(&wwindow, sizeof(wwindow));
        if (wwindow == 0xFF) {
            my_read(&aux, sizeof(aux));
            p->local->vld.bit_count = 0;
        }
        p->local->vld.bit_count = 8;
    } else
        wwindow = p->local->vld.window;

    newbit = (wwindow >> 7) & 1;
    p->local->vld.window = wwindow << 1;
    p->local->vld.bit_count--;
    *(res) = newbit;
    return 0;
}



//------------------------------------------------------------------------
/* utility and counter to return the number of bits from file */
/* right aligned, masked, first bit towards MSB's               */

inline unsigned int get_size2(DOLProcess *p) {
    unsigned char aux[2];
    my_read(&aux[0], sizeof(aux[0]));
    my_read(&aux[1], sizeof(aux[1]));
    return ((aux[0] << 8) | aux[1]);  /* big endian */
}

//skip a segment we don't want
void skip_segment2(DOLProcess *p) {
    unsigned int size;
    unsigned char tag[5], waste;
    unsigned int i;

    size = get_size2(p);
    if (size > 5) {
        for (i = 0; i < 4; i++)
            my_read(&tag[i], sizeof(tag[i]));
        tag[4] = 0;
        size -= 4;
    }
    for(i=0; i<(size - 2); i++)
        my_read(&waste, sizeof(waste));
}

/*----------------------------------------------------------------*/
/* find next marker of any type, returns it, positions just after */
/* EOF instead of marker if end of file met while searching ...   */
/*----------------------------------------------------------------*/
unsigned int get_next_MK2(DOLProcess *p) {
    unsigned char bufp;
    unsigned int c;
    int ffmet = 0;
    int locpassed = -1;

    do {
        my_read(&bufp, sizeof(bufp));
        c = (unsigned int)bufp;
        switch (c) {
        case 0xFF:
            ffmet = 1;
            break;
        case 0x00:
            ffmet = 0;
            break;
        default:
            if (ffmet){
                dbgprintf(VERBOSE, "\tPROCESSFRAME\tfound marker %x\n",c);
                return (0xFF00 | c);
            }
            ffmet = 0;
            break;
        }
        locpassed++;
    } while (c!= EOF);
    return (unsigned int)EOF;
}

/*----------------------------------------------------------*/
/* Loading of Huffman table, with leaves drop ability       */
/*----------------------------------------------------------*/
int load_huff_tables(DOLProcess *p) {
    unsigned char aux, buf, waste;
    int size, Mclass, id, max;
    int LeavesN, LeavesT, i;
    int AuxCode;

    size = get_size2(p);/* this is the tables' size */

    size -= 2;
    while ((size > 0))  {
        my_read(&aux, 1*(sizeof(aux)));

        Mclass = first_quad(aux);  /* AC or DC */
        id = second_quad(aux);    /* table no */

        if (id > 1) {
            dbgprintf(INFO, "\tERROR:\tBad HTable identity %d!\n", id);
        }

        id = HUFF_ID(Mclass, id);

        dbgprintf(VERBOSE, "\tVLD\tLoading Table %d\n", id);

        size--;
        LeavesT = 0;
        AuxCode = 0;
        for (i = 0; i < 16; i++) {
            my_read(&buf, 1*(sizeof(buf)));

            LeavesN = buf;
            p->local->vld.ValPtr[id][i] = LeavesT;
            p->local->vld.MinCode[id][i] = AuxCode * 2;
            AuxCode = p->local->vld.MinCode[id][i] + LeavesN;
            p->local->vld.MaxCode[id][i] = (LeavesN) ? (AuxCode - 1) : (-1);
            LeavesT += LeavesN;
        }

        size -= 16;
        if (LeavesT > MAX_SIZE(Mclass)) {
            max = MAX_SIZE(Mclass);
            printf("\tWARNING:\tTruncating Table by %d symbols\n",
                   LeavesT - max);
        } else
            max = LeavesT;

        for (i = 0; i < max; i++) { /* get huffman table */
            my_read(&buf, 1*(sizeof(buf)));
            p->local->vld.HTable[id][i] = buf;  /* load in raw order */
        }

        for (i = max; i < LeavesT; i++) {
            my_read(&waste, sizeof(waste));
        }
        size -= LeavesT;
        dbgprintf(VERBOSE, "\tVLD:\tUsing %d words of table memory\n", LeavesT);
    }
    return 0;
}
