#include "defines.h"
#include "parser.h"

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>

#define passReplacement 0
#define passLabel       1
#define passAssemble    2

const char strict_white_space[] = " \t\n\r(),[]";
char commands[kTotal_commands][32];
char arg_structure[kTotal_commands][64];  
unsigned char script[script_limit];         //finished script
int scr_i = 0;
char source[100][256];
int source_offset[100];

void upcase(char* str);
void pack_string(char * str);
char *get_word(char* str, char* buffer);
void do_pass(char str[][256], int pass, int total_lines);

int total_commands;

int main(int argc, char *argv[]) {
    FILE *labelFile,*scriptFile,*outFile;
    char buffer[256],*label_name_swap,parse_buffer[256],*ptr;
    int i,label_index = 0,op_i,arg_i,row_index,temp_label;
    unsigned int script_index = 0,current_command = 0;
    unsigned short label_swap,script_address,command_address[80];
    unsigned char script_command;
    label_struct temp_lab;

    if (argc < 2) {
        puts("Usage: parser <input file>");
        return 0;
    }
    
    memset(script,0,script_limit);
    
    if (!(scriptFile = fopen("scriptdef.asm","r"))) {
        puts("Error opening script definition file");
        return 1;
    }
    
    //read in commands
    i = 0;
    while (fgets(buffer,256,scriptFile)) {
        if (buffer[0] == ';') break;
        if (buffer[0] == '\t') {
                for (op_i = 0; buffer[op_i] != '\n' && buffer[op_i]; op_i++);
                buffer[op_i] = 0;
            if (buffer[2] != ';') {
                //handle command list
                upcase(buffer + 5);
                strcpy(commands[i],buffer + 5);
            } else {
                //handle argument definitions
                strcpy(arg_structure[i++],buffer + 3);
            }
        }
    }
    total_commands = i;
    
    
    if (!(labelFile = fopen("labels.lab","r"))) {
        puts("Error opening label files.");
        return 1;
    }
    puts("Reading in labels.");
    while (fgets(buffer,256,labelFile)) {
        for (i = 0; buffer[i] != ' '; i++);
        buffer[i] = 0;
        label_array[label_index].name = (char*) malloc(i+1);
        strcpy(label_array[label_index].name, buffer);
        sscanf(buffer+i+4,"%x",&temp_label);
        label_array[label_index].value = (unsigned short) temp_label;
        label_index++;
    }
    total_labels = label_index;
    fclose(labelFile);
   
    // begin reading in defines
    puts("Reading in structures.");
    if (!(labelFile = fopen("structures.inc","r"))) {
        puts("Error opening structures.inc");
        return 1;
    }
    while (fgets(buffer,256,labelFile)) {
        if (!next_char(buffer)) continue;
        for (i = 0; buffer[i] && buffer[i]!=' '; i++);
        buffer[i] = 0;
        temp_lab.name = (char*) malloc(i+1);
        strcpy(temp_lab.name, buffer);
        temp_lab.value = parse_string(buffer+i+2);
        insert_label(&temp_lab);
        //printf("%s: %04x\n",temp_lab.name,temp_lab.value);
    }
    fclose(labelFile);
    
    /*
    //special case for just sorting labels
    if (sort_only) {
        puts("Sorting complete. Saving to labels.lab...");
        outFile = fopen("labels.lab","w");
        for (i = 0; i < total_labels; i++) {
            fprintf(outFile,"%s = $%0.4x",label_array[i].name,label_array[i].value);
            if (i != total_labels-1) {
                fputs("\n",outFile);
            }
        }
        fclose(outFile);
        puts("Done.");
        return 0;
    }*/
    
    scriptFile = fopen(argv[1],"r");
    if (!scriptFile) {
        puts("Error opening source file.");
        return 1;
    }
    puts("Reading in lines.");
    //read script into line vector
    row_index = arg_i = i = 0;
    ptr = NULL;
    while (fgets(buffer,256,scriptFile)) {
        if (!next_char(buffer)) continue;
        if (*next_char(buffer)==';') continue;
        upcase(buffer);
        //ptr = next_char(buffer);
        //pack_string(ptr);
        if (i) ptr = source[row_index]+i; // continue a previous line
        else ptr = source[row_index];
            
        for (i = 0; buffer[i]!='\n' && !(buffer[i]==';' || buffer[i]==':'); i++);
        if (buffer[i] == ';' || buffer[i]==':') { //we hit a ';'
            buffer[i] = 0;
            i = 0;
            if (ptr > source[row_index]) strcpy(ptr,next_char(buffer));
            else strcpy(ptr,buffer);
            puts(source[row_index]);
            row_index++;
        } else { //if there was a line end without ';'
            char *tptr;
            buffer[i] = 0;
            if (ptr > source[row_index]) tptr = next_char(buffer);
            else tptr = buffer;
            strcpy(ptr,tptr);
            i = (ptr - source[row_index]) + strlen(tptr); //clip off terminating zero
        }
    }
        
    memset(source_offset,0,sizeof(source_offset)); 
    puts("Starting passes...");
    
    do_pass(source,passReplacement,row_index);
    do_pass(source,passLabel,row_index);
    do_pass(source,passAssemble,row_index);
    
    //change the extention on the file to save
    strcpy(buffer,argv[1]);
    for (i = 0; buffer[i] != '.'; i++);
    strcpy(buffer+i+1,"ccr");
    outFile = fopen(buffer,"w");
    //begin script output loop
    for (i = 0, op_i = 0; i < scr_i; i++, op_i++) {
        if (!op_i) fprintf(outFile," .db ");
        fprintf(outFile,"$%0.2X",script[i]);
        if (op_i != 5 && i < scr_i-1) {
            fputc(',',outFile);
        } else {
            fprintf(outFile,"\n");
            op_i = -1;
        }
    }
    //fprintf(outFile,"\n.end");
    
    printf("Output file is %s.\n",buffer);
     
    fclose(scriptFile);
    //fclose(outFile);
    return 0;
}




