#include <stdio.h>
#include <exec/types.h>
#include <exec/io.h>
#include <exec/memory.h>

#include <graphics/gfxbase.h>
#include <graphics/text.h>
#include <graphics/gfxmacros.h>
#include <devices/console.h>
#include <devices/keymap.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <intuition/intuition.h>
#include <functions.h>

extern UBYTE filestring[];

UBYTE *LoTypes;
ULONG *LoMap;
UBYTE *LoCap;
UBYTE *LoRep;
UBYTE *HiTypes;
ULONG *HiMap;
UBYTE *HiCap;
UBYTE *HiRep;
int abort=0;

extern void FillKeyMap();
extern long DLength();
extern void NewStr();

long tlen;
UBYTE *told,*tnew;

struct {
UBYTE length;
UBYTE string[127];
} DStr[8]; /* one per each qualifier */

struct IOStdReq *WriteMsg;   /* I/O request block pointer */
struct MsgPort  *WritePort;  /* a port at which to receive replies*/
int DescLen[8]={2,4,4,8,4,8,8,16};
struct KeyMap MyKeyMap;

/*      For writing to the console: */
void MakeConWP()
{
   WritePort = CreatePort("mycon.write",0L);
   if(WritePort == 0L) exit(100);    /* error in createport */
   WriteMsg =  CreateStdIO(WritePort);
   if(WriteMsg == 0L) exit(200);     /* error in createstdio */
}



/* output a NULL-terminated string of characters to a console */
void
TurnCursorOff()
{
   WriteMsg->io_Command = CMD_WRITE;
   WriteMsg->io_Data = (APTR)"[0 p";
   WriteMsg->io_Length = -1;
   DoIO(WriteMsg);
   return;
}




/* this function returns a value of 0 if the console
 * device opened correctly and a nonzero value (the error
 * returned from OpenDevice) if there was an error.
 */
long OpenConsole(window)
struct Window *window;
{
/* Open a console device */
   long error;

   MakeConWP(); /* make a write console port */
   WriteMsg->io_Data   = (APTR) window;
   WriteMsg->io_Length = (long)sizeof(*window);
   error=OpenDevice("console.device", 0L, WriteMsg, 0L);
   TurnCursorOff();
   return(error);
}



/* Set the current KeyMap with pointers in given keymap. */

void SetKeyMap(KeyMap)
struct KeyMap *KeyMap;
{
   WriteMsg->io_Command = CD_SETKEYMAP;
   WriteMsg->io_Length = 32L;
   WriteMsg->io_Data = (APTR)KeyMap;
   DoIO(WriteMsg);
}


/* Make a new string so the FreeMem does not crash */
void FixDeadKey(key)
int key;
{
   int i;
   char *temp;

   told=(UBYTE *)LoMap[key];
   tnew=(UBYTE *)&LoMap[key];
   LoMap[key]=0;

   if(told[0]==0) tnew[3]=told[1];
   else tnew[3]=*(told+told[1]);

   if(told[2]==0) tnew[2]=told[3];
   else tnew[2]=*(told+told[3]);

   LoTypes[key]&=0x0F;
}



/* Fill Default keymap and copy to MyKeyMap */
void DefaultKeyMap()
{
   int i;

   FillKeyMap(&MyKeyMap);
   for(i=0;i<64;i++)
   {
      LoTypes[i]=MyKeyMap.km_LoKeyMapTypes[i];
      LoMap[i]=MyKeyMap.km_LoKeyMap[i];
      if(LoTypes[i]&0x40) NewStr(i);
      if(abort) return;
      if(LoTypes[i]&0x20) FixDeadKey(i);
   }
   for(i=0;i<8;i++)
   {
      LoCap[i]=MyKeyMap.km_LoCapsable[i];
      LoRep[i]=MyKeyMap.km_LoRepeatable[i];
   }
   for(i=0;i<56;i++)
   {
      HiTypes[i]=MyKeyMap.km_HiKeyMapTypes[i];
      HiMap[i]=MyKeyMap.km_HiKeyMap[i];
      if(HiTypes[i]&0x40) NewStr(i+0x40);
      if(abort) return;
      if(HiTypes[i]&0x20) FixDeadKey(i+0x40);
   }
   for(i=0;i<7;i++)
   {
      HiCap[i]=MyKeyMap.km_HiCapsable[i];
      HiRep[i]=MyKeyMap.km_HiRepeatable[i];
   }
}



