#include <inc/lib.h>
//#include <string.h>

static void
parse_args(char *buf, char **args) {
    int i = 0;
    int argc = 1;    
    while (buf[i] != 0) {
        while(!((buf[i] == ' ')||(buf[i] == 0))) {
            i++;
        }
        if (buf[i] == ' ') {
            buf[i] = 0;
            i++;
            args[argc] = &(buf[i]);
        }
        argc++;       
        
    }
    args[argc] = 0;    
}

#define back 1

#define CMD_READ 1
#define CMD_SET 2
#define CMD_BACK 3
#define CMD_STEP 4
#define CMD_CONT 5
#define CMD_RUN 6
#define CMD_QUIT 7


static int
parse_cmd(char *buf) {
    
    char cmd[10];
    int i;
    memset(cmd,0,10);    
    i=0;
    while(!((buf[i] == ' ')||(buf[i] == 0))) {
        cmd[i] = buf[i];
        i++;
    }

    if (strcmp(cmd,"read\0") == 0) {
        return CMD_READ;
    } else if (strcmp(cmd,"set\0") == 0) {
        return CMD_SET;
    } else if ((strcmp(cmd,"s\0") == 0) ||(strcmp(cmd,"step\0") == 0)) {
        return CMD_STEP;
    } else if ((strcmp(cmd,"b\0") == 0) ||(strcmp(cmd,"backstep\0") == 0)) {
        return CMD_BACK;
    } else if ((strcmp(cmd,"c\0") == 0)||(strcmp(cmd,"continue\0") == 0)) {
        return CMD_CONT;
    } else if (strcmp(cmd,"run\0") == 0) {
        return CMD_RUN;
    } else if (strcmp(cmd,"quit\0") == 0) {
        return CMD_QUIT;
    } else {
        return -1;
    }
}

static void 
print_line(envid_t envid) {

    char src_file[64];
    memset(src_file, 0, 64);
    int line = 0;
    if (sys_debug_file(envid, src_file, &line) < 0) {
        cprintf("no line found\n");
        return;
    }
     
    char *p;
    char file[64];
    memset(file,0,64);
    if ((p = (char*)strchr(src_file,'/')) == 0) {
         file[0] = '/';
         strcpy(file+1,src_file);
    } else {
        strcpy(file,p);
    }
    //cprintf("file: %s\n",file);    
    int fdnum;
    if ((fdnum = open(file, O_RDONLY)) < 0) {
        cprintf("could not open file\n");
        return;
    }
    
    struct Stat stat;
    if (fstat(fdnum, &stat) < 0) {
        cprintf("could not stat file\n");
        close(fdnum);
        return;
    }
    char buf[1024];
    int line_count = 1;
    off_t pos = 0;
    //cprintf("line %d: %d\n",count,p);        
    while ((line_count < (line))&&(pos < stat.st_size)) {
        //cprintf("pos: %08x\n",pos);
        seek(fdnum,pos);
        memset(buf,0,1024);
        if ((pos+1023) < stat.st_size) {
            readn(fdnum, buf, 1023);
        } else {
            readn(fdnum, buf, stat.st_size-pos);
        }
        
        p = ((char *)strchr(buf,'\n'));
        if (p != 0) {
            line_count++;
            pos += (off_t)(p-buf+1);
        } else {
            pos += 1023;
        }        
    }
    if (pos >= stat.st_size) {
        close(fdnum);
        return;
    }
    memset(buf,0,1024);
    seek(fdnum,pos);
    readn(fdnum,buf,1023);

    //cprintf("%s",buf);
    char *e = (char *)strchr(buf,'\n');
    if (e == 0) {
        cprintf("%d: %s\n",line, buf);
    } else {
        char print[e-buf+1];
        memset(print,0,e-buf);
        strncpy(print,buf,e-buf);
        print[e-buf] = 0;
        cprintf("%d: %s\n",line,print);    
    }
    //cprintf("%d: %s", line, print);
    close(fdnum);
}

void
usage(void)
{
	fprintf(1, "usage: jdb [program...]\n");
	exit();
}

