/****************************************************************************
* KingFisher Release 2 CLI client: "CLIent"
* Copyright © 1994 by Udo Schuermann
* All rights reserved
* ------------------------------------------------------------------------
* Demonstration of a KingFisher Release 2 message-based interface without the
* use of the kf-api.  In other words, we do everything the "hard" way here.
* ------------------------------------------------------------------------
* This program is available only to registered users of KingFisher.
* PLEASE DO NOT REDISTRIBUTE!
****************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <exec/memory.h>
#include <libraries/dos.h>
#include <proto/exec.h>
#include <proto/dos.h>

#include "kf.h"
#include "kf-err.c"      /* defines KFError[] array */


#define VERSION "1.4"

/* prototypes */
BOOL TellKF( struct KFMsg *myMsg );

#ifndef __MYDATE__
#define __MYDATE__ __AMIGADATE__
#endif

char Version[] = "$VER: CLIent "VERSION" for KingFisher Release 2 "__MYDATE__;



void UpgradeBuffer( struct KFMsg *myMsg ) {
  char *p;

  /* Is the maximum required/requested buffer size (myMsg->BParam) larger than what
   * we have currently allocated (myMsg->BufferSize)? */
  if( myMsg->BParam > myMsg->BufferSize ) {
    /* then try reallocating the memory to which we are pointing... */
    if( p = realloc(myMsg->Buffer,myMsg->BParam) ) {
      printf("NOTE: Upgraded our buffer from %d to %d bytes in size\n",
	     myMsg->BufferSize,myMsg->BParam);
      myMsg->BufferSize = myMsg->BParam;
      myMsg->Buffer = p;
    } else
      printf("Could not upgrade our buffer from %d to %d bytes\n",
	     myMsg->BufferSize,myMsg->BParam);
  }
} /* UpgradeBuffer() */