/* DeAlloc string mem before exit and before LoadKeyMap() */
void ClearStr(key)
int key;
{
   int i;
   for(i=0;i<key;i++) /* deallocate strings both lo and hi*/
   {
      if(LoTypes[i]&0x40)
      {
         told=(UBYTE *)LoMap[i];
         FreeMem(told,DLength(told));
      }
   }
}



ULONG LoadKeyMap(string)
char *string;
{
   int i;
   ULONG segment;
   UWORD *Ptr,*km;
   ULONG *temp,*nptr,length;
   char *name;

   segment=(ULONG)LoadSeg(string);
   if(segment==0) return(0L);

   Ptr=(UWORD *)BADDR(segment);
   temp=(ULONG *)Ptr;
   length=*(temp-1);
   nptr=(ULONG *)(Ptr+7);
   name=(char *)*nptr;

   if((ULONG)name < (ULONG)temp || (ULONG)name > (length+(ULONG)temp))
   {
      UnLoadSeg(segment);
      return(0L);
   }
   km=(UWORD *)&MyKeyMap.km_LoKeyMapTypes;
   for(i=0;i<16;i++) km[i]=Ptr[i+9];
   SetKeyMap(&MyKeyMap);
   ClearStr();
   DefaultKeyMap();

   UnLoadSeg(segment);
   return(segment);
}



/* Fill the given KeyMap with pointer to current keymap. */
void FillKeyMap(KeyMap)
struct KeyMap *KeyMap;
{
   WriteMsg->io_Command = CD_ASKKEYMAP;
   WriteMsg->io_Length = 32L;
   WriteMsg->io_Data = (APTR) KeyMap;
   DoIO(WriteMsg);
}


/* strings are stored as sl0sO0sl1sO1...s0s1...
 * where sl0 is length of string 0 and sO0 is offset from start of descriptor
 * since descriptor overhead comes first sO0 is number of bytes in descriptor
 * since contiguous and monotonically increasing the last string offset +
 * length is the total length.
 */
long DLength(DString)
UBYTE *DString;
{
   long temp;
   int i,num;
   temp=num=DString[1];   /* number of strings in descriptor */
   for (i=0;i<num;i++,i++) temp+=DString[i];  /* total length of strings */
   return(temp);
}


FreeKeyMap(which)
int which;
{
   FreeMem(LoTypes,120L);
   if(which>1)FreeMem(LoMap,480L);
   if(which>2)FreeMem(LoCap,15L);
   if(which>3)FreeMem(LoRep,15L);
}


/* Make a new string so the FreeMem does not crash */
void NewStr(key)
int key;
{
   int i,j,in,out,len,desc;
   UBYTE type;

   told=(UBYTE *)LoMap[key];
   tlen=DLength(told);       /* total length of strings + descriptor  */
   tnew=(UBYTE *)AllocMem(tlen,0L);
   if(tnew==0)
   {
      ClearStr(key);
      FreeKeyMap(4);
      abort=1;
      return;
   }

   type=LoTypes[key]&0x07;
   desc=out=DescLen[type]; /* account for bytes in descriptor */
   for(i=0;i<desc;i++)
   {
      len=tnew[i]=told[i];
      i++;
      in=told[i];
      tnew[i]=out;
      for(j=0;j<len;j++)tnew[out+j]=told[in+j];
      out+=len;
   }
   LoMap[key]=(ULONG)tnew;
}





void ConsoleCleanup()
{
   CloseDevice(WriteMsg);
   DeleteStdIO(WriteMsg);
   DeletePort(WritePort);
}