void
umain(int argc, char **argv) {
    
    if (argc < 2) {
        usage();
    }
    char argv0buf[1024];
	int i;
    
    // fix the command
    if (argv[1][0] != '/') {
		argv0buf[0] = '/';
		strcpy(argv0buf + 1, argv[1]);
		argv[1] = argv0buf;
	}

    const int NUM_STEPS = 64;
    envid_t back_steps[NUM_STEPS];
    int cur_step = 0;
    char *buf;
    char *args[16];
    envid_t prog = -1;

    buf = readline("(jdb) ");
    int cmd = parse_cmd(buf);
    while(cmd != CMD_QUIT) {
        switch(cmd) {
        case CMD_RUN:
            if (prog == -1) {
                args[0] = argv[1];
                parse_args(buf,args);
                prog = spawn(argv[1], ((const char**) args), 1);
                //cprintf("running program in env %08x\n",prog);
                cur_step = 0;
                int i=0;
                int r = sys_page_map(prog, (void*)(USTABDATA), 0, (void*)(USTABDATA), PTE_U|PTE_P); 
                while(r != -E_INVAL) {
                    r = sys_page_map(prog, (void*)(USTABDATA+i), 0, (void*)(USTABDATA+i), PTE_U|PTE_P); 
                    i+=PGSIZE;
                }
                if (prog >= 0) {
                    if (sys_debug_run(prog) < 0) {
                        cprintf("%s finished.\n",argv[1]);
                    } else {
                        if (back) {
                            //cprintf("Step %d:\n",cur_step);
                            back_steps[cur_step%NUM_STEPS] = checkpoint(prog);
                            //cprintf("Env %08x\n", back_steps[cur_step%NUM_STEPS]);
                            cur_step++;
                        }
                        print_line(prog);
                    }
                    //cprintf("done debugging");
                }
            } else {
                buf = readline("program is already running, restart?(y/n) ");
                if (buf[0] == 'y') {
                    sys_env_destroy(prog);
                    prog = -1;
                    cmd = CMD_RUN;
                    continue;
                }
            }
            break;
        case CMD_BACK:
            if (back) {
                cur_step--;
                //cprintf("Step %d\n",cur_step);
                sys_env_swap(prog,back_steps[cur_step%NUM_STEPS]);
                //cprintf("swapped %08x and %08x\n",prog, back_steps[cur_step%NUM_STEPS]);
                //sys_env_set_status(prog,ENV_RUNNABLE);
                //cprintf("set %08x to runnable\n",prog);
                sys_env_destroy(back_steps[cur_step%NUM_STEPS]);
                //cprintf("destroy %08x\n",back_steps[cur_step%NUM_STEPS]);
                print_line(prog);
            }
            break;
        case CMD_STEP:
            if (prog != -1) {
                if (sys_debug_step(prog) < 0) {
                    cprintf("%s finished.\n",argv[1]);
                } else {
                    if (back) {
                        //cprintf("Step %d:\n",cur_step);
                        back_steps[cur_step%NUM_STEPS] = checkpoint(prog);
                        cur_step++;
                    }
                    print_line(prog);
                }
            } else {
                cprintf("no program running.\n");
            }
            break;
        case CMD_READ:
            if (prog != -1) {
                parse_args(buf, args);
                sys_debug_peek(prog,args[1]);
            } else {
                cprintf("no program running.\n");
            }
            break;
        case CMD_SET:
            if (prog != -1) {
                parse_args(buf, args);
                int val = 0;
                int i;
                for(i = 0; i < strlen(args[2]); i++) {
                    val = val*10 + ((int)(args[2][i])) - 48;
                    //cprintf("val: %d\n",val);
                }
                sys_debug_poke(prog,args[1],val);
            } else {
                cprintf("no program running.\n");
            }
            break;
        case CMD_CONT:
            if (prog != -1) {
                if (sys_debug_run(prog) < 0) {
                    cprintf("%s finished.\n",argv[1]);
                } else {
                    if (back) {
                        //cprintf("Step %d:\n",cur_step);
                        back_steps[cur_step%NUM_STEPS] = checkpoint(prog);
                        cur_step++;
                    }
                    print_line(prog);
                }
            } else {
                cprintf("no program running.\n");
            }
            break;
        default:
            cprintf("[run, continue, read, set, step, backstep, quit]\n");
        }

        buf = readline("(jdb) ");
        cmd = parse_cmd(buf);    
    }
    cprintf("exiting jdb ...\n");
}
