#include <stdio.h>
#include "defines.h"
#include "main.h"
#include "parser.h"

int last_define = 0;

//BOOL (*op_table[])(int, int) = {&c_eq,&c_gr,&c_l,&c_eq,&c_gre,&c_le,&c_neq};
BOOL c_eq(int arg1, int arg2) {
    if (arg1 == arg2) return TRUE;
    return FALSE;
}
BOOL c_gr(int arg1, int arg2) {
    if (arg1 > arg2) return TRUE;
    return FALSE;
}
BOOL c_l(int arg1, int arg2) {
    if (arg1 < arg2) return TRUE;
    return FALSE;
}
BOOL c_neq(int arg1, int arg2) {return !c_eq(arg1,arg2);}
BOOL c_gre(int arg1, int arg2) {return c_gr(arg1,arg2) || c_eq(arg1,arg2);}
BOOL c_le(int arg1, int arg2) {return c_l(arg1,arg2) || c_eq(arg1,arg2);}    


void do_preop(FILE* outFile, int pre, char* ptr) {
    switch (pre) {
        case 0: { /* Define */
            int i;
            char word_buf[strlen(ptr) + 1];
            char null_define[] = " ";
            
            for (i = 0; ptr[i] && ptr[i] != '(' && ptr[i] != ' ' && ptr[i]!='\t'; i++);
            if (ptr[i] != '(') {
                char *define = get_word(ptr, word_buf);
                //printf("No arguments on this macro %s %s\n",word_buf, define);
                if (!next_char(define)) define = null_define - 1;
                /* Increment define to skip white space */
                last_define = insert_define(word_buf, define + 1);
                
                //printf("'%s' '%s'\n",define_array[last_define].name,define_array[last_define].define);
            } else {
                int name_len;
                ptr = get_level_word(ptr, word_buf, '(');
                name_len = strlen(pack_string(word_buf));
                
                ptr = get_level_word(ptr, word_buf + name_len + 1, ')');
                if (!next_char(ptr)) ptr = null_define - 1;
    
                last_define = insert_define(word_buf, ptr + 1);
                define_array[last_define].name = (char*) realloc(define_array[last_define].name, 
                    name_len + strlen(word_buf + name_len + 1 ) + 2);
                strcpy(define_array[last_define].name + name_len + 1, word_buf + name_len + 1);
                //printf("'%s' '%s'\n",define_array[last_define].name,define_array[last_define].define);
            }
        }
        break;
        case 1: {//INCLUDE
            FILE *tempFile;
            BOOL binfile,seen_space;
            int tempchar, i;
            
            if (!(ptr = next_char(ptr))) {
                post_error("Missing filename to include.");
                break;
            }
            if (ptr[0] == '"') {
                for (i = 1; ptr[i] != '"' && ptr[i]; i++) ptr[i-1] = ptr[i];
                ptr[i-1] = 0;
            }
            for (i = 0; ptr[i] && ptr[i]!='.'; i++);
            i++;
            
            //assemble known text types.
            if (!strcmp(ptr + i, current_extention) || !strcmp(ptr + i,".inc")) {
                return assemble_file(ptr,outFile);
            }
            // begin dealing with binary, maybe.
            if (!(tempFile = fopen(ptr,"rb"))) {
                return post_error("Couldn't find file %s.",ptr);
            }
        
            if (fgetc(tempFile) == 'B' && fgetc(tempFile) == 'M' && 
                !(fseek(tempFile,4L,SEEK_CUR),(fgetc(tempFile) || fgetc(tempFile)
                    || fgetc(tempFile) || fgetc(tempFile)))) {
                int width,scan,offset,image_byte,height;

                unsigned char ibuffer[16384/4];
                // Handle bitmap.
                //printf("Bitmap file!\n");
                offset = fgetc(tempFile);
                offset += fgetc(tempFile) << 8;
                fseek(tempFile,6L,SEEK_CUR);
                width = fgetc(tempFile);
                width += fgetc(tempFile) << 8;
                fgetc(tempFile);
                fgetc(tempFile);
                height = fgetc(tempFile);
                height += fgetc(tempFile) << 8;
                
                if (width % 8) width+=8;
                width>>=3;
                scan = (width % 4) ? 4 : 0;
                scan += (width & ~0x3);
                fseek(tempFile, offset, SEEK_SET);

                i = -width;
                while (height--) {
                    int ci;
                    for (i+=width*2, ci = 0; ci < scan && (image_byte = fgetc(tempFile)) != EOF; ci++) {
                        if (ci < width) {
                            ibuffer[--i] = ~image_byte;
                            program_counter++;
                        }
                    }
                }
                fclose(tempFile);
                i += width;
                while (i-- && !pass_one) fputc(ibuffer[i], outFile);
                if (tabFile && !pass_one) tabinsert("Bitmap file included successfully.");
            } else {
                binfile = seen_space = FALSE;
                for (i = 0; ((tempchar = fgetc(tempFile)) != EOF) && i < 1024; i++) {
                    if (tempchar == '\t' || tempchar == ' ') seen_space = TRUE;
                    if ((tempchar >= 14 && tempchar <= 31) || tempchar > 145) {
                        binfile = TRUE;
                        break;
                    }
                }
                if (binfile || !seen_space) {
                    fseek(tempFile,0L,SEEK_SET);
                    if (!pass_one && tabFile) {
                        list_bytes = 0;
                        fprintf(tabFile,"bin   %04X: ",program_counter);
                    }
                    while ((tempchar = fgetc(tempFile))!=EOF) {
                        if (!pass_one) {
                            if (tabFile && list_bytes==4) {
                                fprintf(tabFile,"\nbin   %04X: ",program_counter);
                                list_bytes = 0;
                            }
                            tfputc(tempchar,outFile);
                        }
                        program_counter++;
                    }
                    fclose(tempFile);
                } else {
                    fclose(tempFile);
                    assemble_file(ptr, outFile);
                }
            }
            if (tabFile && !pass_one) fprintf(tabFile,"\n");
            //printf("Done with %s.\n",ptr);
            break;
        }
        case 2: //IF
            {
            static const char comparitors[4] = "=><!";
            BOOL (*op_table[])(int, int) = {&c_eq,&c_gr,&c_l,&c_eq,&c_gre,&c_le,&c_neq};
            int current_comp,targ1,targ2, i;
            char arg_buffer[64];
            char word_buf[256];
            //puts("IF");
            strcpy(word_buf,ptr);
            for (i = 0; word_buf[i]; i++) {
                for (current_comp = 0; current_comp < kComparitors_amount && comparitors[current_comp] != word_buf[i]; current_comp++);
                if (current_comp != kComparitors_amount) break;
            }
            if (!word_buf[i]) {
                targ2 = 0;
                targ1 = parse_string(word_buf);
                current_comp = 1;
            } else {
                word_buf[i] = 0;
                // recommend arg buffer for parsing
                strcpy(arg_buffer,word_buf);
                i++;
                if (word_buf[i] == '=') {
                    current_comp+=3; i++;
                }
                targ1 = parse_string(arg_buffer);
                targ2 = parse_string(word_buf+i);
            }
            //printf("%s: %d, %s: %d\n",arg_buffer,targ1,word_buf+i,targ2);
            if (!op_table[current_comp](targ1,targ2)) goto finish_if;
            break;
        }
        case 3: 
        do_if: {//IFDEF
            char word_buf[256];
            char arg_buf[2][64];
            get_word(ptr,word_buf);
            if (search_defines(word_buf) != -1) break;
        finish_if:
            strcpy(arg_buf[0], "#ELSE");
            strcpy(arg_buf[1], "#ENDIF");
            skip_until(arg_buf, 2);
            break;
        case 4: //IFNDEF
            //printf("ifndefing ...\n");
            get_word(ptr,word_buf);
            if (search_defines(word_buf) == -1) break;
            goto finish_if;
            break;
        case 5: // ELSE
            strcpy(arg_buf[0], "#ENDIF");
            skip_until(arg_buf, 1);
            break;
        }
        case 6: // ENDIF
        case 10: // ENDCOMMENT
        case 12:
            //puts("ENDIF");
            break;
        case 7: //DEFCONT
            //break if nothing is after it
            if (!ptr) break;
            //printf("defcnt ptr: = \"%s\"\n",ptr);
            define_array[last_define].define = (char*) realloc(define_array[last_define].define,
                strlen(ptr) + strlen(define_array[last_define].define) + 1);
            strcat(define_array[last_define].define, ptr);
            //printf("new define: %s\n",define_array[last_define].define);
            break;
        case 8: {// UNDEFINE
            char word_buf[256];
            int i;
            
            if (!(ptr = next_char(ptr))) {
                post_error("No argument for directive undefine.");
                break;
            }
            strcpy(word_buf, ptr);
            i = search_defines(ptr);
            if (i != -1) {
                free(define_array[i].name);
                free(define_array[i].define);
                total_defines--;
                while (i < total_defines) {
                    define_array[i] = define_array[i+1];
                    i++;
                }
            } else {
                post_error("Macro \"%s\" was already undefined",word_buf);
            }
            break;
        }
        case 9: {// COMMENT
            char arg_buf[1][64];
            strcpy(arg_buf[0], "#ENDCOMMENT");
            skip_until(arg_buf,1);
            break;
        }
        case 11: { //MACRO
            char input_buf[256];
            char word_buf[256];
            
            do_preop(outFile, 0, ptr);
            //printf("macro pointer: %s\n",ptr);
            if (tabFile && !pass_one) fprintf(tabFile,"-  -  -  -  %s",linebuf);
            while (get_line(current_file, input_buf)) {
                //printf("mcr ln: %s\n",input_buf);
                if (next_char(input_buf)) {
                    if (tabFile && !pass_one) fprintf(tabFile,"%5d %04x~             %s\n",
                        line_number,program_counter,input_buf);
                    get_word(input_buf, word_buf);
                    if (!strcmp(upcase(word_buf), "#ENDMACRO")) break;
                    strcat(input_buf,"\\");
                    do_preop(outFile, 7, input_buf);
                }
            }
            break;
        }
    }
}
                    
void handle_preop(FILE* outFile, char *word_buf, char *ptr) {
    static const char preops[][16] = {"DEFINE","INCLUDE","IF","IFDEF","IFNDEF",
        "ELSE","ENDIF","DEFCONT","UNDEFINE","COMMENT","ENDCOMMENT","MACRO","ENDMACRO"};
    int pre;

    upcase(word_buf);
    for (pre = 0; pre < kPreop_amount && strcmp(word_buf + 1, preops[pre]); pre++);
    if (pre < kPreop_amount) {
        do_preop(outFile, pre, ptr);
    } else {
        if (pass_one) post_error("Invalid pre-operation %s.", word_buf);
    }
}
