#include "qe3.h"

#include "deco.h"

//the list of deco bitmaps for each episode.
deco_bitmap_t *deco_bitmaps[4] = {NULL, NULL, NULL, NULL};
int num_deco_pics[4];

//this function reads in all the decoinfo.txt files and creates the 
//deco bitmap lists.
void MakeDecoInfoLists() {
    char decoinfo_path[1024];

    //make the path to the e1 decoinfo.txt file.
    ExpandReletivePath("models/deco/e1/decoinfo.txt", decoinfo_path);
    //read in the list.
    MakeDecoInfoList(decoinfo_path, &deco_bitmaps[0], &num_deco_pics[0]);

    //make the path to the e2 decoinfo.txt file.
    ExpandReletivePath("models/deco/e2/decoinfo.txt", decoinfo_path);
    //read in the list.
    MakeDecoInfoList(decoinfo_path, &deco_bitmaps[1], &num_deco_pics[1]);

    //make the path to the e3 decoinfo.txt file.
    ExpandReletivePath("models/deco/e3/decoinfo.txt", decoinfo_path);
    //read in the list.
    MakeDecoInfoList(decoinfo_path, &deco_bitmaps[2], &num_deco_pics[2]);

    //make the path to the e4 decoinfo.txt file.
    ExpandReletivePath("models/deco/e4/decoinfo.txt", decoinfo_path);
    //read in the list.
    MakeDecoInfoList(decoinfo_path, &deco_bitmaps[3], &num_deco_pics[3]);
}

//strips leading and trailing spaces from a string.
void StripSpaces(char *str) {
    //the src and dest positions we read/write to.
    int dest = 0;
    int src = 0;
    
    //the length of the string without the spaces.
    int len = 0;

    if (str == NULL) return;

    //skip past any leading spaces.
    while (str[src] == ' ') {
        src++;
    }

    //copy the string 
    while (str[src] != '\0') {
        //copy the character.
        str[dest] = str[src];

        //increment src and dest.
        dest++;
        src++;

        //increment the length
        len++;
    }

    //null terminate the string.
    str[len] = '\0';

    //remove any spaces at the end of the string.
    while (str[len - 1] == ' ') {
        //get rid of the space.
        str[len - 1] = '\0';
        //decrement the length.
        len--;
    }
}

#define MAX_DECO_LINE_LEN 256
#define NUM_DECO_FIELDS 13

//this function takes the path of a decoinfo.txt file and a reference
//to the list to fill in.  This should be called only once per list.
void MakeDecoInfoList(char *file_path, deco_bitmap_t **list, int *num_pics) {
    FILE *file;
    //the text fields that we will read out of each line of the file
    char fields[NUM_DECO_FIELDS][MAX_DECO_LINE_LEN];
    //the current position we are writing in the line
    int line_pos = 0;
    //the current field we are reading
    int cur_field = 0;
    //flag to tell if we are reading through a comment.
    int in_comment = 0;
    //the file length.
    int file_len;

    //make sure we hava a non-blank path name.
    if (file_path == NULL || file_path[0] == '\0') return;
    //make sure we have a pointer to the last NULL in the list.
    if (list == NULL || *list != NULL);

    //open the file.
    file = fopen(file_path, "rb");
    if (file == NULL) return;

    //get the length of the file.
    fseek(file, 0, SEEK_END);
    file_len = ftell(file);
    fseek(file, 0, SEEK_SET);

    //the current position we are writing in the line
    line_pos = 0;

    //the current field we are reading
    cur_field = 0;

    //reset the first field.
    fields[0][0] = '\0';

    //flag to tell if we are reading through a comment.
    in_comment = 0;

    while (file_len > 0) {
        //read in a character.
        char lastchar = fgetc(file);
        //decrement the file_len var.
        file_len--;

        //check if this line is a comment.
        if (line_pos == 0) {
            if (lastchar == ';') {
                in_comment = 1;
            }
            else {
                in_comment = 0;
            }
        }

        //check if we are reading through a comment.
        if (in_comment == 1) {
            line_pos++;
            //check what character we read in last, if it was a \n, we are on the next line.
            if (lastchar == '\n') {
                //we are leaving the comment.
                in_comment = 0;

                //reset our line position.
                line_pos = 0;
            }
            //read the next character, dont do any processing on this character.
            continue;
        }

        //put the character in the line.
        fields[cur_field][line_pos] = lastchar;

        //check which character we read.
        //check if we hit the end of the line or file.
        if (lastchar == '\r' || file_len < 1) {
            //the end of the line/file.
            //null terminate the current field.
            fields[cur_field][line_pos] = '\0';

            //reset our line position
            line_pos = 0;

            //null terminate the remainder of the fields.
            for (cur_field++; cur_field < NUM_DECO_FIELDS; cur_field++) {
                fields[cur_field][0] = '\0';
            }

            //read past the \n in the file.
            fgetc(file);
            file_len--;
        }
        //check if we are done with the current field.
        //either we find a comma or the field was too long.
        else if (lastchar == ',' || line_pos >= MAX_DECO_LINE_LEN - 1) {
            //we found the end of the current field.
            //null terminate the field string
            fields[cur_field][line_pos] = '\0';
            //go to the next field.
            cur_field++;
            //reset our line position.
            line_pos = 0;
        }
        else {
            //we didnt end our current field or our current line,
            //advance to the next line position
            line_pos++;
        }

        //check if we have read in all the fields.
        if (cur_field >= NUM_DECO_FIELDS) {
            //got all the fields.  

            int i;
            //the md2 file name.
            char *model_name;
            //the deco name.
            char *deco_name;

            //remove spaces from the fields.
            for (i = 0; i < NUM_DECO_FIELDS; i++) {
                StripSpaces(&fields[i][0]);
            }

            //get the bitmap and deco name.
            deco_name = &fields[0][0];
            model_name = &fields[1][0];

            //add the bitmap to our list.
            AddDecoToList(deco_name, model_name, &list, num_pics);

            //reset cur_field
            cur_field = 0;

            //reset our line position
            line_pos = 0;

            //read the next character.
            continue;
        }

        //read another character
    }
    
}

