/****************************************************************/
/*                                                              */
/*              Standard instrument device startup              */
/*                                                              */
/*                   version 2000.05.29                         */
/*                                                              */
/****************************************************************/

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/tasks.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <exec/devices.h>
#include <exec/errors.h>
#include <libraries/dos.h>
#include <dos/dostags.h>
#include <dos/dos.h>
#include <proto/exec.h>
#include <proto/dos.h>

#include <string.h>
#include <instrument.h>

// #define STACK_SIZE   10000

struct UnitId
{
  struct Node     ui_Node;
  struct Process* ui_Prc;
  struct Unit     ui_Unit;
};

extern struct ExecBase *SysBase;
extern int  stackSize;
extern char DevName[];
extern char DevIdString[];

extern void TaskStartup(void);

static struct List UnitList;

static struct UnitId *GetUnit(UBYTE *name, int num);
static void DeleteUnit(struct UnitId *Id);

void NewTask(struct Unit *Unit,char* devName,int devUnit)
{
  Unit->unit_MsgPort.mp_SigBit = AllocSignal(-1);
  Unit->unit_MsgPort.mp_Flags = PA_SIGNAL;
  
  UnitMain(Unit,devName,devUnit);
}

struct InstrumentBase *DevInit(struct InstrumentBase *libbase)
{
  if (SysBase->LibNode.lib_Version < 37)
    return NULL; /* can only run under 2.04 or greater */ 
  else
  {
    NewList(&UnitList);
    return libbase;
  }
}

int DevOpen(struct InstrumentBase *libbase,long UnitNum,struct InstrumentIO *ior,long flags)
{
  struct UnitId *id;
  UBYTE* DevName;
  int retcode;
  
  DevName = (UBYTE *)(ior->io_Data);

  if( !(id = GetUnit(DevName,UnitNum)) )
    retcode = IOERR_OPENFAIL;
  else
  {
    struct MsgPort *OldReply;
    
    OldReply = ior->io_Message.mn_ReplyPort;

    if( ior->io_Message.mn_ReplyPort = CreatePort(0,0) )
    {
      ior->io_Error = 0;
      ior->io_Command = FCMD_OPEN;
      PutMsg(&id->ui_Unit.unit_MsgPort, &ior->io_Message);
      //WaitPort(ior->io_Message.mn_ReplyPort);
      {
        struct Message *msg;
        int delayTick = 2;
        
        do
        {
          delayTick *= 2;
          Delay(delayTick);
          msg = GetMsg(ior->io_Message.mn_ReplyPort);
        }
        while( (msg == NULL) && (delayTick < 256) );
        
        if( msg == NULL )
        {
          // Hanging - send abort signal
          Signal(&id->ui_Prc->pr_Task,SIGBREAKF_CTRL_C);
          WaitPort(ior->io_Message.mn_ReplyPort);
        }
      }
      DeletePort(ior->io_Message.mn_ReplyPort);
      if( ior->io_Error )
      {
        if( id->ui_Unit.unit_OpenCnt == 0 )
          DeleteUnit(id);
        retcode = ior->io_Error;
      }
      else
        retcode = 0;
    }
    else
    {
      retcode = IOERR_OPENFAIL;
    }

    ior->io_Message.mn_ReplyPort = OldReply;
  }
  return retcode;
}

void DevClose(struct InstrumentBase *libbase,struct InstrumentIO *ior)
{
  struct UnitId *id;

  id = (struct UnitId *)UnitList.lh_Head;
  while( id->ui_Node.ln_Succ && (&id->ui_Unit != *ior->io_Unit) )
    id = (struct UnitId *)id->ui_Node.ln_Succ;

  if( id->ui_Node.ln_Succ )
  {
    /* send CLOSE  message to the child process */
    struct MsgPort *OldReply;
    
    OldReply = ior->io_Message.mn_ReplyPort;

    if( ior->io_Message.mn_ReplyPort = CreatePort(0,0) )
    {
      ior->io_Error = 0;
      ior->io_Command = FCMD_CLOSE;
      PutMsg(&id->ui_Unit.unit_MsgPort, &ior->io_Message);
      WaitPort(ior->io_Message.mn_ReplyPort);
      DeletePort(ior->io_Message.mn_ReplyPort);
      if( id->ui_Unit.unit_OpenCnt == 0 )
      {
        DeleteUnit(id);
      }
    }
    ior->io_Unit = (struct Unit **)-1;

    ior->io_Message.mn_ReplyPort = OldReply;
  }
}

BOOL DevExpunge(struct InstrumentBase *libbase)
{
  return TRUE;
}

void DevBeginIO(struct InstrumentBase *libbase,struct InstrumentIO *ior)
{
  ior->io_Error = 0;

  if( (ior->io_Flags & IOF_QUICK) && QuickIO(ior) )
    ior->io_Message.mn_Node.ln_Type = NT_REPLYMSG;
  else
  {
    ior->io_Message.mn_Node.ln_Type = NT_MESSAGE;
    PutMsg(&((*ior->io_Unit)->unit_MsgPort), &ior->io_Message);
  }
}

