#include <stdint.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <FL/Fl.H>
#include <Fl/Fl_Window.H>
#include <FL/Fl_File_Chooser.H>
#include "Fl/Fl_Export.H"
#include "Fl/fl_types.h"
#include <Fl/Fl_Tree.H>

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Table.H>
#include <FL/fl_draw.H>
#include <FL/fl_ask.H>
#include <dirent.h>


#include "ui.h"
#include "avr.h"
#include "avrsim.h"

progmem flash[0x10000];
uint8_t eeprom[0x10000],ram[0x10000];

uint8_t ports[MAX_PORTS];
uint8_t bits[MAX_BITS];

int last_modified_ram=-1;
int last_modified_eeprom=-1;
int last_modified_flash=-1;

Fl_Double_Window *ram_win,*eeprom_win,*flash_win;
Fl_Double_Window *tree_win;
Fl_Tree *tree;
Fl_Tree_Item *gr,*reg;

UserInterface *ui;
int run=0;
int brkp=0;
fn_rec *fn_root;

long int f_cpu=1000000;
int max=0,pc=0,clockcycles=0;
int sourcefile,sourceline;
int level=0;
char fn_hexFile[PATH_MAX],devicename[50];
char fileshortname[PATH_MAX];
char includePath[10*PATH_MAX];
char definitions[PATH_MAX];
uint8_t eeadrh,eeadrl,eedr,eecr;
uint16_t sramstart=0x60,ramend=0x45f;
uint16_t flashend=0xff,ioend=0x3f;
char flags[]="ITHSVNZC";
char io_names[256][16];
reg_rec registers[32];

#include "table.cpp"
#include "hardware.c"
#include "disass.c"
#include "simul.c"
#include "file_oper.c"

#ifndef WIN32
#include "images/avrsim.xpm"
static Fl_Pixmap p_sim(avr_sim_xpm);
static Fl_RGB_Image img_icon(&p_sim,0);
#endif

int srchCodeAdr(const char *st){
	int r=0,c;
	do{
		if(flash[r].name){
			if(!strcmp(flash[r].name,st)) return r;
		}
		r++;
	} while(r<0x10000);
	c=sscanf(st,"%x",&r);
	if(c==1) return r;
	return -1;	// Fehler!
}

void actSource(int file,int line){
	int start,end,r,sX,sY;
	char st[PATH_MAX];
	if(sourcefile!=file){
		sourcefile=file;
		r=ui->src_output->buffer()->loadfile(getSrcFileName(file));
		ui->src_win->label(getSrcFileName(file));
		sourceline=-1;
		if(r){
			fl_alert("%s konnte nicht geöffnet werden!",getSrcFileName(file));
			return;
		}
	}
	ui->src_output->buffer()->unselect();
	start=ui->src_output->buffer()->skip_lines(0,line-1);
	end=ui->src_output->buffer()->line_end(start);
	ui->src_output->buffer()->select(start,end);
	/* sicherstellen, dass die Zeile sichtbar ist */
	r=ui->src_output->position_to_xy(start,&sX,&sY);
	if((sY>ui->src_output->h()-30)||(!r)){
		ui->src_output->scroll(line-1,0);
	}
	sourceline=line;
}

void refreshTitle(){
	char st[2*PATH_MAX];
	sprintf(st,"%s (%s) - AVR Simulator",fileshortname,devicename);
	ui->main_win->label(st);
}