void main() {

  struct MsgPort *myPort=NULL;
  struct KFMsg *myMsg,*reply;
  BOOL happy = TRUE,connected=FALSE;
  char cmd[80];
  char *p,*q;
  LONG n,i;

  printf("CLIent "VERSION" for KingFisher Release 2\n"
	 "Copyright \© 1994 by Udo Schuermann\n"
	 "All rights reserved\n\n");


  if( FindPort( KFPortName ) ) {

    /*****************************************************************
     * Initialize our message; we use this one to communicate with the
     * KFServer's message port.  If something fails, we'll just set
     * the variable 'happy' to FALSE to avoid running through the main
     * while() loop below.
     *****************************************************************
     */
    if( myMsg = AllocMem( sizeof(struct KFMsg), MEMF_PUBLIC|MEMF_CLEAR ) ) {
      myMsg->AmigaMsg.mn_Node.ln_Type = NT_MESSAGE;
      myMsg->AmigaMsg.mn_Length       = sizeof(struct KFMsg);
      if( myMsg->AmigaMsg.mn_ReplyPort = myPort = CreateMsgPort() ) {
	if( myMsg->Buffer = malloc( 256 ) )
	  myMsg->BufferSize = 256;
	else {
	  printf("Cannot allocate 256 bytes for buffer space!\n");
	  happy = FALSE;
	}
      } else {
	printf("Cannot create a private message port!\n");
	happy = FALSE;
      }
    } else {
      printf("Cannot allocate memory for message interface!\n");
      happy = FALSE;
    } /* if */


    /*****************************************************************
     * Now loop until we're no longer "happy," i.e. made to shut down
     * or the user decided to shut us down.
     *****************************************************************
     */
    while( happy ) {
      /*****************************************************************
       * Prompt the user for input and process it
       *****************************************************************
       */
      printf("\nKF Ready: ");
      fflush(stdout);
      if( p = fgets(cmd,sizeof(cmd),stdin) ) {
	/* convert string "foo bar fun" to "foo\0bar fun" where p points to "bar fun" */
	while( *p > ' ' )
	  p++;
	if( *p )
	  *p++ = '\0';
	while( *p == ' ' )
	  p++;
	/* kill the terminating newline, too */
	q = p;
	while( *q )
	  if( *q == '\n' )
	    *q = '\0';
	  else
	    q++;

	/*****************************************************************
	 * we now have converted a line such as
	 *      get=4289\n\0
	 *      ^cmd
	 * to this:
	 *      get\04289\0
	 *      ^cmd ^p
	 *****************************************************************
	 */


	/*****************************************************************
	 * HELP
	 *****************************************************************
	 */
	if( (stricmp(cmd,"?") == 0) || (stricmp(cmd,"help") == 0) )
	  printf("The CLIent understands the following commands:\n"
		 "  ? or HELP     This display\n"
		 "  HELLO [name]  Announce yourself to the server (optional name)\n"
		 "  BYE           Say good-bye to the server\n"
		 "  STATUS        Get status information\n"
		 "  GET [n]       Get fish n (n is optional fish number)\n"
		 "  POS           Get current position in database\n"
		 "  TEST n        Retrieve n successive fish\n"
		 "  LIST          List all databases available for use\n"
		 "  USE database  Select a new database for use\n"
		 "  BUILD file    Add all fish from given file, unconditionally\n"
		 "  PARSE file    Add fish from given file with prompts for each\n"
		 "  REINDEX       Reindexes the current database\n"
		 "  QUIT          Quit the CLIent\n");
	else

	/*****************************************************************
	 * HELLO
	 *****************************************************************
	 */
	if( stricmp(cmd,"HELLO") == 0 ) {
	  if( !connected ) {
	    myMsg->Command = kfcHELLO;
	    myMsg->BParam = (ULONG)FindTask(NULL);
	    myMsg->RESERVED = NULL;
	    if( myMsg->Buffer ) {
	      if( *p == '\0' )
		p = "CLIent "VERSION" for KingFisher Release 2";
	      printf("We call ourselves \"%s\"\n",p);
	      strncpy(myMsg->Buffer,p,myMsg->BufferSize);
	    }
	    if( TellKF(myMsg) ) {
	      if( myMsg->Error == kfeOK ) {
		printf("You are now connected to the KFServer.\n");
		connected = TRUE;
		UpgradeBuffer( myMsg );
	      }
	    } if( myMsg->Error != kfeREFUSED )
	      connected = TRUE;
	  } else
	    printf("Use BYE before saying HELLO again.\n");
	} else

	/*****************************************************************
	 * BYE
	 *****************************************************************
	 */
	if( stricmp(cmd,"BYE") == 0 ) {
	  myMsg->Command = kfcBYE;
	  if( TellKF(myMsg) ) {
	    if( myMsg->Error == kfeOK ) {
	      printf("KFServer has said good bye.\n"
		     "Use the HELLO command to reconnect to the KFServer.\n");
	      connected = FALSE;
	    }
	  }
	} else

	/*****************************************************************
	 * STATUS
	 *****************************************************************
	 */
	if( stricmp(cmd,"STATUS") == 0 ) {
	  myMsg->Command = kfcSTATUS;
	  if( TellKF(myMsg) ) {
	    if( myMsg->Error == kfeOK )
	      printf(myMsg->Buffer);
	  }
	} else

	/*****************************************************************
	 * GET
	 *****************************************************************
	 */
	if( stricmp(cmd,"GET") == 0 ) {
	  myMsg->BParam = atol( p );    /* if 0 then KFServer gets current fish */
	  myMsg->Command = kfcGETFISH;
	  if( TellKF(myMsg) ) {
	    if( myMsg->Error == kfeOK )
	      printf("Flags = %04X, Name = ",myMsg->FParam);
	      printf(myMsg->Buffer);
	  }
	} else

	/*****************************************************************
	 * POS
	 *****************************************************************
	 */
	if( stricmp(cmd,"POS") == 0 ) {
	  myMsg->Command = kfcGETPOS;
	  if( TellKF(myMsg) )
	    if( myMsg->Error == kfeOK )
	      printf("Current position in database: %d\n",myMsg->BParam);
	} else

	/*****************************************************************
	 * TEST
	 *****************************************************************
	 */
	if( stricmp(cmd,"TEST") == 0 ) {
	  n = atol(p);
	  printf("Retrieving %d successive fish for TEST ...",n);
	  myMsg->Command = kfcGETPOS;
	  if( TellKF(myMsg) )
	    i = myMsg->BParam;
	  else
	    n = 0;
	  while( n > 0 ) {
	    myMsg->BParam = i;
	    myMsg->Command = kfcGETFISH;
	    if( TellKF(myMsg) ) {
	      i++;
	      n--;
	    } else {
	      printf(" (test loop terminated by GETFISH %d error)",i);
	      n = 0;
	    }
	  } /* while */
	  printf(" done!\n");
	} else

	/*****************************************************************
	 * LIST
	 */
	if( stricmp(cmd,"LIST") == 0 ) {
	  myMsg->Command = kfcLISTDBASES;
	  if( TellKF(myMsg) )
	    printf("---Databases Available---\n%s"
		   "-------------------------",myMsg->Buffer);
	} else

	/*****************************************************************
	 * USE database
	 */
	if( stricmp(cmd,"USE") == 0 ) {
	  if( *p ) {
	    myMsg->Command = kfcSELECTDBASE;
	    myMsg->BParam = (ULONG)p;
	    if( TellKF(myMsg) ) {
	      printf("Database \"%s\" is now in use!",p);
	      UpgradeBuffer( myMsg );
	    }
	  } else
	    printf("You should also give the name of a database to use!\n");
	} else

	/*****************************************************************
	 * PARSE file
	 */
	if( stricmp(cmd,"PARSE") == 0 ) {
	  if( *p ) {
	    myMsg->Command = kfcPARSEFILE;
	    myMsg->BParam = (ULONG)p;
	    while( TellKF(myMsg) ) {
	      if( myMsg->Error == kfeOK ) {
		printf("Disk %d, Flags %04d\n"
		       "--------------------NEW FISH--------------------\n%s"
		       "------------------------------------------------\n"
		       "Add this to the database? (y/N/q) ",1,0,myMsg->Buffer);
		fflush(stdout);
		if( p = fgets(cmd,sizeof(cmd),stdin) ) {
		  if( (*p == 'y') || (*p == 'Y') ) {
		    myMsg->Command = kfcADDFISH;
		    myMsg->BParam  = 1;
		    myMsg->FParam  = 0x0000;
		    if( TellKF(myMsg) )
		      printf("Fish has been added as number %d\n\n",myMsg->BParam);
		  } else
		    if( (cmd[0] == 'q') || (cmd[0] == 'Q') )
		      break;
		} else
		  break;
		myMsg->Command = kfcPARSEFILE;  /* prepare for next swing through loop */
	      } else
		break;
	    } /* while */
	    if( myMsg->Error != kfeNOMORE ) {
	      myMsg->Command = kfcSTOPPARSE;
	      if( TellKF(myMsg) )
		printf("Database has been made sharable again.\n");
	    } else
	      printf("Parsing completed.  Database is sharable again.\n");
	  } else
	    printf("Please also give the name of a file!\n");
	} else

	/*****************************************************************
	 * BUILD file
	 */
	if( stricmp(cmd,"BUILD") == 0 ) {
	  if( *p ) {
	    myMsg->Command = kfcPARSEFILE;
	    myMsg->BParam = (ULONG)p;
	    while( TellKF(myMsg) ) {
	      if( myMsg->Error == kfeOK ) {
		myMsg->Command = kfcADDFISH;
		myMsg->BParam  = 1;
		myMsg->FParam  = 0x0000;
		if( TellKF(myMsg) )
		  myMsg->Command = kfcPARSEFILE;  /* prepare for next swing through loop */
		else
		  break;
	      } else
		break;
	    } /* while */
	    if( myMsg->Error != kfeNOMORE ) {
	      myMsg->Command = kfcSTOPPARSE;
	      if( TellKF(myMsg) )
		printf("Database has been made sharable again.\n");
	    } else
	      printf("Parsing completed.  Database is sharable again.\n");
	  } else
	    printf("Please also give the name of a file!\n");
	} else

	/*****************************************************************
	 * REINDEX
	 *****************************************************************
	 */
	if( stricmp(cmd,"REINDEX") == 0 ) {
	  printf("Reindexing ");
	  /* we need to loop repeatedly until KFServer tells us it's done
	   */
	  for(;;) {
	    printf(".");
	    fflush(stdout);
	    myMsg->Command = kfcREINDEX;
	    if( !TellKF(myMsg) )
	      break;
	  } /* for */
	  printf("\n");
	} else

	/*****************************************************************
	 * QUIT (implies a BYE if connected)
	 *****************************************************************
	 */
	if( stricmp(cmd,"QUIT") == 0 ) {
	  if( connected ) {
	    myMsg->Command = kfcBYE;
	    if( TellKF(myMsg) ) {
	      if( myMsg->Error == kfeOK ) {
		printf("KFServer has said good bye.\n");
		connected = FALSE;
	      }
	    }
	  }
	  happy = FALSE;
	} else

	/*****************************************************************
	 * Command not recognized ...
	 *****************************************************************
	 */
	  if( cmd[0] )
	    printf("No such command: \"%s\"\n",cmd);
      } else {
	if( connected ) {
	  myMsg->Command = kfcBYE;
	  if( TellKF(myMsg) ) {
	    if( myMsg->Error == kfeOK ) {
	      printf("KFServer has said good bye.\n");
	      connected = FALSE;
	    }
	  }
	}
	happy = FALSE;  /* EOF received */
      }

      while( reply = (struct KFMsg *)GetMsg( myPort ) ) {
	if( reply->Command == kfcEXIT ) {
	  printf("Received EXIT request from server!\n");
	  connected = FALSE;
	}
	if( reply != myMsg )
	  ReplyMsg( (struct Message *)reply );
      }
    } /* while */

    if( myPort )
      DeleteMsgPort( myPort );

    if( myMsg ) {
      if( myMsg->Buffer )
	free( myMsg->Buffer );
      FreeMem( myMsg, sizeof(struct KFMsg) );
    }
    printf("Bye-bye!\n");
  } else
    printf("KFServer is not running!\n"
	   "Please start the server first!\n");
} /* main */


