/* Baseemp.c the framework for an empire client written in C.  This code does
** the most rudimentary functions and is meant as a starting point for people
** who want to write clients for empire in C, especially for those who are 
** porting to a non-unix environment.
*/

#include "baseemp.h"

int main(int argc, char** argv) {
  int s;

  if (argc==3) {
    s=open_session(argv[1], argv[2]);
  } else {
    fprintf(stderr, "baseemp host port\n");
    exit(1);
  }

  session_loop(s);
  close_session(s);
  return 1;
}

int open_session(const char* host, const char* sport) {
  unsigned short port;
  int s;
  struct hostent *hostnm;
  struct sockaddr_in server;

  hostnm=gethostbyname(host);		/* Look up the ip for the host */
  port=(unsigned short) atoi(sport);	/* convert the port to an int */
  if (hostnm==NULL) {
    fprintf(stderr, "Gethostbyname failed.\n");
    exit(-1);
  }

  server.sin_family=AF_INET;		/* Set the family of the address */
  server.sin_port=htons(port);		/* Convert the port to network order */
					/* copy the address */
  server.sin_addr.s_addr=*((unsigned long *) hostnm->h_addr);

					/* Get a stream socket */
  if ((s=socket(AF_INET, SOCK_STREAM,0))<0) {
    perror("socket:");
    exit(-1);
  }

					/* connect to the server */
  if (connect(s, &server, sizeof(server))<0) {
    perror("Connect()");
    exit(-1);
  }
printf ("Connected!\n");
  return s;				/* return the socket */
}

void session_loop(int s) {
  fd_set readers;
  struct ioqueue* tmp;


  while(1) {
    if (!waiting && count>0) {		/* If we aren't waiting on the */
					/* server and we have commands */
					/* waiting in queue process them */
      procio(s);
    }
    FD_ZERO(&readers);			/* Clear the mask */
    FD_SET(0, &readers);		/* Set the keyboard flag */
    FD_SET(s, &readers);		/* Set the socket flag */
    if (waiting || count==0) {		/* if we are waiting and we */
					/* have no commands */
					/* wait until a flaged descriptor is 
					   activated */
      if (select(s+1, &readers, NULL, NULL, 0)<0) {
	  perror("Select:");
	  exit(1);
      }
      if (FD_ISSET(s, &readers)) 	/* If we have socket activity */
        server_io(s);
      if (FD_ISSET(0, &readers)) 	/* If we have keyboard activity */
        client_io(s);
    }
  }
}

/* Get a line from the keyboard and put it on the queue */
/* I have set a limit of 1024 characters per input line, I can't think of any */
/* time that more would be needed, but if you start losing the end of your */
/* input, that could be it. */
void client_io() {
  char buf[1024];

  if (gets(buf)) { push(buf); }
}

/* Process all commands on the queue as we get the oppurtunity */
void procio(int s) {
  FILE *INPUT;
  char buf[1024], *data;
  int a;

  data=buf;
  pull(data); 				/* Get a command from the queue */
  strcat(data, "\n");			/* Put a linefeed on the end of it */
					/* If we are in a tele/anno send the 
					   data with no translation */
  if (CODE==C_FLUSH && sscanf(PROMPT, "%d left", &a)) {
    send(s, data, strlen(data), 0);
  } 
					/* lines beginning with a : are
				 	   sent to the OS */
  else if (*data==':') { system(++data); }
  else if (*data=='<') {		/* lines beginning with a < are
					   for redirecting input */
    INPUT=fopen(++data, "r");		/* open the input file */
    while(gets(buf)) push(buf);		/* get a line and push it on the queue */
    fclose(INPUT);			/* close the file when were done */
  }
  else if (*data=='&') {		/* lines beginning with a & are for
					   sub prompts only */
    if (CODE==C_FLUSH) {
      send(s, ++data, strlen(data)-1, 0);
      waiting=1;			/* set wait for output flag */
    }
  }

/******************************************************************************/
/* If you are adding in more commands, such as alias or redirecting to a pipe */
/* then they would go here						      */
/******************************************************************************/

  else {				/* If we got this far it must be 
					   regular input so send it as is */
    waiting=1;				/* set wait for output flag */
    send(s, data, strlen(data), 0);
  }
}

