#include <stream.h>
#include <strstream.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <xmObject.h>

#define LINES_EDITED 8
#define COLMS_EDITED 8
#define BYTES_EDITED (LINES_EDITED * COLMS_EDITED)

class HexWindow : public XmWindow
{
	char* fn;
	int fd;
	long curOffs;
	int curSize;

	void initialize();

	void cmd(char*);
	void about(void*);
	void quit(void*);
public:
	HexWindow() : XmWindow("Hex Edit")	{ fn = NULL; fd = 0; curOffs = 0L; }

	void gnuInit()	{ setInitWindowSize(0, 0, 500, 400); HexWindow::initialize(); }
};


void XmApp::initialize()
{
	(new HexWindow)->gnuInit();
};

char nmBuf[100];

inline char* objName(char* obj, int n)
{
	ostrstream lnn(nmBuf, 100);
	lnn << obj << n;
	lnn.put('\0');
	return(nmBuf);
}

char hxBuf[20];

inline char* cToHex(char c)
{
	sprintf(hxBuf, "%02X", c & 0x00000000FF);
	return(hxBuf);
}

void HexWindow::initialize()
{
	if(App->getCmdArg(1))
		fn = App->getCmdArg(1);
	if(!fn || !(fd = ::open(fn, O_RDWR)))
	{	cout << App->getCmdArg(0) << ": error opening file \"" << fn << "\"\n";
		quit(NULL);
	}

	XmDropdownMenu* m = createDropdownMenu();

	m->addLabels("&File", "&Misc", NULL);
	m->setCurrentLabel("File");
	m->addItems(Entry("&Open...", CB(HexWindow::cmd)),
				Entry("&Close", CB(HexWindow::cmd)), NULLENTRY);
	m->addSeparator();
	m->addItem(Entry("E&xit", CB(HexWindow::quit)));
	m->setCurrentLabel("Misc");
	m->addItem(Entry("&About...", CB(HexWindow::about)));

	XmGroupBox* grp;
	addSubpane(grp = new XmGroupBox("grp", 0, 0, 200, 200));
	grp->add((new XmStaticText("txtFileName", 15, 22, 180, 18))->setText(fn));
	int all = 0;
	for(int i = 0; i < LINES_EDITED; i++)
	{	grp->add((new XmStaticText(objName("txt", i), 15, 56 + (36 * i), 97, 18, 0x0 | XmSleft))->setText("0x00000000"));
		for(int j = 0; j < COLMS_EDITED; j++)
			grp->add((new XmEdit(objName("edt", all++), 119 + (37 * j), 47 + (36 * i), 36, 36, 0x0)));
	}

	XmToolBar* tb;

	tb = addToolbar(XmSleft);
	tb->add((new XmPushButton("&Next")), this, CB(HexWindow::cmd), CB_OBJ_NAME);
	tb->add((new XmPushButton("&Prev")), this, CB(HexWindow::cmd), CB_OBJ_NAME);
	tb->add((new XmPushButton("&Write")), this, CB(HexWindow::cmd), CB_OBJ_NAME);
	cmd("Prev");
}

void HexWindow::cmd(char* n)
{
	char curBuf[BYTES_EDITED];
	int i;

	switch(*n)
	{	case 'N':
		case 'P':
		curOffs += (*n == 'N' ? BYTES_EDITED : -BYTES_EDITED);
		curOffs = (curOffs > 0 ? curOffs : 0);
		for(i = 0; i < LINES_EDITED; i++)
			staticText(objName("txt", i))->setText(form("%l08X", curOffs + (i * COLMS_EDITED)));
		lseek(fd, curOffs, 0);
		curSize = read(fd, curBuf, BYTES_EDITED);
		for(i = 0; i < BYTES_EDITED; i++)
			edit(objName("edt", i))->setText(i < curSize ? cToHex(curBuf[i]) : "--");
		if(curOffs)
			toolbar(XmSleft)->pushButton("Prev")->enable();
		else
			toolbar(XmSleft)->pushButton("Prev")->disable();
		if(curSize < BYTES_EDITED)
			toolbar(XmSleft)->pushButton("Next")->disable();
		else
			toolbar(XmSleft)->pushButton("Next")->enable();
		break;
		case 'W':
		if((new XmMsgBox("Are you sure?", "Write", XmSmboxQuestion))->showMsg())
		{	lseek(fd, curOffs, 0);
			char* valstr;
			for(i = 0; i < curSize; i++)
			{	valstr = edit(objName("edt", i))->getText();
				int val;
				sscanf(valstr, "%X", &val);	// &curBuf[i]);
				curBuf[i] = val;
			}
			write(fd, curBuf, curSize);
		}
		break;
		default:
		(new XmMsgBox("Sorry, not implemented.", n, XmSmboxInfo))->showMsg();
	}
}

void HexWindow::about(void*)
{
}

void HexWindow::quit(void*)
{
	::close(fd);
	exit(0);
}