void FlipBMPScanlines(bitmap_t *bmp) {
    int i;
    unsigned char line[64 * 3];  //64 * 3 = 192

    if (bmp == NULL) return;
    if (bmp->data == NULL) return;
    if (bmp->width != 64) return;
    if (bmp->height != 64) return;
    if (bmp->bpp != 24) return;

    //go from line 0 to line 31.
    for (i = 0; i < 32; i++) {
        //save the scanline.
        memcpy(line, &bmp->data[i * 192], 192);

        //copy the bottom scanline up.
        memcpy(&bmp->data[i * 192], &bmp->data[(63 - i) * 192], 192);

        //copy the saved scanline back down.
        memcpy(&bmp->data[(63 - i) * 192], line, 192);
    }
}

void FlipBMPRGB(bitmap_t *bmp) {
    int i;

    if (bmp == NULL) return;
    if (bmp->data == NULL) return;
    if (bmp->width != 64) return;
    if (bmp->height != 64) return;
    if (bmp->bpp != 24) return;

    //go from line 0 to line 31.
    for (i = 0; i < 64; i++) {
        //go across the scanline.
        int x;
        for (x = 0; x < 64; x++) {
            unsigned char r;
            //the offset of the r component of this pixel in the bmp data array.
            int red_offset;

            red_offset = i * 192 + x * 3;

            //get the red.
            r = bmp->data[red_offset];

            //copy the blue to the red spot.
            bmp->data[red_offset] = bmp->data[red_offset + 2];

            //put the red where blue used to be
            bmp->data[red_offset + 2] = r;

        }
    }
}

//adds a deco with the given name and bitmap to the list. 
//the list pointer is moved to point to the end NULL pointer
//in the list.
void AddDecoToList(char *deco_name, char *model_name, deco_bitmap_t ***list_end, int *num_pics) {
    deco_bitmap_t *el;
    //the length of the md2 file name.
    int name_len;
    //the path of the bitmap file.
    char bitmap_file[512];

    //make sure we have non-blank names.
    if (deco_name[0] == '\0') return;
    if (model_name[0] == '\0') return;
    //make sure we are pointing at a pointer to the last NULL in the list.
    if (list_end == NULL || *list_end == NULL || **list_end != NULL) return;

    //get the length of the model name
    name_len = strlen(model_name);
    if (name_len < 4) return;
    //make sure there is a 3 character file extension on the name.
    if (model_name[name_len - 4] != '.') return;

    //swap the file extension.
    strcpy(&model_name[name_len - 4], "_pic.bmp");

    //allocate a list element.
    el = (deco_bitmap_t *)malloc(sizeof(deco_bitmap_t));
    if (el == NULL) return;

    //initialize the new element.
    el->next = NULL;

    //copy the name over.
    strcpy(el->name, deco_name);

    //get the name of the bitmap file.
    ExpandReletivePath(model_name, bitmap_file);
    if (bitmap_file[0] == '\0') return;

    //check if the file exists.
    if (GetFileAttributes(bitmap_file) == -1) {
        //the file doesnt exist.
        //delete the element we allocated.
        free(el);
        return;
    }

    //try to load the bitmap.
    LoadBMPWithInfo(bitmap_file, &el->bitmap, &el->info);

    //flip the bitmap scanlines
    FlipBMPScanlines(&el->bitmap);
    //flip the bitmap rgb's
    FlipBMPRGB(&el->bitmap);

    //add the bitmap to the list.
    **list_end = el;

    //move the list pointer.
    *list_end = &el->next;

    //increment the count of pics we have in the list.
    *num_pics = *num_pics + 1;
}