// ./make_cnt Demo2.map /dev/tty  (or /dev/stdout)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <error.h>
#include <errno.h>

#include "list.h"

#define MAXBUFF 512

typedef struct {
  char *replace;
  char *with;
  int special;
} SIMPLE_REPLACE;

#define T_NOT_SPECIAL 0
#define T_INCLUDE 1
#define T_ASSIGN 2

SIMPLE_REPLACE simple_replace[] = {{"INCLUDE", ".include", T_INCLUDE},
                                   {"EQU", " = ", T_ASSIGN},
                                   {"FDB", ".dw", T_NOT_SPECIAL},
                                   {"FCB", ".db", T_NOT_SPECIAL},
                                   {"FCC", ".ascii", T_NOT_SPECIAL},
                                   {"END", "; END", T_NOT_SPECIAL},
                                   {"LIST", "; LIST", T_NOT_SPECIAL},
                                   {"SETDP", "; SETDP", T_NOT_SPECIAL},
                                   {NULL, NULL}};
char *line;

T *program;
T *cnt;

void cnt_from_list(void);

int do_comments = true;

int main(int argc, char **argv) {
  int line_counter = 0;  // bei welcher Zeile sind wir gerade, nicht sicher, ob diese immer stimmt
                         // (current line, not sure if it is always correct)

  //fprintf(stdout, "\nCalling of");
  //for (int ii = 0; ii < argc; ii++) fprintf(stdout, " %s", argv[ii]);
  //fprintf(stdout, "\n");

  if (argc < 3) {
    fprintf(stderr, "Usage: make_cnt <infile> <outfile> <comment>\n");
    return 1;
  }

  if (argc > 3) do_comments = 0;

  if (!strcmp(argv[1], argv[2])) {
    fprintf(stderr, "make_cnt: infile must not be the same as outfile\n");
    return 1;
  }

  FILE *inFile = fopen(argv[1], "r");
  if (inFile == NULL) {
    fprintf(stderr, "make_cnt: cannot read \"%s\" - %s\n", argv[1], strerror(errno));
    exit(EXIT_FAILURE);
  }
  FILE *outFile = fopen(argv[2], "w");
  if (outFile == NULL) {
    fprintf(stderr, "make_cnt: cannot write \"%s\" - %s\n", argv[2], strerror(errno));
    exit(EXIT_FAILURE);
  }

  line = malloc(MAXBUFF + 1);
  if (line == NULL) {
    fprintf(stderr, "make_cnt fails: malloc error.\n");
    exit(EXIT_FAILURE);
  }
  program = new_T();
  cnt = new_T();

  // read program
  size_t got = MAXBUFF;
  while (getline(&line, &got, inFile) != -1) {  // read file
    //fprintf(stderr, "Debug: %s", line);
    char *newline = (char *)malloc(MAXBUFF + 1);
    if (newline == NULL) {
      fprintf(stderr, "make_cnt fails: malloc error.\n");
      exit(EXIT_FAILURE);
    }
    strcpy(newline, line);
    append(program, newline);
  }
  fclose(inFile);

  cnt_from_list();

  // write program
  first(cnt);
  while (!isLast(cnt)) {
    fprintf(outFile, "%s%s", (char *)retrieve(cnt), "\n");
    removeWithElementDelete(cnt);
  }
  fclose(outFile);

  first(program);
  while (!isLast(program)) {
    removeWithElementDelete(program);
  }
  return 0;
}

void insert_before(char *line, char *insert) {
  static char _line[MAXBUFF];
  strcpy(_line, line);
  line[0] = 0;
  strcat(line, insert);
  strcat(line, _line);
}

/* NOT parser specific yet, uses fgets and fputs instead of PARSE_END_OF_LINE */
/* replace every occurence of define with value */
int replace(char *line, char *define, char *value) {
  static char _line[MAXBUFF];
  char *pos;
  int posi = 0;
  int replaced = 0;
  pos = line;
  pos = strstr(pos, define);
  while (pos) {
    replaced++;
    posi = pos - line;
    strcpy(_line, line);
    *strstr(_line + posi, define) = (char)0;
    strcat(_line, value);
    strcat(_line, pos + strlen(define));
    strcpy(line, _line);
    pos = line + posi + strlen(value);
    pos = strstr(pos, define);
  }
  return replaced;
}

