#include <stdio.h>
#include <rpc/rpc.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netdb.h>

#include "stdfm_rpc.h"
#include "fm_commands.h"

#define Success 0
#define Failure -1

static struct timeval fiveSeconds = { 5, 0 };
static struct timeval halfMinute = { 30, 0 };
static FmRpcNiceErrorT nice;
static char *emptyString = ""; 

static char *copyString(s)
char *s;
{
char *new;

new = (char *)malloc(strlen(s) + 1);
if (! new)
	return (NULL);
strcpy(new, s);
return (new);
}

static freeString(sp)
char **sp;
{
if (*sp)
	free(*sp);
*sp = NULL;
}

static bool_t localCall(proc, inproc, in, outproc, out, total_timeout)
struct timeval total_timeout;
char *in, *out;
xdrproc_t inproc, outproc;
{
struct hostent *hp;
int addrlen, sock = RPC_ANYSOCK;
CLIENT *client;
enum clnt_stat clnt_stat;
struct sockaddr_in server_addr;

hp = gethostbyname("localhost");
if (! hp)
	return (FALSE);

addrlen = sizeof(struct sockaddr_in);
bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr, hp->h_length);
server_addr.sin_family = AF_INET;
server_addr.sin_port = 0;

client = clntudp_create(&server_addr, FM_RPC_PROG, FM_RPC_VERS, total_timeout, &sock); 
if (! client)
	return (FALSE);
	
clnt_stat = clnt_call(client, proc, inproc, in, outproc, out, total_timeout);
if (clnt_stat != RPC_SUCCESS) {
	clnt_perror(client, "FmClient");
	return (FALSE);
}

close(sock);
clnt_destroy(client);
return (TRUE);
}

static freeReturn(returnp, freer)
char *returnp;
xdrproc_t freer; 
{
XDR anXdrStream;

xdrmem_create(&anXdrStream, returnp, 8 * 1024, XDR_FREE);
freer(&anXdrStream, returnp);
}

/*
General commands.
*/

static void doFmCommands(doc, codes, num) 
unsigned int doc;
int *codes;
unsigned int num;
{
FmRpcCommandsArgT arg;

arg.doc = doc; 
arg.commands.FmRpcCommandsT_len = num; 
arg.commands.FmRpcCommandsT_val = codes; 
bzero(&nice, sizeof(nice));
if (localCall(FM_RPC_COMMANDS, xdr_FmRpcCommandsArgT, &arg, xdr_FmRpcNiceErrorT, &nice, halfMinute)) {
	if (nice.code != Success)
		printf("Cannot execute because %s (%d).\n", nice.message, nice.code);
	freeReturn(&nice, xdr_FmRpcNiceErrorT);
}
}

/*
Special commands.
*/

static int newDoc(template)
char *template;
{ 
FmRpcNewDocArgT arg;
FmRpcNewDocReturnT newDocReturn;
int handle;

arg.template = template;
bzero(&newDocReturn, sizeof(newDocReturn));
if (localCall(FM_RPC_NEW_DOC, xdr_FmRpcNewDocArgT, &arg, xdr_FmRpcNewDocReturnT, &newDocReturn, halfMinute))
	if (newDocReturn.nice.code == Success) {
		printf("Doc's handle is %d.\n", handle = newDocReturn.doc);
		freeReturn(&newDocReturn, xdr_FmRpcNewDocReturnT);
		return (handle);
	}
	else {
		printf("Can't new %s because %s (%d).\n", arg.template, newDocReturn.nice.message, newDocReturn.nice.code);
		freeReturn(&newDocReturn, xdr_FmRpcNewDocReturnT);
		return (-1);
	}
else return (-1);
}

static int openDoc(doc)
char *doc;
{ 
FmRpcOpenDocArgT arg;
FmRpcOpenDocReturnT openDocReturn;
int handle;

arg.doc = doc;
bzero(&openDocReturn, sizeof(openDocReturn));
if (localCall(FM_RPC_OPEN_DOC, xdr_FmRpcOpenDocArgT, &arg, xdr_FmRpcOpenDocReturnT, &openDocReturn, halfMinute))
	if (openDocReturn.nice.code == Success) {
		printf("Doc's handle is %d.\n", handle = openDocReturn.doc);
		freeReturn(&openDocReturn, xdr_FmRpcOpenDocReturnT);
		return (handle);
	}
	else {
		printf("Can't open %s because %s (%d).\n", arg.doc, openDocReturn.nice.message, openDocReturn.nice.code);
		freeReturn(&openDocReturn, xdr_FmRpcOpenDocReturnT);
		return (-1);
	}
else return (-1);
}