int DevAbortIO(struct InstrumentBase *libbase,struct InstrumentIO *ior)
{
  if( (ior->io_Flags & IOF_QUICK) || (ior->io_Message.mn_Node.ln_Type==NT_REPLYMSG) )
    return 0;
  else
  {
    struct Node *np;
    BOOL Aborted = FALSE;

    Forbid();
    np = (*ior->io_Unit)->unit_MsgPort.mp_MsgList.lh_Head;
    while( np->ln_Succ && (np != &ior->io_Message.mn_Node) )
      np = np->ln_Succ;
    if( np == &ior->io_Message.mn_Node )
    {
      Remove(np);
      ior->io_Error = IOERR_ABORTED;
      ReplyMsg(&ior->io_Message);
      Aborted = TRUE;
    }
    Permit();
 
    if( !Aborted )
    {
      struct UnitId *id;

      id = (struct UnitId *)UnitList.lh_Head;
      while( id->ui_Node.ln_Succ && (&id->ui_Unit != *ior->io_Unit) )
        id = (struct UnitId *)id->ui_Node.ln_Succ;

      if( id->ui_Node.ln_Succ )
      {
        /* send ABORT signal to the child process */
        Signal(&id->ui_Prc->pr_Task,SIGBREAKF_CTRL_C);
      }
    }
    return 0;
  }
}

static struct UnitId *GetUnit(UBYTE *name, int num)
{
  char PrcName[256];
  char *p;
  struct UnitId *id;

  p =  stpcpy(PrcName,DevIdString);
  p =  stpcpy(p,".");
  p += stcgfn(p,(char *)name);
  p =  stpcpy(p,".");
  (void)stci_d(p,num);

  if( !(id = (struct UnitId *)FindName(&UnitList,PrcName)) )
  {
    struct StartUpMsg msg;

    if( !(id = (struct UnitId *)AllocVec(sizeof(struct UnitId)+strlen(PrcName)+1,MEMF_CLEAR)) )
      return NULL;
    (void)stpcpy((char *)(id+1),PrcName);
    id->ui_Node.ln_Name = (char *)(id+1);

    if( !(msg.msg.mn_ReplyPort = CreateMsgPort()) )
    {
      FreeVec(id);
      return NULL;
    }

    id->ui_Prc = CreateNewProcTags( NP_Entry,TaskStartup,
                                    NP_StackSize,stackSize,
                                    NP_Name,(char *)(id+1),
                                    NP_Priority,4,
                                    TAG_DONE
                                  );

    if( id->ui_Prc == NULL )
    {
      DeleteMsgPort(msg.msg.mn_ReplyPort);
      FreeVec(id);
      return NULL;
    }

    id->ui_Unit.unit_MsgPort.mp_Node.ln_Type = NT_MSGPORT;
    id->ui_Unit.unit_MsgPort.mp_Node.ln_Name = (char *)(id+1);
    id->ui_Unit.unit_MsgPort.mp_Flags = PA_IGNORE;
    id->ui_Unit.unit_MsgPort.mp_SigTask = (struct Task *)id->ui_Prc;
    (void)NewList(&id->ui_Unit.unit_MsgPort.mp_MsgList);
    id->ui_Unit.unit_OpenCnt = 0;

    /* Send the startup message */
    msg.msg.mn_Length = sizeof(struct StartUpMsg); 
    msg.msg.mn_Node.ln_Type = NT_MESSAGE;
    msg.unit = &id->ui_Unit;
    msg.devName = name;
    msg.devUnit = num;

    PutMsg(&id->ui_Prc->pr_MsgPort,&msg.msg);
    WaitPort(msg.msg.mn_ReplyPort);
    (void)GetMsg(msg.msg.mn_ReplyPort);

    if( msg.unit )
      AddTail(&UnitList,&id->ui_Node);
    else
    {
      /* Send close message */
      msg.msg.mn_Node.ln_Type = NT_MESSAGE;
      PutMsg(&id->ui_Prc->pr_MsgPort,&msg.msg);
      WaitPort(msg.msg.mn_ReplyPort);
      (void)GetMsg(msg.msg.mn_ReplyPort);
      FreeVec(id);
      id = NULL;
    }
    DeleteMsgPort(msg.msg.mn_ReplyPort);
  }

  return id;
}

static void DeleteUnit(struct UnitId *id)
{
  struct Message msg;
    
  if( msg.mn_ReplyPort = CreateMsgPort() )
  {
    msg.mn_Length = sizeof(struct Message); 
    msg.mn_Node.ln_Type = NT_MESSAGE;
    PutMsg(&id->ui_Prc->pr_MsgPort,&msg);

    WaitPort(msg.mn_ReplyPort);
    (void)GetMsg(msg.mn_ReplyPort);
    DeleteMsgPort(msg.mn_ReplyPort);

    Remove(&id->ui_Node);
    FreeVec(id);
  }
}