char* get_span(char* str, char* buffer, char limit) {
    int i, op_i;
    for (i = 0; *str != 0; str++) {
        if (*str == limit) {
            if (i) break;
        } else {
            buffer[i++] = *str;
        }
    }
    buffer[i] = 0;
    if (*str == 0 && !i) {
        return NULL;
    }
    return str;
}



void do_pass(char str[][256], int pass, int total_lines) {
    int script_offset = 0,i,cline = 0;
    unsigned int result,result2,ci;
    char parsebuf[128],*ptr;
    char namebuf[16],resultbuf[128];
    label_struct templab;
    
    printf("Doing pass %d...\n",pass);
    
    for (; cline < total_lines; cline++) {
        if (pass == passReplacement) {
            for (i = 0; str[cline][i] && str[cline][i] != '.'; i++);
            if (str[cline][i]) {
                for (i = 0; str[cline][i] && str[cline][i]!='['; i++);
                if (!str[cline][i]) {
                    printf("Bad access: %s\n",str[cline]);
                    return;
                }
                ptr = get_word(str[cline],namebuf);
                ptr = get_word(ptr,parsebuf);
                ptr += 2;
                result = parse_string(parsebuf);
                ptr = get_word(ptr,parsebuf);
                ptr = next_char(ptr);
                ptr = next_char(ptr + 1);
                puts(ptr);
                result2 = parse_string(ptr);
                strcpy(resultbuf,namebuf);
                strcat(resultbuf,"_");
                strcat(resultbuf,parsebuf);
                if (parse_string(resultbuf)) {
                    printf("Searching with %s...\n",resultbuf);
                    if (search_labels(resultbuf)!=-1) {
                        sprintf(str[cline]," SET_%s_ATTR8(%d,%s,%d)",namebuf,result,resultbuf,result2);
                        printf(" SET_%s_ATTR8(%d,%s_%s,%d)\n",namebuf,result,namebuf,parsebuf,result2);
                    } else if (search_labels(strcat(resultbuf,"_"))!=-1) {
                        sprintf(str[cline]," SET_%s_ATTR16(%d,%s,%d)",namebuf,result,resultbuf,result2);
                        printf(" SET_%s_ATTR16(%d,%s_%s,%d)\n",namebuf,result,namebuf,parsebuf,result2);
                    } else {
                        printf("%s is not a valid structure member.\n");
                        exit(1);
                    }                      
                } else {
                    sprintf(str[cline]," ACTIVATE_%s(%d, %d)",namebuf,result,result2);
                    printf(" ACTIVATE_%s(%d, %d)\n",namebuf,result,result2);   
                }
            }
        } else {
            //puts(str[cline]);
            source_offset[cline] = script_offset;
            printf("Offset: %d\n",script_offset);
            if (next_char(str[cline]) > str[cline]) { // if the first character is spaced forward, it's a command
                ptr = get_word(str[cline],parsebuf);
                for (i = 0; i < total_commands && strcmp(parsebuf,commands[i]); i++);
                
                if (i < total_commands) {
                    char *argptr;
                    int argcount = 0,ci;
                    
                    script_offset++; //add one for the "opcode"
                    
                    if (pass == passAssemble) script[scr_i++] = i;
                    
                    ptr = next_char(ptr);
                    if (ptr && *ptr == '(') ptr++;
                    else {
                        //puts("Singler.");
                        continue;
                        // it's a singler.
                        // output the byte.
                    }
                    /* parse values of arguments */
                    strcpy(namebuf,"ARGX");
                    ci = 0;
                    for (ptr = get_span(ptr,resultbuf,','); ptr; ptr = get_span(ptr,resultbuf,',')) {
                            namebuf[3] = '1'+ci;
                            //puts(namebuf);
                            if (!*ptr) get_span(resultbuf,resultbuf,')');
                            label_array[search_labels(namebuf)].value = parse_string(resultbuf);
                            //puts(resultbuf);
                            ci++;               
                    }

                    for (argptr = get_span(arg_structure[i],parsebuf,','); argptr; argptr = get_span(argptr,parsebuf,',')) {
                        if (*next_char(parsebuf)=='*') {
                            script_offset+=2;
                            if (pass == passAssemble) {
                                ci = parse_string(next_char(parsebuf)+1);
                                script[scr_i++] = ci & 0xFF;
                                script[scr_i++] = ci >> 8;
                            }
                        } else {
                            script_offset++;
                            if (pass == passAssemble) 
                                script[scr_i++] = parse_string(parsebuf) & 0xFF;
                        }
                        argcount++;
                    }
                        
                } else {
                    puts("Invalid command.");
                    return;
                }
            } else {
                get_span(str[cline],parsebuf,':');
                templab.name = malloc(strlen(parsebuf)+1);
                templab.value = script_offset;
                insert_label(&templab);
            }
        }
    }
}
    