void refresh(){
	int op=0,op_old,l=0,c;
	char st[1000],st1[100],brk;

	ui->src_output->buffer()->unselect();
	op=pc;
	ui->disass_output->buffer()->text("");
	ui->input_registers->buffer()->text("");
	while(l<60){
		if(flash[op].name){
			sprintf(st,"%s:\n",flash[op].name);
			ui->disass_output->buffer()->append(st);
			l++;
		}
		op_old=op;
		disass(&op,st);
		if(op_old==brkp) brk='>'; else brk=' ';
		sprintf(st1,"%c%04X:",brk,op_old);
		c=0;
		while(op_old<op){
			sprintf(st1+strlen(st1)," %04X",flash[op_old].opcode);
			op_old++;
			c++;
		}
		while(c<3){
			strcat(st1,"     ");
			c++;
		}
		sprintf(st1+strlen(st1),"%s\n",st);
		ui->disass_output->buffer()->append(st1);
		l++;
	}
	if(!ports[PO_SREG]) ui->input_registers->buffer()->append("Position of SREG is not known!\nPlease select definitions-file!\n");
	l=ram[ports[PO_SREG]];
	strcpy(st,"");
	for(c=0;c<8;c++){
		sprintf(st+strlen(st),"%c=%d ",flags[c],(l>>7)&1);
		l<<=1;
	}
	l=ram[ports[PO_SPL]];
	if(ram[ports[PO_SPH]]) l+=(ram[ports[PO_SPH]])<<8;
	sprintf(st+strlen(st),"\nSP=%04x X=%04x Y=%04x Z=%04x",l,getPtr(rrX),getPtr(rrY),getPtr(rrZ));
	ui->input_registers->buffer()->append(st);
	for(c=0;c<32;c++){
		sprintf(st,"%02X",ram[c]);
		registers[c].input->value(st);
	}

	ram_win->redraw();
	refreshTree(tree->root());
	tree_win->redraw();

	if(flash[pc].flags&PM_SOURCE){
		actSource(flash[pc].src_file,flash[pc].src_line);
	}
}

void actCyc(){
	char st[100];
	double d=0;
	sprintf(st,"%04X",brkp);
	ui->input_brkpt->value(st);
	sprintf(st,"%04X",pc);
	ui->input_pc->value(st);
	sprintf(st,"%d",clockcycles);
	ui->input_cycles->value(st);
	if(!f_cpu){
		ui->output_runtime->value("CPU Speed not defined");
	}
	if(clockcycles/f_cpu){
		d=(double)clockcycles/(double)f_cpu;
		sprintf(st,"%.2lf s",d);
		ui->output_runtime->value(st);
		return;
	}
	if(clockcycles*1000/f_cpu){
		d=(double)clockcycles/(double)f_cpu*1000;
		sprintf(st,"%.3lf ms",d);
		ui->output_runtime->value(st);
		return;
	}
	if(clockcycles*1000000/f_cpu){
		d=(double)clockcycles/(double)f_cpu*1000000;
		sprintf(st,"%.2lf µs",d);
		ui->output_runtime->value(st);
		return;
	}
	d=(double)clockcycles/(double)f_cpu*1000000000;
	sprintf(st,"%.1lf ns",d);
	ui->output_runtime->value(st);
}

void brkTest()
{
	if(pc==brkp){
		char st[100];
		run=0;
		sprintf(st,"Breakpoint 0x%04x reached",brkp);
		ui->statusline->value(st);
	}
}

void Reset(){
	run=0;
	pc=0;
	clockcycles=0;
	readCodeFile(fn_hexFile);
	actCyc();
	refresh();
}

void Stop(void){
	run=0;
	ui->statusline->value("Stopped");
}

void Step(){
	sim();
	actCyc();
	refresh();
}

void Run(){
	int c=0;
	run=1;
	ui->statusline->value("Running...");
	while(run){
		c++;
		if(c>10000){
			Fl::check();
			c=0;
		}
		sim();
		brkTest();
	}
	actCyc();
	refresh();
}

void StepOver(){
	int c=0,oldlevel=level;
	run=1;
	do{
		c++;
		if(c>10000){
			Fl::check();
			c=0;
		}
		sim();
		brkTest();
	} while((run)&&(oldlevel!=level));
	actCyc();
	refresh();
}

void StepOut(){
	int c=0,oldlevel=level;
	run=1;
	do{
		c++;
		if(c>10000){
			Fl::check();
			c=0;
		}
		sim();
		brkTest();
	} while((run)&&(oldlevel==level));
	actCyc();
	refresh();
}

void Autostep(){
	run=1;
	ui->statusline->value("Stepping...");
	while(run){
		Fl::check();
		sim();
		actCyc();
		refresh();
		brkTest();
	}
}

void GotoMain(){
	int c=0,b=0;
	run=1;
	b=srchCodeAdr("main");
	if(b<0){
		ui->statusline->value("main function not found");
		return;
	}
	ui->statusline->value("Running...");
	while(run){
		c++;
		if(c>10000){
			Fl::check();
			c=0;
		}
		sim();
		if(pc==b){
			run=0;
			ui->statusline->value("main reached");
		}
	}
	actCyc();
	refresh();
}


#include "ui.cxx"
#include "tree.cpp"
#include "config.c"