void remove_current_char(char *string) {
  while (*string != 0) {
    *string = (*(string + 1));
    string++;
  }
}

void remove_all_spaces(char *string) {
  while (*string != 0) {
    if (isspace(*string))
      remove_current_char(string);
    else
      string++;
  }
}

void cnt_from_list(void) {
  int i;
  char *newline = NULL;
  char last_address[MAXBUFF]; // = "0000";
  int in_symbol_table = false;
  int after_symbol = false;
  int in_prog = false;

  strcpy(last_address, "0000");
  first(program);
  while (!isLast(program)) {
    i = 0;
    line = (char *)retrieve(program);
    if (strstr(line, "Symbol   Value        Decimal")) {
      in_symbol_table = true;
    }
    if (strstr(line, "labels used")) {
      in_symbol_table = false;
      after_symbol = true;
    }
    if (in_symbol_table) {
      if (strstr(line, ":")) {
        if (!strstr(line, " <macro>")) {
          char *label;
          char *address;
          char *testline;
          int lpos = getPos(program);
          int is_equ = false;
          int is_label = false;
          label = strdup(line);
          strtok(label, ":");
          remove_all_spaces(label);
          address = strdup(1 + strstr(line, "$"));
          *(address + 4) = 0;
          if (!strstr(line, "-")) {
            if (*address >= 'c') is_label = true;
          } else {
            remove_current_char(address);
            remove_current_char(address);
          }
          while (!isLast(program)) {
            i = 0;
            testline = (char *)retrieve(program);
            if (strstr(testline, label)) {
              if (*(strstr(testline, label) + strlen(label)) == ':') {
                is_label = true;
                if (is_equ) break;
              } else if (strstr(testline, "#")) {
                is_equ = true;
                if (is_label) break;
              }
            }
            next(program);
          }

          if (is_equ) {
            newline = (char *)malloc(MAXBUFF + 1);
            if (newline == NULL) {
              fprintf(stdout, "make_cnt fails: malloc error.\n");
              exit(EXIT_FAILURE);
            }
            sprintf(newline, "EQU \"%s\" 0x%s ", label, address);
            append(cnt, newline);
          }

          if (is_label) {
            newline = (char *)malloc(MAXBUFF + 1);
            if (newline == NULL) {
              fprintf(stdout, "make_cnt fails: malloc error.\n");
              exit(EXIT_FAILURE);
            }
            sprintf(newline, "LABEL   0x%s \"%s\"", address, label);
            append(cnt, newline);
          }

          free(label);
          free(address);
          setPos(program, lpos);  // 0..ende-1
        }
      }
    }

    if (after_symbol) {
      if (*(line + 5) == ':') {
        char *address = strdup(line);
        in_prog = true;

        *(address + 4) = 0;
        if (do_comments) {
          if (strstr(line, ";")) {
            char *comment = strdup(strstr(line, ";"));
            newline = (char *)malloc(MAXBUFF + 1);
            if (newline == NULL) {
              fprintf(stdout, "make_cnt fails: malloc error.\n");
              exit(EXIT_FAILURE);
            }
            sprintf(newline, "COMMENT   0x%s \"%s\"", address, comment);
            append(cnt, newline);
            free(comment);
          }
        }
        strcpy(last_address, address);
        free(address);

      } else /* if (*(line+5)!=':') */ {
        if (in_prog) {
          if (do_comments) {
            if (strstr(line, ";")) {
              char *comment = strdup(strstr(line, ";"));
              newline = (char *)malloc(MAXBUFF + 1);
              if (newline == NULL) {
                fprintf(stdout, "make_cnt fails: malloc error.\n");
                exit(EXIT_FAILURE);
              }
              sprintf(newline, "COMMENT_LINE   0x%s \"%s\"", last_address, comment);
              append(cnt, newline);
              free(comment);
            }
          }
        }
      }
    }

    //0000 : 67204743452031..                      DB      "g GCE 1998", $80  ; 'g'

    next(program);
  }
  exit(EXIT_SUCCESS);
}
