#include <fcntl.h>
#include <stdio.h>
#include <errno.h>

#include "defs.h"
#include "memory.h"
#include "6502P.h"
#include "unix_io.h"
#include "unix_ioP.h"
#include "text.h"

/*
 * FIXME: tmp hack to allow IO with unix without doing the messy 6522 stuff
 * Some of this is based on code from the previous BBC Emulator by Ian
 * Stephenson.
 */

char *read_string(word addr) {
    static char string[256];
    int i = 0;

    do {
	string[i] = readb(addr);
	addr++;
    } while (string[i++] != '\r');
    string[--i] = '\0';
    
    return string;
}

/* not used */
void osword0() {
    int block;
    int strstart;
    int strl;
    char temp[256];

    block = X+Y*256;
    strstart = readb(block)+readb(block+1)*0x100;

    gets(temp);
    for(strl=0;temp[strl]!=0; strl++)
	writeb(strstart+strl,temp[strl]);
    strl=strlen((char *)temp);
    Y=strl; /* +1*/
    P &= ~0x01;
    writeb(strstart+strl,0x0D);
}

/* not used */
void osword() {
    switch (A) {
    case 0:
	osword0();
	break;
    }
}

static cat_info *next_cat_info(int job) {
    static FILE *fp = NULL;
    static cat_info info;
    cat_info_s info_s;
    
    switch (job) {
    case 1:
	if (NULL == (fp = fopen(".catalogue", "r"))) {
	    perror(".catalogue");
	}
	return (cat_info *)0;
    case 0:
	if (fread(&info_s, sizeof(cat_info_s), 1, fp) != 0) {
	    strncpy(info.filename, info_s.filename, 40);
	    info.load  = strtol(info_s.load,  NULL, 16);
	    info.exec  = strtol(info_s.exec,  NULL, 16);	
	    info.start = strtol(info_s.start, NULL, 16);
	    info.end   = strtol(info_s.end,   NULL, 16);
	    return &info;
	} else {
	    fclose(fp);
	    fp = NULL;
	    return (cat_info *)0;
	}
    default:
	if (fp)
	    fclose(fp);
    }
}

static cat_info *get_cat_info(char *file) {
    cat_info *info;
    int done = 0;

    (void)next_cat_info(1);

    do {
	info = next_cat_info(0);
	if (strcmp(file, info->filename) == 0) {
	    (void)next_cat_info(2);
	    return info;
	}
    } while (info);
}

static void write_cat_info(cat_info *info) {
    cat_info_s info_s;
    cat_info_s tmp;
    FILE *fp;
    int done = 0;

    /* FIXME: quick hack to ensure file exists */
    close(open(".catalogue", O_CREAT, 0666));
    if (NULL == (fp = fopen(".catalogue", "r+"))) {
	perror(".catalogue");
	return;
    }
    rewind(fp);

    strncpy(info_s.filename, info->filename, 40);
    sprintf(info_s.load,  "%08X", info->load);
    sprintf(info_s.exec,  "%08X", info->exec);
    sprintf(info_s.start, "%08X", info->start);
    sprintf(info_s.end,   "%08X", info->end);
    
    while (fread(&tmp, sizeof(cat_info_s), 1, fp) != 0) {
	if (strcmp(info_s.filename, tmp.filename) == 0) {
	    fseek(fp, -sizeof(cat_info_s), SEEK_CUR);
	    fwrite(&info_s, sizeof(cat_info_s), 1, fp);
	    done = 1;
	    break;
	}
    }

    if (!done)
	fwrite(&info_s, sizeof(cat_info_s), 1, fp);

    fclose(fp);
    return;
}