void upcase(char* str) {
    int temp = 'a'-'A';
    int i;
    BOOL within_quote;
    
    within_quote = FALSE;
    for (i = 0; str[i]; i++) {
        if (str[i]>='a' && str[i]<='z' && !within_quote) {
            str[i]-=temp;
            continue;
        }
        if (str[i]=='\'') within_quote = !within_quote;
    }
}

void pack_string(char * str) {
    int temp = 'a'-'A';
    int i,str_i,arg_i;
    
    for (i = 0,str_i = 0; str[i]; i++) {
        for (arg_i = 0; arg_i < 4 && str[i] != strict_white_space[arg_i]; arg_i++);
        if (arg_i == 4) {
            str[str_i++] = str[i];
        }
    }
    str[str_i] = 0;
    upcase(str);
}

// returns pointer to byte after string
// fills given string with the next word
char *get_word(char* str, char* buffer) {
    int i, op_i;
    for (i = 0; *str != 0; str++) {
        for (op_i = 0; op_i < 9 && (*str != strict_white_space[op_i]); op_i++);
        if (op_i < 9) {
            if (i) break;
        } else {
            buffer[i++] = *str;
        }
    }
    buffer[i] = 0;
    if (*str == 0 && !i) {
        return NULL;
    }
    return str;
}
