#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);
}


int ctrlflag;
/* 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];
      ctrlflag=0;
      if((type&7)==7)
      {
         ctrlflag=1;
         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;
         }
      }
      if (ctrlflag)
      {
         DStr[4].length=1;
         DStr[4].string[0]=DStr[0].string[0] & 0x9F;
         DStr[4].string[1]=NULL;
      }
   }
} /* 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;
   ctrlflag=0;
   if(ctrl==1 && DStr[4].string[0]==(DStr[0].string[0] & 0x9F))
   {
      ctrlflag=1;
      ctrl=0;
      DStr[4].length=0;
      DStr[4].string[0]=NULL;
   }
   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;
         }
      }
      LoTypes[key]=type;
      if (ctrlflag) LoTypes[key]|=4;
   }
}


/* Save off current keymap in hunk format (not easy)*/
ULONG
SaveKeyMap(strng)
char *strng;
{
long *fd,NumStr,StrOffset,len,HunkSize,name,string;
ULONG *buffer;
UBYTE *cbuf;
int ind,i;

   len=708; /* size of fixed portion of buffer at front */

   NumStr=0;
   for(ind=0;ind<120;ind++) /* Both Hi and Lo maps */ 
   {
      if(LoTypes[ind]&0x40)
      {
         NumStr++;
         len+=DLength(LoMap[ind]);
      }
   }

   if(len&3) len=4+len&~3;                /* round up to mult of 4 (ULONG) */
   name=len;
   len+=strlen(filestring)+1;             /* account for keymap file name */
   len+=11;                               /* account Serial Number + null */
   if(len&3) len=4+len&~3;                /* round up to mult of 4 (ULONG) */
   HunkSize=(len-0x20)>>2;

   NumStr+=9;         /* account for 9 offsets needed for keymap and name */
   len+=(NumStr<<2);  /* allocate space for relocation table in buffer */
   len+=20;           /* fixed size for reloc32_hunk stuff */
   cbuf=(UBYTE *)AllocMem(len,0L);
   if(cbuf==0)
   {
      ClearStr(120);
      FreeKeyMap(4);
      abort=1;
      return;
   }
   buffer=(ULONG *)cbuf;
   string=0x02C4;
   if(buffer==0L)
   {
      puts("OH NO... not enough space for temp buffer\n");
      exit(104);
   }
   buffer[0]=0x000003F3;
   buffer[1]=0;
   buffer[2]=1;
   buffer[3]=buffer[4]=0;
   buffer[5]=HunkSize;
   buffer[6]=0x000003E9;
   buffer[7]=HunkSize;
   buffer[8]=buffer[9]=buffer[10]=0;
   /* handle Node name */
   buffer[HunkSize+7]=0; /* zero pad name at end */
   buffer[11]=(name-0x20)<<16;
   strcpy(cbuf+name,filestring);
   strcat(cbuf+name,"/Serial No");
   {UBYTE *c; c=cbuf+name; while(*c!='/')c++;*c=0;}
   buffer[12]=0x004C0000;  /* KeyMap at constant offsets */
   buffer[13]=0x00C40000;
   buffer[14]=0x002E0000;
   buffer[15]=0x003D0000;
   buffer[16]=0x008C0000;
   buffer[17]=0x01C40000;
   buffer[18]=0x00360000;
   buffer[19]=0x00450000;
   for(ind=0;ind<15;ind++) /* Both Lo and Hi Cap & Rep */
   {
      cbuf[ind+0x4E]=LoCap[ind];
      cbuf[ind+0x5D]=LoRep[ind];
   }
/* get ready for string relocation if needed */
   buffer[HunkSize+8]=0x03EC;
   buffer[HunkSize+9]=NumStr;
   buffer[HunkSize+10]=0;
   StrOffset=HunkSize+NumStr+1;
   buffer[StrOffset+1]=0x002A;
   buffer[StrOffset+2]=0x0026;
   buffer[StrOffset+3]=0x0022;
   buffer[StrOffset+4]=0x001E;
   buffer[StrOffset+5]=0x001A;
   buffer[StrOffset+6]=0x0016;
   buffer[StrOffset+7]=0x0012;
   buffer[StrOffset+8]=0x000E;
   buffer[StrOffset+9]=0x000A;
   buffer[StrOffset+10]=0;
   buffer[StrOffset+11]=0x03F2;

   for(ind=0;ind<120;ind++) /* Both Lo and Hi maps */
   {
      cbuf[ind+0x6C]=LoTypes[ind];
      if(LoTypes[ind]&0x40)
      {
         buffer[ind+0x39]=string-0x20;
         told=(UBYTE *)LoMap[ind];
         tlen=DLength(told);
         for(i=0;i<tlen;i++) cbuf[string+i]=told[i]; /* assuming contiguous*/
         buffer[StrOffset]=(ind<<2)+0xE4-0x20;
         --StrOffset;
         string+=tlen;
      }
      else
         buffer[ind+0x39]=LoMap[ind];
   }

   fd=(long *)Open(strng,MODE_NEWFILE);
   if (fd==0L)
   {
      FreeMem(cbuf,len);
      return(0);
   }
   Write(fd,cbuf,len);
   Close(fd);
   FreeMem(cbuf,len);
   return(1);
}

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;
}