/* take string and put in DStr array for easy editing */
void String(key)
int key;
{
   int i,in,out,test,oldtest,offset;
   UBYTE type;

   for (i=0;i<8;i++) DStr[i].length=DStr[i].string[0]=NULL;
   type=LoTypes[key]&0x07;
   if (LoTypes[key]&0x40) /* strings */
   {
      told=(UBYTE *)LoMap[key];
      oldtest=-1;
      in=0;
      for(out=0;out<8;out++)
      {
         if ((test=(type&out))>oldtest)
         {
            oldtest=test;
            DStr[out].length=told[(in<<1)];  /* length */
            offset=told[(in<<1)+1];          /* offset */
            for(i=0;i<DStr[out].length;i++)DStr[out].string[i]=told[offset+i];
            DStr[out].string[i]=NULL;
            in++;
         }
      }
   }
   else /* plain keys */
   {
      told=(UBYTE *)&LoMap[key];
      if((type&7)==7) type=3; /* special case of ctrl with bits 5&6 to 0 */
      oldtest=-1;
      in=3;
      for(out=0;out<8;out++)
      {
         if ((test=(type&out))>oldtest)
         {
            oldtest=test;
            DStr[out].length=1;            /* length    */
            DStr[out].string[0]=told[in];  /* key value */
            DStr[out].string[1]=NULL;
            --in;
         }
      }
   }
} /* end of String */




/* put string back in standard Descriptor format from DStr array */
DeString(key)
int key;
{
   int shift,alt,ctrl,i,offset,keyflag,sum,test,oldtest,in,out;
   UBYTE type;

   shift =DStr[1].length+DStr[3].length+DStr[5].length+DStr[7].length;
   alt   =DStr[2].length+DStr[3].length+DStr[6].length+DStr[7].length;
   ctrl  =DStr[4].length+DStr[5].length+DStr[6].length+DStr[7].length;
   type=0;
   if (shift) type|=1;
   if (alt)   type|=2;
   if (ctrl)  type|=4;
   offset=tlen=DescLen[type]; /* account for bytes in descriptor */

   keyflag=1; /* true mean it is a key */
   for (sum=0,i=0;i<8;i++)
   {
      if (DStr[i].length>1) keyflag=0;
      sum+=DStr[i].length;
   }
   if (sum>4) keyflag=0;
   
   if (!keyflag) /* strings */
   {
      for (i=0;i<8;i++) tlen+=DStr[i].length;
      tnew=AllocMem(tlen,0L);
      if(tnew==0)
      {
         ClearStr(120);
         FreeKeyMap(4);
         abort=1;
         return;
      }
      if (LoTypes[key]&0x40)
      {
         told=(UBYTE *)LoMap[key];
         FreeMem(told,DLength(told));
      }

      oldtest=-1;
      in=0;
      for(out=0;out<8;out++)
      {
         if ((test=(type&out))>oldtest)
         {
            oldtest=test;
            tnew[(in<<1)]=DStr[out].length;  /* length */
            tnew[(in<<1)+1]=offset;          /* offset */
            for(i=0;i<DStr[out].length;i++)tnew[offset+i]=DStr[out].string[i];
            offset+=DStr[out].length;
            in++;
         }
      }
      LoMap[key]=(ULONG)tnew;
      LoTypes[key]=0x40|type;
   }
   else /* plain keys */
   {
      told=(UBYTE *)&LoMap[key];
      oldtest=-1;
      in=3;
      for(out=0;out<8;out++)
      {
         if ((test=(type&out))>oldtest)
         {
            oldtest=test;
            told[in]=DStr[out].string[0];  /* key value */
            --in;
         }
      }
      if (type!=3 || LoTypes[key]!=7) LoTypes[key]=type;
   }
}


/* Save off current keymap in hunk format (not easy)*/
ULONG
SaveKeyMap(strng)
char *strng;
{
   return(1L);
}

AllocKeyMap()
{
   LoTypes=(UBYTE *)AllocMem(120L,0L);
   if(LoTypes==0) goto Cancel;
   LoMap=  (ULONG *)AllocMem(480L,0L);
   if(LoMap==0) 
   {
      FreeKeyMap(1);
      goto Cancel;
   }
   LoCap=  (UBYTE *)AllocMem(15L,0L);
   if(LoCap==0) 
   {
      FreeKeyMap(2);
      goto Cancel;
   }
   LoRep=  (UBYTE *)AllocMem(15L,0L);
   if(LoRep==0) 
   {
      FreeKeyMap(3);
      goto Cancel;
   }

   HiTypes=LoTypes+64;
   HiMap=  LoMap+64;
   HiCap=  LoCap+8;
   HiRep=  LoRep+8;
   return;
Cancel:
   abort=1;
   return;
}