static void import(isCurrentDoc, doc, file) 
int isCurrentDoc;
unsigned int doc;
char *file;
{
FmRpcImportArgT arg;

arg.doc = doc; 
arg.file = file; 
arg.isCurrentDoc = isCurrentDoc;
bzero(&nice, sizeof(nice));
if (localCall(FM_RPC_IMPORT, xdr_FmRpcImportArgT, &arg, xdr_FmRpcNiceErrorT, &nice, halfMinute)) {
	if (nice.code != Success)
		printf("Cannot import %s because %s (%d).\n", file, nice.message, nice.code);
	freeReturn(&nice, xdr_FmRpcNiceErrorT);
}
}

static int isMakeBackup = 0;
static int mode = 0640;

static void saveDoc(doc, file) 
unsigned int doc;
char *file;
{
FmRpcSaveDocArgT arg;

arg.doc = doc; 
arg.file = file; 
arg.mode = mode;
arg.isMakeBackup = isMakeBackup;
bzero(&nice, sizeof(nice));
if (localCall(FM_RPC_SAVE_DOC, xdr_FmRpcSaveDocArgT, &arg, xdr_FmRpcNiceErrorT, &nice, halfMinute)) {
	if (nice.code != Success)
		printf("Cannot save to %s because %s (%d).\n", file, nice.message, nice.code);
	freeReturn(&nice, xdr_FmRpcNiceErrorT);
}
}

static void saveText(doc, file) 
unsigned int doc;
char *file;
{
FmRpcSaveTextArgT arg;

arg.doc = doc; 
arg.file = file; 
arg.mode = mode;
arg.isMakeBackup = isMakeBackup;
bzero(&nice, sizeof(nice));
if (localCall(FM_RPC_SAVE_TEXT, xdr_FmRpcSaveTextArgT, &arg, xdr_FmRpcNiceErrorT, &nice, halfMinute)) {
	if (nice.code != Success)
		printf("Cannot save text to %s because %s (%d).\n", file, nice.message, nice.code);
	freeReturn(&nice, xdr_FmRpcNiceErrorT);
}
}

static FmRpcSaveMifArgT aMifArg;

#define ASK_MIF_OPTION(prompt,option)\
{\
char ack[81];\
printf(prompt);\
scanf("%s", ack);\
switch (ack[0]) {\
case 'y':\
	aMifArg.saveOptions |= option; break;\
case 'n':\
	break;\
default:\
	return;\
}\
}

static void askMifOptions()
{
bzero(&aMifArg, sizeof(aMifArg));

ASK_MIF_OPTION("Save text?",MIF_SAVE_TEXT);
ASK_MIF_OPTION("Save tags?",MIF_SAVE_TAGS);
ASK_MIF_OPTION("Save formats?",MIF_SAVE_FMTS);
ASK_MIF_OPTION("Save font information?",MIF_SAVE_FONTS);
ASK_MIF_OPTION("Save markers?",MIF_SAVE_MKRS);
ASK_MIF_OPTION("Save anchored frames?",MIF_SAVE_AFMS);
ASK_MIF_OPTION("Save layout?",MIF_SAVE_LAYT);
ASK_MIF_OPTION("Save master pages?",MIF_SAVE_MPAGE);
ASK_MIF_OPTION("Save font catalog?",MIF_SAVE_FCAT);
ASK_MIF_OPTION("Save paragraph catalog?",MIF_SAVE_PCAT);
ASK_MIF_OPTION("Save template?",MIF_SAVE_TMPLT);
ASK_MIF_OPTION("Save dictionary?",MIF_SAVE_DICT);
ASK_MIF_OPTION("Save variables?",MIF_SAVE_VARS);
}

static void saveMif(doc, file) 
unsigned int doc;
char *file;
{
aMifArg.doc = doc; 
aMifArg.file = file; 
aMifArg.mode = mode;
aMifArg.isMakeBackup = isMakeBackup;
bzero(&nice, sizeof(nice));
if (localCall(FM_RPC_SAVE_MIF, xdr_FmRpcSaveMifArgT, &aMifArg, xdr_FmRpcNiceErrorT, &nice, halfMinute)) {
	if (nice.code != Success)
		printf("Cannot save MIF to %s because %s (%d).\n", file, nice.message, nice.code);
	freeReturn(&nice, xdr_FmRpcNiceErrorT);
}
}

/*
Hypertext commands.
*/