void osfile() {
    word block;
    word load_addr;
    word exec_addr;
    word start_addr;
    word end_addr;
    char *file;
    int i;

    block = Y * 0x100 + X;

    /* read filename */
    file = read_string(readb(block+1) * 0x100 + readb(block));

    /* read addresses */
    load_addr  = readb(block+ 2) + 0x100 * readb(block+ 3);
    exec_addr  = readb(block+ 6) + 0x100 * readb(block+ 7);
    start_addr = readb(block+10) + 0x100 * readb(block+11);
    end_addr   = readb(block+14) + 0x100 * readb(block+15);

    /* now do the relevant operation */
    switch (A) {
	FILE *fp;
	char c;
	word addr;
	cat_info info;
	cat_info *infop;

    case 0x00: /* save file */
	fp = fopen(file, "w");
	for (i = load_addr; i < end_addr; i++) {
	    c = readb(i);
	    fputc(c, fp);
	}
	fclose(fp);
	strncpy(info.filename, file, 40);
	info.load  = load_addr;
	info.exec  = exec_addr;
	info.start = start_addr;
	info.end   = end_addr;
	write_cat_info(&info);
	break;
    case 0x01: /* write cat info */
	break;
    case 0x02: /* write load addr */
	break;
    case 0x03: /* write exec addr */
	break;
    case 0x04: /* write attributes */
	break;
    case 0x05: /* read cat info */
	break;
    case 0x06: /* delete file */
	break;
    case 0x07: /* create empty file */
	break;
    case 0xff: /* load file */
	if ((fp = fopen(file, "r")) == NULL) {
	    puts("File not found.");
	    return;
	}
	infop = get_cat_info(file);

	/* FIXME: should read PAGE variable instead of assuming 0xE00 */
	load_addr = exec_addr ? (infop ? infop->load : 0xE00) : load_addr;
	addr = infop->end - infop->load + load_addr;

	for (i = load_addr; i < addr; i++) {
	    writeb(i, fgetc(fp));
	}
	
	fclose(fp);
	break;
    }
}

osfsc() {
    char *comline;
    int i;
    cat_info *infop;
    FILE *fp;
    word end_addr;
    word load_addr;

    switch (A) {
    case 0x00: /* *OPT */
	break;
    case 0x01: /* EOF */
	break;
    case 0x03: /* unrecognised * command */
	break;
    case 0x02: /* star/ */
    case 0x04: /* *RUN */
	comline = read_string(Y * 0x100 + X);

	if ((fp = fopen(comline, "r")) == NULL) {
	    puts("File not found.");
	    return;
	}
	infop = get_cat_info(comline);
	/* FIXME: should read PAGE variable instead of assuming 0xE00 */
	load_addr = infop ? infop->load : 0xE00;
	end_addr = infop->end - infop->load + load_addr;

	for (i = load_addr; i < end_addr; i++) {
	    writeb(i, fgetc(fp));
	}
	PC = infop->exec;

	break;
    case 0x05: /* *CAT */
	system("/bin/ls -Cq");
	break;
    case 0x06: /* new fs */
	break;
    case 0x07: /* return file handle range */
	break;
    case 0x08: /* *command */
	break;
    case 0x09: /* *EX */
	break;
    case 0x0a: /* *INFO */
	break;
    case 0x0b: /* library *RUN */
	break;
    }
}

void unix_call() {
    byte op;
    extern int debug;

    PC += 2;
    switch (op = readb(PC - 1)) {
    case 0x00:
	debug = 0;
	break;
    case 0x01:
	debug = 1;
	break;
    case 0x66: {
	int fd = open("data/new.bbc", O_RDWR | O_TRUNC | O_CREAT, 0666);
	write(fd, mem, 65536); 
	close(fd);
    }
    case 0xdd:
	osfile();
	break;
    case 0x1e:
	osfsc();
	break;
    case 0xee:
	/* oswrch */
	write_char(A);
	break;
    case 0xe0:
	/* osrdch */
	A = read_char();
	if (A == '\n')
	    A = '\r';
	break;
    case 0xf1:
	/* osword */
	osword();
	break;
    case 0xfe:
	PC = readw(0x206);
	break;
    case 0xff:
	writew(0xfd, readw(SP + 0x102));
	A  = readb(0xfc);
	PC = readw(0x202);
	printf("0x%04x -> 0xfd, 0x%02x -> A, 0x%04x -> PC",
	       readw(0xfd), A, PC);
	break;
    default:
	printf("Unknown unix_call %d\n", op);
	break;
    }
}