/*
 * Read a line of output from the server and process it.
 */

void server_io(int s) {
  FILE* OUTPUT;
  char line[1024], buf[1024];
  int timeon, btus;

      getline(s, line);			/* get one line for the server */
      pullword(line, &CODE, line);	/* pull off the output code */
      switch(CODE) {			/* switch on code */
        case C_DATA:			/* if regular old command data */
	  printf("%s",line);
	  break;
	case C_FLUSH:			/* If a sub prompt */
          waiting=0;
	  sprintf(PROMPT, "%s",line);
          printf("%s",PROMPT);
	  break;
	case C_PROMPT:			/* If a command prompt */
          waiting=0;			/* reset the wait for output flag */
	  sscanf(line,"%d %d", &timeon, &btus);
	  sprintf(PROMPT,"[%s: %d %d] ", "empire", timeon, btus);
	  printf("%s",PROMPT); 
	  break;
	case C_REDIR:			/* If redirecting to a file */
	case C_PIPE:			/* If redirecting to a pipe */
	case C_CMDOK:			/* Successful 'pre-play' command */
	case C_INIT:			/* connect and successful login */
	  waiting=0;
	  break;
	case C_EXIT:			/* The server is closing the session */
	  printf("%s",line);		/* (nicely) */
	  close_session(s);
	  break;
	case C_CMDERR:			/* error logging in */
	case C_BADCMD:			/* error logging in */
	  printf("%s", line);
	  send(s,"quit", strlen("quit"), 0);
	  break;
	case C_ABORT:			/* hell if I know */
	case C_EXECUTE:			/* I never use execute I just use < */
	  printf("%s",line);		/* but if you want it it goes here */
          break;
	default:
	  printf("Unrecognized output from server\n");
	  close_session(s);
          break;
      }
      fflush(stdout);			/* flush the output (for the prompt) */
}

/*
**Read in a line of output from the descriptor and return it. 
*/

int getline(int s, char *buf) {
  int i=0;
  char c=0;

  buf[0]='\0';
  if (recv(s, &c, 1, 0)==0) {
    printf("Server failure!\n");
    close_session(s);
  }
  while(c!='\n') {		/* Read from descriptor until newline */
    buf[i++]=c;
    recv(s, &c, 1, 0);
  }
  buf[i++]=c;
  buf[i++]='\0';
  return 1;
}

/*
 * Close the socket and exit.
 */

void close_session(int s) {
  close(s);
  exit(1);
}


/* Push a line onto the top of the queue */
void push(char *data) {
  struct ioelem *new;

  count++;
  new=malloc(sizeof(struct ioelem));
  strcpy(new->data, data);
  if (iohead) {
    new->next=iohead;
    new->prev=NULL;
    iohead=new;
  } else {
    iohead=new; iotail=new;
    iohead->next=NULL; iohead->prev=NULL;
  }
}

/* Pull a line fro mthe bottom of the queue */
void pull(char *data) {
  struct ioelem *tmp;  
  
  if (iotail) {
    count--;
    strcpy(data,iotail->data);
    tmp=iotail;
    if (iotail->prev) {
      iotail->prev->next=NULL;
    } else {
      iohead=NULL;
    }
    iotail=tmp->prev;
    free(tmp);
  }
}

/* seperate a string into the first word and everything else */
void pullword(char *buf, char *word1, char *word2) {
  int i,j;
  char *tmp;

  for(j=0; isspace(buf[j]); j++);	/* strip leading whitespace */
  for(i=0; !isspace(buf[j]); i++, j++) word1[i]=buf[j];
  word1[i++]='\0';
  for(i=0, j++; buf[j]!='\0'; j++, i++) word2[i]=buf[j];
  word2[i++]='\0';
}