/* sends message to KFServer and waits for a reply; waits until our
 * message is returned from server; processes all intermittend
 * stuff from the server (i.e. kfcEXIT requests);
 * returns FALSE if any problem occurred, or if the server asked us
 * to exit; TRUE means "all is well, let's keep on dancing!"
 */
BOOL TellKF( struct KFMsg *myMsg ) {

  struct MsgPort *PrimaryPort,*myPort=myMsg->AmigaMsg.mn_ReplyPort;
  struct KFMsg *reply;
  BOOL result=TRUE,showError=TRUE,done=FALSE;
  ULONG sigs;

  Forbid();
  if( PrimaryPort = FindPort( KFPortName ) )
    PutMsg( PrimaryPort, (struct Message *)myMsg );
  Permit();

  if( PrimaryPort ) {  
    while( !done ) {
      sigs = Wait( (1L << myPort->mp_SigBit) | SIGBREAKF_CTRL_C );
      if( sigs & (1L << myPort->mp_SigBit) )
	while( reply = (struct KFMsg *)GetMsg( myPort ) ) {
	  if( reply == myMsg ) {
	    done = TRUE;
	    if( showError && (myMsg->Error != kfeOK) ) {
	      printf("ERROR: %s\n",KFError[myMsg->Error]);
	      if( myMsg->Error == kfeTRUNC )
		UpgradeBuffer(myMsg);
	      if( myMsg->Buffer )
		printf(myMsg->Buffer);
	      printf("\n");
	      result = FALSE;
	    }
	  } else {
	    if( reply->Command == kfcEXIT ) {
	      printf("Received EXIT request from server!\n");
	      result = FALSE;
	    } else {
	      if( myMsg->Error != kfeOK ) {
		printf("ERROR: %s\n",KFError[myMsg->Error]);
		if( myMsg->Buffer )
		  printf(myMsg->Buffer);
		printf("\n");
		showError = FALSE;
		result = FALSE;
	      }
	    }
	    printf("Replying to %08x\n",reply->AmigaMsg.mn_ReplyPort);
	    ReplyMsg( (struct Message *)reply );
	  }
	} /* while */
      if( sigs & SIGBREAKF_CTRL_C ) {
	SetSignal(0L,SIGBREAKF_CTRL_C);
	printf("Wait for reply from server interrupted.\n"
	       "The client is now in a precarious state and should NOT quit!\n");
	done = TRUE;
	result = FALSE;
      }
    } /* while */
  } else {
    printf("The KFServer (%s) has shutdown!\n"
	   "Please QUIT the CLIent, too.\n",KFPortName);
    myMsg->Error = 0xffff;
    result = FALSE;
  }
  return result;
} /* Tell */
