#include <stdio.h>
#include "defines.h"
#include "parser.h"
#include "main.h"

void do_directive(FILE *outFile, int dir, char *ptr) {
    static int directive_level = 0;

    directive_level++;
    switch (dir) {
        BOOL is_word, is_echo;            
        case 6: //ORG
            program_counter = parse_string(ptr);
            break;
        case 3: //LIST
            tabFile = tabBackup;
            tfputc = (tabFile) ? &tabfputc : &fputc;
            break;
        case 4: //NOLIST
            tabFile = NULL;
            tfputc = &fputc;
            break;
        case 5: //END
            break;
        case 9: {//FILL
            int size, result;
            char parse_buf[256];
            
            ptr = get_level_word(ptr, parse_buf,',');
            size = parse_string(parse_buf);
            result = parse_string(ptr);
            result &= 0xFF;
            goto finish_fill;          
        case 10: //BLOCK
            get_level_word(ptr, parse_buf,',');
            size = parse_string(parse_buf);
            result = 0;
        finish_fill:
            if (current_parse_error) break;
            if (size > 0) {
                if (!pass_one && tabFile) tabinsert("Writing %d bytes of %x",size, result);
                
                program_counter += size;
                if (!pass_one) while(size--) fputc(result,outFile);
            }
            break;
        }
        case 11: //ADDINSTR
            if (pass_one) add_instruction(ptr);
            break;
        case 1: //DW
        case 8: //WORD
            is_word = TRUE;
            goto finish_data;
        case 0: //DB
        case 7: //BYTE
            is_word = FALSE;
        finish_data:
            is_echo = FALSE;
            goto do_list;
        case 12: 
            is_echo = TRUE;
        do_list: { //ECHO
            FILE *echo_target = stdout;
            char target_buffer[256],target_format[2] = "w",word_buf[128];
            
            if (is_echo) {
                if (pass_one) break;
                if (ptr[0] == '>') {
                    int m_loc;
                    
                    if ((++ptr)[0] == '>') target_format[0] = 'a';
    
                    ptr = next_char(ptr + 1);
                    if (!ptr) {
                        post_error("No filename given for echo target.");
                        break;
                    }
                    /* Give pointer the first character after the filename word */
                    ptr = get_word(ptr, target_buffer);
                    if ((m_loc = search_defines(reduce_string(target_buffer))) != -1) {
                        strcpy(target_buffer, next_char(define_array[m_loc].define));
                    }
                    if (!(echo_target = fopen( reduce_string(target_buffer),target_format))) {
                        post_error("Failed to open file \"%s\" for echo", target_buffer);
                        break;
                    }
                }
            }
            
            while (ptr = get_level_word( next_char(ptr), word_buf, ',')) {
                if (word_buf[0] == '"') {
                    reduce_string(word_buf);
                    if (is_echo) fprintf(echo_target, word_buf);
                    else {
                        int i;
                        for (i = 0; word_buf[i]; i++) {
                            if (!pass_one) tfputc(word_buf[i], outFile);
                            program_counter++;
                        }
                    }
                } else {
                    int m_loc, result;
                    char macro_buf[256], *end_char;

                    end_char = get_level_word(word_buf, macro_buf, '(');
                    m_loc = search_defines(pack_string(macro_buf));
                    if (m_loc == -1 || next_char(end_char)) {
                        //printf("Parsing for db: %s\n",word_buf);
                        result = parse_string(word_buf);
                        if (is_echo) {
                            if (!current_parse_error) fprintf(echo_target,"%d",result);
                            else fprintf(echo_target,"(error)");
                        } else {
                            if (!pass_one) tfputc(result & 0xFF, outFile);
                            program_counter++;
                            if (is_word) {
                                if (!pass_one) tfputc(((unsigned int) result >> 8) & 0xFF, outFile);
                                program_counter++;
                            }
                        }
                    } else {
                        if (is_echo && echo_target != stdout) {
                            char echo_buf[5 + 256 + strlen(define_array[m_loc].define)];
                            
                            fflush(echo_target);
                            strcat(strcat(strcpy(echo_buf,">> "), target_buffer)," ");
                            do_directive(outFile, dir, strcat(echo_buf, define_array[m_loc].define));
                        } else {
                            do_directive(outFile, dir, define_array[m_loc].define);
                        }
                    }
                }
            }
            //printf("Done handling echo.\n");
            if (is_echo) {
                if (echo_target == stdout) {
                    if (directive_level == 1) putchar('\n');
                } else fclose(echo_target);
            }
            break;
        }
        case 13: //ERROR
            if (!pass_one) {
                if (tabFile) fflush(tabFile);
                exit(parse_string(ptr));
            }
            break;
    }
    directive_level--;
}   

void handle_directive(FILE *outFile, char *word_buf, char *ptr) {
    int dir;
    char dir_buf[16 + strlen(ptr)+1];
    static const char directives[][16] = {"DB","DW","EQU","LIST","NOLIST","END","ORG","BYTE","WORD","FILL","BLOCK","ADDINSTR","ECHO","ERROR"};
    
    //printf("wordbuf: %s\nptr: %s\n",word_buf,ptr);
    //printf("Got a directive at %s somehow\n",ptr+1);
    ptr = next_char(get_word(ptr + 1, dir_buf));
    upcase(dir_buf);
    //printf("Directive: %s\n",dir_buf);
    for (dir = 0; dir < kDirective_amount && strcmp(directives[dir],dir_buf); dir++);
    /* Skip this step if it's not a label assigning directive */
    if (dir < kDirective_amount) {
        if (word_buf[0] != '.') {
            int word_len = strlen(word_buf),i;
            char assemble_buf[strlen(ptr) + word_len + 3];
    
            if (dir != 2) {
                do_assemble_line(outFile,word_buf);
                return do_directive(outFile, dir, ptr);
            } else if (pass_one) {
                ptr -= word_len + 2;
                for (i = 0; i < word_len; i++) ptr[i] = word_buf[i];
                ptr[i] = ' ';
                ptr[i+1] = '=';
                return assemble_line(outFile, ptr);
            }
        } else {
            do_directive(outFile, dir, ptr);
            //printf("Did directive: %s\n",ptr);
        }
    } else {
        if (pass_one) post_error("Invalid directive %s.", dir_buf);
    }
}
