/*****************************************************************************
*   Routines to handle I/O of objects through exec message ports             *
*   Client side.                                                             *
*                                                                            *
* Written by:  Kriton Kyrimis                                                *
*        and   Gershon Elber                        Ver 0.2, December 1993.  *
*****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <exec/types.h>
#include <exec/ports.h>
#include <exec/memory.h>

#ifdef __SASC
#include <dos.h>
#include <proto/dos.h>
#include <proto/exec.h>
#endif

#ifdef __GNUC__
#include <dos/dos.h>
#include <dos/var.h>
#include <inline/dos.h>
#include <inline/exec.h>
#endif

#include "irit_sm.h"
#include "irit_soc.h"

#include "amiga.h"

static struct MsgPort *port = NULL;
static LONG oldpri;
static int Active = FALSE;
static int GlblBinaryIPC = FALSE;
static int GlblEchoInput = FALSE;
static char GlblClientUnReadChar = 0;

static int SocReadObjPrefix(void);

/*****************************************************************************
*  Opens the port. Returns TRUE if succesful.                                *
*****************************************************************************/
int SocClientCreateSocket(void)
{
    int len;
    char *name, buf[4];

    GlblBinaryIPC = getenv("IRIT_BIN_IPC") != NULL;
    len = GetVar(SERVER_VAR, buf, sizeof(buf), GVF_GLOBAL_ONLY);
    if (len < 0) {
	return NULL;
    }
    DeleteVar(SERVER_VAR, GVF_GLOBAL_ONLY);

    name = getenv("IRIT_SERVER_PORT");
    if (!name) {
        name = IRIT_SERVER_PORT;
    }
    Forbid();
    port = FindPort(name);
    Permit();
    if (port) {
	Active = TRUE;
	buf[0] = '\0';
	SetVar(CLIENT_VAR, buf, -1, GVF_GLOBAL_ONLY);
	/* This beast busy-waits! Reduce its priority so that we don't hog
	   other tasks, namely irit. */
	oldpri = SetTaskPri(FindTask(0L), -1);
	return TRUE;
    } else {
	return FALSE;
    }
}

/*****************************************************************************
*  Close the port.                                                           *
*****************************************************************************/
void SocClientCloseSocket(void)
{
    /* Nothing to do on our side. */
    Active = FALSE;
    SetTaskPri(FindTask(0L), oldpri);
}

/*****************************************************************************
*  Set echo printing of read input.                                          *
*****************************************************************************/
void SocClientEchoInput(int EchoInput)
{
    GlblEchoInput = EchoInput;
}

/*****************************************************************************
* Attempt to read (non blocking) from socket an object.                      *
* If read is successful the object is returned, otherwise NULL is returned.  *
*****************************************************************************/
IPObjectStruct *SocClientReadOneObject(void)
{
    char *ErrorMsg;
    IPObjectStruct *PObj;

    if (Active && SocReadObjPrefix()) {
        IritPrsrSetReadOneObject(TRUE);

        IritPrsrReadSocket(TRUE);
        if (GlblBinaryIPC) {
            PObj = IritPrsrGetBinObject(NULL);
        }
        else { 
            PObj = IritPrsrGetObjects(NULL);
        }
        IritPrsrReadSocket(FALSE);
    } else {
        PObj = NULL;
    }

    if (IritPrsrParseError(&ErrorMsg)) {
        fprintf(stderr, "Socket: %s\n", ErrorMsg);
    }

    return PObj;
}

/*****************************************************************************
*  Unget once char from client port                                          *
*****************************************************************************/
void SocClientUnReadChar(char c)
{
    GlblClientUnReadChar = c;
}

/*****************************************************************************
*  Non blocking read of a single char. Returns EOF if no data is found       *
*****************************************************************************/
int SocClientReadCharNonBlock(void)
{
    struct IritMessage *msg;
    int c;

    static int BufferSize = 0, BufferPtr = 0;
    static unsigned char Buffer[MSGSIZE];

    if (GlblClientUnReadChar != 0) {
	c = GlblClientUnReadChar;
	GlblClientUnReadChar = 0;
    } else {
        if (BufferPtr < BufferSize) {
            c = Buffer[BufferPtr++];
        } else {
            msg = (struct IritMessage *)GetMsg(port);
	    if (msg) {
		BufferSize = msg->nbytes;
		memcpy(Buffer, msg->txt, BufferSize);
		ReplyMsg((struct Message *)msg);
	        if (GlblEchoInput) {
	            int i;
	            unsigned char *p = Buffer;

	            if (GlblBinaryIPC) {
	                for (i = 0; i < BufferSize; i++) {
	                    if (i % 16 == 0)
	                        printf("\n%04x: ", i);
	                    printf("%02x ", *p++);
	                }
	                printf("\n");
	            }
	            else
	                for (i = 0; i < BufferSize; i++) {
	                    char tmp = *p++;
	                    putc(tmp, stdout);
	                }
	        }
		BufferPtr = 0;
		c = Buffer[BufferPtr++];
	    } else {
	        c = EOF;
	    }
        }
    }
    return c;
}

/*****************************************************************************
* Get a single line for syncronization purposes that will prefix an object.  *
*   Returns TRUE if prefix found, FALSE otherwise.                           *
*****************************************************************************/
static int SocReadObjPrefix(void)
{
    if (GlblBinaryIPC) {
        int c;

        if ((c = SocClientReadCharNonBlock()) != EOF) {
            SocClientUnReadChar(c);
            return TRUE;
        }
    }
    else {
        if (SocClientReadCharNonBlock() == '[') {
	    SocClientUnReadChar('[');
	    return TRUE;
        }
    }

    return FALSE;
}