int myHandler(int ev){
	int k,s;
	switch(ev){
		case FL_SHORTCUT:
		s=Fl::event_state();	/* shift keys */
		k=Fl::event_key();		/* key code */
		switch(k){
			case 0xffc2:	/* F5 */
			if(s&FL_SHIFT){
				Reset();
				return true;
			}
			if(s&FL_CTRL){
				Stop();
				return true;
			}
			if(s&FL_ALT){
				Autostep();
				return true;
			}
			Run();
			return true;
			case 0xffc3:	/* F6 */
			return true;
			case 0xffc4:	/* F7 */
			return true;
			case 0xffc5:	/* F8 */
			return true;
			case 0xffc6:	/* F9 */
			return true;
			case 0xffc7:	/* F10 */
			StepOver();
			return true;
			case 0xffc8:	/* F11 */
			if(s&FL_SHIFT){
				StepOut();
				return true;
			}
			Step();
			return true;
			case 0xffc9:	/* F12 */
			return true;
			default:
				//printf("Shortcut %x (%x)\n",k,s);
			break;
		}
		break;
	}
	return false;
}

MyTable *cr_tab(int x, int y, int w, int h, Fl_Double_Window **win, int kind,int col_width,const char *name){
	MyTable *r;
	*win=new Fl_Double_Window(x,y,w,h,name);
	r=new MyTable(0,0,w,h,kind,col_width);
	(*win)->end();
	(*win)->resizable(r);
	(*win)->show();
	(*win)->hide();
	return r;
}

void regCallback(Fl_Widget *wi){
	reg_rec *rp;
	int i;
	rp=(reg_rec*) wi->user_data();
	sscanf(rp->input->value(),"%x",&i);
	ram[rp->num]=i;
	//fl_alert("Edit Callback %d",rp->num);
	refresh();
}

int main(int argc, char **argv){
	FL_NORMAL_SIZE = 11;
	ui=new UserInterface();
	int i,r,c;
	long long int li;
	char st[100];
	cleanFlash();
	cleanEeprom();
	for(i=0;i<256;i++) sprintf(io_names[i],"0x%02x",i);
	for(c=0;c<4;c++)
	for(r=0;r<8;r++){
		i=8*c+r;
		registers[i].num=i;
		sprintf(registers[i].name,"r%02d",i);
		registers[i].input=new Fl_Input(30+70*c,120+20*r,30,20,registers[i].name);
		registers[i].input->callback(regCallback);
		registers[i].input->user_data((void*)&registers[i]);
		ui->main_win->add(registers[i].input);
	}
	if(argc>1) readCodeFile(argv[1]);
	ui->disass_output->buffer(new Fl_Text_Buffer());
	ui->terminal_output->buffer(new Fl_Text_Buffer());
	ui->disass_output->buffer(new Fl_Text_Buffer());
	ui->input_registers->buffer(new Fl_Text_Buffer());
	ui->src_output->buffer(new Fl_Text_Buffer());
	Fl::add_handler(myHandler);
	ui->btn_end->tooltip("Exit");
	ui->btn_run->tooltip("Run (F5)");
	ui->btn_brk->tooltip("Break(Ctrl-F5)");
	ui->btn_reset->tooltip("Reset (Shift-F5)");
	ui->btn_step->tooltip("Step Into (F11)");
	ui->btn_step_over->tooltip("Step Over (F10)");
	ui->btn_step_out->tooltip("Step Out (Shift-F11)");
	ui->btn_autostep->tooltip("Auto Step (Alt-F5)");
	ui->btn_gotomain->tooltip("Goto main()");

	ram_tab=cr_tab(20,20,380,400,&ram_win,DT_RAM,20,"RAM");
	eeprom_tab=cr_tab(40,40,380,400,&eeprom_win,DT_EEPROM,20,"EEPROM");
	flash_tab=cr_tab(60,60,640,400,&flash_win,DT_FLASH,40,"FLASH");

#ifndef WIN32
	ui->main_win->default_icon(&img_icon);
#endif

	tree_win=new Fl_Double_Window(40,40,500,900,"Input Output");
	tree=new Fl_Tree(0,0,500,900);
	tree->callback(tree_callback);
	tree->root()->labelfont(FL_COURIER);
	tree_win->end();
	tree_win->resizable(tree);
	tree_win->show();
	tree_win->hide();
	readConfig();
	refresh();
	actCyc();
	ui->main_win->show();
	Fl::run();
	return 0;
}