static void jump(doc, isNewWindow, dstDoc, tag)
unsigned int doc;
int isNewWindow;
char *dstDoc, *tag;
{
FmRpcGoToLinkArgT arg;

arg.doc = doc; 
arg.isNewWindow = isNewWindow; 
arg.dstDoc = dstDoc; 
arg.tag = tag; 
bzero(&nice, sizeof(nice));
if (localCall(FM_RPC_GO_TO_LINK, xdr_FmRpcGoToLinkArgT, &arg, xdr_FmRpcNiceErrorT, &nice, halfMinute)) {
	if (nice.code != Success)
		printf("Cannot go to link %s in %s because %s (%d).\n", tag, dstDoc, nice.message, nice.code);
	freeReturn(&nice, xdr_FmRpcNiceErrorT);
}
}

static void ls(doc, file) 
unsigned int doc;
char *file;
{
FmRpcListExternalsArgT arg;

arg.doc = doc; 
arg.file = file; 
bzero(&nice, sizeof(nice));
if (localCall(FM_RPC_LIST_EXTERNALS, xdr_FmRpcListExternalsArgT, &arg, xdr_FmRpcNiceErrorT, &nice, halfMinute)) {
	if (nice.code != Success)
		printf("Cannot list external insets in %s because %s (%d).\n", file, nice.message, nice.code);
	freeReturn(&nice, xdr_FmRpcNiceErrorT);
}
}

/*
Calls I implement.
*/

FmRpcNiceErrorT *fm_rpc_launch_1(argp)
void *argp;
{
printf("Launched.\n");
nice.code = Success;
nice.message = emptyString;
return (&nice);
}

FmRpcNiceErrorT *fm_rpc_edit_inset_1(argp)
FmRpcEditInsetArgT *argp;
{
if (argp->isIntern)
	printf("Edit internal inset filed in %s.\n", argp->insetFile);
else printf("Edit external inset filed in %s.\n", argp->insetFile);
nice.code = Success;
nice.message = emptyString;
return (&nice);
}

FmRpcNiceErrorT *fm_rpc_update_inset_1(argp)
FmRpcUpdateInsetArgT *argp;
{
printf("Update inset filed in %s.\n", argp->insetFile);
nice.code = Success;
nice.message = emptyString;
return (&nice);
}

static struct {
	char *doc, *message;
}
myMessage;

FmRpcNiceErrorT *fm_rpc_message_1(argp)
FmRpcMessageArgT *argp;
{
myMessage.doc = copyString(argp->doc);
myMessage.message = copyString(argp->message);
nice.code = Success;
nice.message = emptyString;
return (&nice);
}

/*
Dumb terminal user interface.
*/

static menu()
{
printf("N	NEW and make it LastOpenedDoc\n");
printf("O	OPEN and make it LastOpenedDoc\n");
printf("M	MIF save LastOpenedDoc\n");
printf("m	MIF save options\n");
printf("T	TEXT save LastOpenedDoc\n");
printf("D	DOC save LastOpenedDoc\n");
printf("S	SAVE options\n");
printf("Q	QUIT LastOpenedDoc\n");
printf("\n");
printf("i	IMPORT file into LastOpenedDoc\n");
printf("I	IMPORT file into current doc\n");
printf("l	LIST externals in LastOpenedDoc\n");
printf("\n");
printf("e	EXECUTE function codes on LastOpenedDoc\n");
printf("f	FIRST page of LastOpenedDoc\n");
printf("n	NEXT page of LastOpenedDoc\n");
printf("p	PREVIOUS page of LastOpenedDoc\n");
printf("\n");
printf("In 2.0, marker type I (8) is hypertext.\n");
printf("j	JUMP in LastOpenedDoc\n");
printf("J	JUMP in LastOpenedDoc & open new window\n");
printf("\n");
printf("?	MENU\n");
printf("q!	QUIT this program\n");
}

