/* Shared Data Server Library */!!

inherit(ProvidedLibs, #SharedDataLib, #(finalized?
simAddress 
   /* address of SIM; see getSimAddress */
lastSdError), 2, nil)!!

now(class(SharedDataLib))!!

/* semiPRIVATE -- should only be called via require(SharedDataLib)
   Define entry points and load library.  Define any structs required.
*/
Def provide(self, path)
{
  ^initialise( new(self:ancestor), path )
}
!!

now(SharedDataLib)!!

/* Return the value of the last error returned by the sds */
Def getLastSdError(self)
{
  ^lastSdError;
}!!

/* Call SdUnegister. */
Def sdReadMemberLong(self, desc | temp)
{
  temp := new(Struct, 4);

  if pcallLock(self)
    lastError :=
      pcall(procs[#SDREADMEMBER], desc, temp);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;  

  lastSdError := lastError;
  
  if (lastError <> 0)
    ^nil;
  endif;
  
  ^longAt(temp, 0);
}!!

/* Call SdnReadMember. */
Def sdnReadMemberLong(self, member | temp)
{
  temp := new(Struct, 4);

  if pcallLock(self)
    lastError :=
      pcall(procs[#SDNREADMEMBER], member, temp);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;  

  lastSdError := lastError;  

  if (lastError <> 0)
    ^nil;
  endif;
  
  ^longAt(temp, 0);
}!!

/* Call SdUnregister. */
Def sdUnregister(self, desc)
{
  if pcallLock(self)
    lastError :=
      pcall(procs[#SDUNREGISTER], desc);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;  
  
  if (lastError <> 0)
    displayError(ErrorTextLibClass$Inst, lastError, CHECK_MODE);
    ^nil
  endif;
  
  ^0;
}!!

/* Call SdRegister (don't support callback). */
Def sdRegister(self, name | temp)
{
  temp := new(Struct, 4);
  putLong(temp, 0L, 0);  /* make it null for C */  

  if pcallLock(self)
    lastError :=
      pcall(procs[#SDREGISTER], asciiz(name), 0L, temp);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;  
  
  if (lastError <> 0)
    displayError(ErrorTextLibClass$Inst, lastError, CHECK_MODE);
    ^nil
  endif;
  
  ^longAt(temp, 0);
}!!

/* Read value of SDN_SIM_ADDRESS. */
Def getSimAddress(self | descriptor sim simValid)
{
  /* get descriptor */
  descriptor := sdRegister(self, "sim valid");
  if not(descriptor)
    ^nil;
  endif;
    
  /* get member */
  simValid := sdReadMemberLong(self, descriptor);
  if not(simValid)
    sdUnregister(self, descriptor);
    ^nil;
  endif;
  
  /* unregister */
  if not(sdUnregister(self, descriptor))
    ^nil;
  endif;
  
  if (simValid = 0)
    ^nil;  /* the sim is not valid */
  endif;
  
  /* get descriptor */
  descriptor := sdRegister(self, "sim register bank address");
  if not(descriptor)
    ^nil;
  endif;
    
  /* get member */
  sim := sdReadMemberLong(self, descriptor);
  if not(sim)
    sdUnregister(self, descriptor);
    ^nil;
  endif;
  
  /* unregister */
  if not(sdUnregister(self, descriptor))
    ^nil;
  endif;
  
  ^sim;
}!!

/* PRIVATE
   Define all entry points for library as prototypes.
*/
Def addImportProcs(self)
{
  add(self, #INITCSERVER,       1, #(0, 0));
  add(self, #SDINITIALIZE,      1, #(0, 0, 1));
  add(self, #SDSETMAINHWND,     1, #(0));
  add(self, #SDTERMINATESERVER, 1, nil);
  add(self, #SDNREADMEMBER,     1, #(1, 1));
  add(self, #SDREGISTER,        1, #(1, 1, 1));
  add(self, #SDUNREGISTER,      1, #(1));
  add(self, #SDREADMEMBER,      1, #(1, 1));
}!!

/* PUBLIC */
Def free(self)
{
  if not(finalized?)
  then
    if pcallLock(self)
    then
      pcall( procs[#SDTERMINATESERVER] ) ; 
      finalized? := #true ;
      pcallUNLock(self);
    else 
      ^ERR_PCALL_RECURSION ;
    endif ;
  endif ;

  free( self:ancestor );
}
!!

/* PUBLIC -- do a silly operation to keep the SDS going. */
Def hitSharedDataServer(self | temp)
{
  temp := new(Struct, 4);

  /* hit the SDS with a call that won't error out. */
  if pcallLock(self)
    lastError :=
      pcall(procs[#SDNREADMEMBER], SDN_NUM_SHARED_DATA_BYTES, temp);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

  if (lastError <> GOOD)
     displayError(ErrorTextLibClass$Inst, lastError, CHECK_MODE);
  endif;
  ^0;
}!!

/* PRIVATE - Open and initialize shared data library */
Def initialise(self, path)
{
   setName(self, path);
   addImportProcs(self);
   load(self);  /* raises alert if fails: alert(System, self, #libLoad) */
   if ((lastError := initServer(self)) <> 0)
     if (lastError <> ER_COM_OPEN)
       displayError(ErrorTextLibClass$Inst, lastError, FORCE_POPUP);
       ^nil;
     else
       ^nil;
     endif;
   else 
     ^self
   endif;
}
!!

/* PRIVATE */
Def initServer(self | rc comType comPort portNum serialCommFlag)
{

  portNum := new( Struct, 4 ) ;
  comType := getProfileEntry(TheProfileInfoObj, "Comm",
                             "type", "" );
  if not(comType) then
    /* no entry was found force it to serial */
    comType := "serial";
  else
    /* an entry was found so turn it into a string for parsing */
    comType := asLowerCase(first(comType));
  endif;

  if (find(comType,"serial",0))
    /* additional serial-line only processing */
    serialCommFlag := 0;
    comPort := getProfileEntry(TheProfileInfoObj, "Serial", "comport", "");
    if (not(comPort)) then
      /* no entry found so force the user to be queried */
      comPort := nil;
    else
      comPort := asLowerCase(first(comPort));
    endif;

    select
      case comPort = "com1" is putWord(portNum, 1, 0);   endCase
      case comPort = "com2" is putWord(portNum, 2, 0);   endCase
      case comPort = "com3" is putWord(portNum, 3, 0);   endCase
      case comPort = "com4" is putWord(portNum, 4, 0);   endCase
      default
        /* unknown port number, query user */ 
        putWord( portNum, 0, 0 );
    endSelect;
  endif;
    
  if pcallLock(self)
    rc := pcall( procs[#SDINITIALIZE], 
                 0, /*@@ hWnd(mainWindow(TheApp)), @@*/
                 0,
                 portNum);
    pcallUNLock(self);
  else 
    rc := ERR_PCALL_RECURSION;
  endif ;

  if (rc = 0)
    /* function returned TRUE */
    if (serialCommFlag)
      portNum := wordAt(portNum,0);
      if ((portNum >= 0) cand (portNum <= 4)) then
        saveProfileEntry(TheProfileInfoObj, "Comm", "type", "serial");
        saveProfileEntry(TheProfileInfoObj, "Serial", "comport",
                       "com" + asString(portNum));
      else
        ^nil;
      endif;
    endif;
  endif;
  ^rc
}
!!

/* PRIVATE */
Def setMainWnd(self)
{
  if pcallLock(self)
  then
    lastError := pcall( procs[#SDSETMAINHWND], 
                        hWnd(mainWindow(TheApp))
               );
    pcallUNLock(self);
  else 
    lastError := ERR_PCALL_RECURSION ;
  endif ;

  ^lastError
}
!!