static void parseOneCommand()
{ 
static int lastOpenedDocHandle = -1; 
char command[81];
int codes[150];

scanf("%s", command);

if (strcmp(command, "q!") == 0)
	exit(0);

switch (command[0]) {
case 'N':
	printf("Template document name? ");
	scanf("%s", command);
	lastOpenedDocHandle = newDoc(command); return;
case 'O':
	printf("Document name? ");
	scanf("%s", command);
	lastOpenedDocHandle = openDoc(command); return;

case 'I':
	printf("File name? ");
	scanf("%s", command);
	import(1, NULL, command); return;

case 'S':
	printf("Make backup? ");
	scanf("%s", command);
	isMakeBackup = (command[0] == 'y');
	printf("Octal mode? ");
	scanf("%o", &mode); return;
case 'm':
	askMifOptions(); return;

case '?':
	menu(); return;
default:
	if (lastOpenedDocHandle == -1) {
		printf("No LastOpenedDoc.\n");
		return;
	}
}

switch (command[0]) {
case 'i':
	printf("File name? ");
	scanf("%s", command);
	import(0, lastOpenedDocHandle, command); return;

case 'l':
	printf("File name? ");
	scanf("%s", command);
	ls(lastOpenedDocHandle, command); return;

case 'D':
	printf("File name? ");
	scanf("%s", command);
	saveDoc(lastOpenedDocHandle, command); return;
case 'M':
	printf("File name? ");
	scanf("%s", command);
	saveMif(lastOpenedDocHandle, command); return;
case 'T':
	printf("File name? ");
	scanf("%s", command);
	saveText(lastOpenedDocHandle, command); return;

case 'e':
	{
	unsigned int i;
	int code;

#define TAB 0x08 
#define EOL 0x09
	printf("Hex function codes from Appendix A?  For example, %x %x %x %x %x %x %x %x 0 displays Tools and turns on Grid via Options dialog.\n", 
		KBD_TOOLWIN,
		KBD_OPTIONS, TAB, TAB, TAB, TAB, '1', EOL);
	for (i = 0; i < 150; ++i) {
		if (scanf("%x", &code) != 1 || code == 0)
			break;
		codes[i] = code;
	}
	doFmCommands(lastOpenedDocHandle, codes, i); return;
	}
case 'f':
	codes[0] = KBD_FIRSTPAGE;
	doFmCommands(lastOpenedDocHandle, codes, 1); return;
case 'n':
	codes[0] = KBD_NEXTPAGE;
	doFmCommands(lastOpenedDocHandle, codes, 1); return;
case 'p':
	codes[0] = KBD_PREVPAGE;
	doFmCommands(lastOpenedDocHandle, codes, 1); return;
case 'Q':
	codes[0] = KBD_QUITWIN;
	doFmCommands(lastOpenedDocHandle, codes, 1); 
	lastOpenedDocHandle = -1; return;

case 'j':
case 'J':
	{
	char dstDoc[81];
	char tag[81];

	printf("Tag? ");
	scanf("%s", tag);
	printf("Doc file name (self means lastOpenedDoc)? ");
	scanf("%s", dstDoc);
	if (strcmp(dstDoc, "self") == 0)
		dstDoc[0] = 0;
	jump(lastOpenedDocHandle, command[0] == 'J', dstDoc, tag); return;
	}

default:
	printf("Unknown command.\n");
}
}

main(argc, argv)
int argc;
char *argv[];
{
InitServer();

printf("A FrameMaker Client/InsetEditor\n"); 
menu();

for (;;) {
	int imask;

	imask = (svc_fds | (1 << fileno(stdin))); 
	if (select(32, &imask, NULL, NULL, NULL) == -1)
		continue;
	if (imask & (1 << fileno(stdin)))
		parseOneCommand();
	svc_getreq(imask);
	if (myMessage.doc) {
		printf("Hypertext message from %s is %s.\n", myMessage.doc, myMessage.message);
		freeString(&myMessage.doc);
		freeString(&myMessage.message);
	}
}
}

/*
Calls I stub.
*/

static FmRpcNiceErrorT failure = { Failure, "" }; 

FmRpcNiceErrorT *fm_rpc_commands_1(argp)
FmRpcCommandsArgT *argp;
{
return (&failure);
}

FmRpcNewDocReturnT *fm_rpc_new_doc_1(argp)
FmRpcNewDocArgT *argp;
{
static FmRpcNewDocReturnT aReturn; 

aReturn.nice.code = Failure;
aReturn.nice.message = emptyString;
return (&aReturn);
}

FmRpcOpenDocReturnT *fm_rpc_open_doc_1(argp)
FmRpcOpenDocArgT *argp;
{
static FmRpcOpenDocReturnT aReturn; 

aReturn.nice.code = Failure;
aReturn.nice.message = emptyString;
return (&aReturn);
}

FmRpcNiceErrorT *fm_rpc_import_1(argp)
FmRpcImportArgT *argp;
{
return (&failure);
}

FmRpcNiceErrorT *fm_rpc_save_doc_1(argp)
FmRpcSaveDocArgT *argp;
{
return (&failure);
}

FmRpcNiceErrorT *fm_rpc_save_text_1(argp)
FmRpcSaveTextArgT *argp;
{
return (&failure);
}

FmRpcNiceErrorT *fm_rpc_save_mif_1(argp)
FmRpcSaveMifArgT *argp;
{
return (&failure);
}

FmRpcNiceErrorT *fm_rpc_list_externals_1(argp)
FmRpcListExternalsArgT *argp;
{
return (&failure);
}

FmRpcNiceErrorT *fm_rpc_go_to_link_1(argp)
FmRpcImportArgT *argp;
{
return (&failure);
}
