#ifndef XPKMASTER_PASSWORD_C
#define XPKMASTER_PASSWORD_C

/* Routinesheader

	Name:		password.c
	Main:		xpkmaster
	Versionstring:	$VER: password.c 1.5 (10.04.97)
	Author:		SDI
	Distribution:	PD
	Description:	password requester related things

 1.0   27.12.96 : first version
 1.1   28.12.96 : starting to code the stuff
 1.2   29.12.96 : optimized and removed bugs
 1.3   02.01.97 : corrected return handling
 1.4   01.03.97 : fixed tag parsing error
 1.5   10.04.97 : now old screen comes to front after Request
*/

#include <pragma/intuition_lib.h>
#include <pragma/exec_lib.h>
#include <pragma/utility_lib.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/sghooks.h>
#include <intuition/intuitionbase.h>
#include <xpk/xpk.h>
#include <xpk/xpkprefs.h>
#include "xpk_strings.h"
#include "xpkmaster.h"

#ifdef __MAXON__
  #define __asm
#endif

#define TESTSIZE	13

static struct PassCharData {
  ULONG Flag;
  UBYTE Lower;
  UBYTE Upper;
} TestField[TESTSIZE] = {
{XPKPASSFF_30x39,0x30,0x39}, {XPKPASSFF_41x46,0x41,0x46},
{XPKPASSFF_61x66,0x61,0x66}, {XPKPASSFF_47x5A,0x47,0x5A},
{XPKPASSFF_67x7A,0x67,0x7A}, {XPKPASSFF_20,   0x20,0x20},
{XPKPASSFF_SPECIAL7BIT,0x21,0x2F},
{XPKPASSFF_SPECIAL7BIT,0x3A,0x40},
{XPKPASSFF_SPECIAL7BIT,0x5B,0x60},
{XPKPASSFF_SPECIAL7BIT,0x7B,0x7F},
{XPKPASSFF_C0xDE,0xC0,0xDE}, {XPKPASSFF_DFxFF,0xDF,0xFF},
{XPKPASSFF_SPECIAL8BIT,0xA0,0xBF},
};

struct PassUserData {
  ULONG pud_Flags;
  UBYTE pud_Entered;
};

static ULONG __asm PassHookFunc(register __a0 struct Hook *hook,
register __a2 struct SGWork *sgw, register __a1 ULONG *msg)
{
// clear the time request here
  ((struct PassUserData *) sgw->Gadget->UserData)->pud_Entered = 1;

  if(*msg == SGH_KEY)
  {
    if((sgw->EditOp == EO_REPLACECHAR) || (sgw->EditOp == EO_INSERTCHAR))
    {
      register ULONG i, f = ((struct PassUserData *) sgw->Gadget->UserData)->pud_Flags;
      register UBYTE c = sgw->Code;

      for(i = 0; i < TESTSIZE; ++i)
      {
        if((f & TestField[i].Flag) && (c >= TestField[i].Lower) &&
        (c <= TestField[i].Upper))
          break;
      }
      if(i == TESTSIZE)
      {
        sgw->Actions |= SGA_BEEP; // let the screen beep
        sgw->Actions &= ~SGA_USE; // do not use char
      }
    }
    return ~0;
  }
  else
    return 0;
}

static struct Hook PassHook = { {0}, (ULONG (*) ()) PassHookFunc, 0, 0};

struct RequestData {
  ULONG			rd_MaxTicks;
  ULONG			rd_Time;
  STRPTR		rd_Title;
  STRPTR		rd_GivenBuffer;
  struct Screen *	rd_Screen;
  struct Screen *	rd_FirstScreen;
  struct Screen *	rd_SelfScreen;
  struct Window	*	rd_Window;
  struct DrawInfo *	rd_DrawInfo;
  struct PassUserData	rd_PassUserData;
  struct Gadget		rd_Gadget;
  struct StringInfo	rd_StringInfo;
  struct StringExtend	rd_StringExtend;
  UBYTE			rd_KeyBuffer[9];
  UBYTE			rd_ScreenTitle[80];
};

#define passbuf		rd->rd_StringInfo.Buffer
#define passsize	rd->rd_StringInfo.MaxChars

/* returns XPKERR codes */
static LONG DoRequest(struct RequestData *rd)
{
  LONG ticks = XPKERR_UNKNOWN;
  
  rd->rd_MaxTicks = rd->rd_Time << 3; /* *8, seems to be correct factor */
  *passbuf = 0;

  if(!rd->rd_Screen && !(rd->rd_Screen = rd->rd_SelfScreen = LockPubScreen(0)))
    return XPKERR_UNKNOWN;

  if((rd->rd_DrawInfo = GetScreenDrawInfo(rd->rd_Screen)))
  {
    rd->rd_Gadget.TopEdge	= rd->rd_Screen->WBorTop;
    rd->rd_Gadget.LeftEdge	= rd->rd_Screen->WBorLeft;
    rd->rd_Gadget.Width		= rd->rd_Screen->Width >> 1;
    rd->rd_Gadget.Height	= rd->rd_Screen->Font->ta_YSize;
    rd->rd_Gadget.Flags		= GFLG_GADGHNONE | GFLG_STRINGEXTEND;
    rd->rd_Gadget.Activation	= GACT_RELVERIFY | GACT_STRINGCENTER;
    rd->rd_Gadget.GadgetType	= GTYP_STRGADGET;
    rd->rd_Gadget.SpecialInfo	= &rd->rd_StringInfo;
    rd->rd_Gadget.UserData	= (APTR) &rd->rd_PassUserData;
    rd->rd_StringInfo.Extension	= &rd->rd_StringExtend;
    rd->rd_StringExtend.Pens[0] =
    rd->rd_StringExtend.Pens[1] =
    rd->rd_StringExtend.ActivePens[0] =
    rd->rd_StringExtend.ActivePens[1] = rd->rd_DrawInfo->dri_Pens[BACKGROUNDPEN];
    rd->rd_StringExtend.EditHook      = &PassHook;
    rd->rd_StringExtend.WorkBuffer    =
      AllocMem(passsize, MEMF_ANY|MEMF_CLEAR);

    if((rd->rd_Window = OpenWindowTags(0,
	WA_Left,	(rd->rd_Screen->Width  - rd->rd_Gadget.Width  -
			rd->rd_Screen->WBorLeft - rd->rd_Screen->WBorRight)>>1,
	WA_Top,		(rd->rd_Screen->Height - rd->rd_Gadget.Height -
			rd->rd_Screen->WBorTop  - rd->rd_Screen->WBorBottom)>>1,
	WA_InnerWidth,	rd->rd_Gadget.Width,
	WA_InnerHeight,	rd->rd_Gadget.Height,
	WA_Gadgets,	&rd->rd_Gadget,
	WA_IDCMP,	IDCMP_GADGETUP | IDCMP_ACTIVEWINDOW | IDCMP_INTUITICKS | IDCMP_RAWKEY,
	WA_Flags,	WFLG_ACTIVATE | WFLG_RMBTRAP,
	WA_PubScreen,	rd->rd_Screen,
	WA_PubScreenFallBack,	TRUE,
	(rd->rd_Title ? WA_ScreenTitle : TAG_IGNORE), rd->rd_Title,
	TAG_DONE)))
    {
      struct IntuiMessage *msg;
      BOOL stop = 0;

      ticks = 0;
      rd->rd_FirstScreen = IntuitionBase->FirstScreen;
      ScreenToFront(rd->rd_Screen);
      while(!stop)
      {
	WaitPort(rd->rd_Window->UserPort);
	while(!stop && (msg = (struct IntuiMessage *)
	GetMsg(rd->rd_Window->UserPort)))
	{
	  if(msg->Class == IDCMP_INTUITICKS)
	    ++ticks;
	  else
	    ticks = 0;

	  switch(msg->Class)
	  {
	  case IDCMP_ACTIVEWINDOW:
	    ActivateGadget(&rd->rd_Gadget, rd->rd_Window, 0); break;
	  case IDCMP_GADGETUP: stop = 1; break;
	  case IDCMP_RAWKEY: if(msg->Code = 0x45) stop = 2; break; /* ESC */
          }
	  ReplyMsg((struct Message *) msg);
	}
	if(rd->rd_PassUserData.pud_Entered)
	{
	  rd->rd_PassUserData.pud_Entered = 0; ticks = 0;
	}
        if(((ticks >= rd->rd_MaxTicks) && rd->rd_Time))
          stop = 2;
      }
      ticks = (stop > 1 || !*passbuf) ? XPKERR_ABORTED : XPKERR_OK;
      CloseWindow(rd->rd_Window);
      if(rd->rd_FirstScreen != rd->rd_Screen)
      {
        struct Screen *sc;
        Forbid();
        sc = IntuitionBase->FirstScreen;
        while((sc = sc->NextScreen) && sc != rd->rd_FirstScreen)
          ;
        if(sc)
          ScreenToFront(sc);
        Permit();
      }
    }
    FreeScreenDrawInfo(rd->rd_Screen, rd->rd_DrawInfo);
  }

  if(rd->rd_SelfScreen)
    UnlockPubScreen(0, rd->rd_SelfScreen);
  if(rd->rd_StringExtend.WorkBuffer)
    FreeMem(rd->rd_StringExtend.WorkBuffer, passsize);

  return ticks;
}

#ifdef __cplusplus
  extern "C"
#endif

LONG __asm LIBXpkPassRequest(register __a0 struct TagItem *ti)
{
  register struct RequestData *rd;
  LONG err = 2, mode = 0;
  struct TagItem *tags = ti;

#ifdef DEBUG
  DebugTagList("XpkPassRequest", tags);
#endif

  if(!(rd = AllocMem(sizeof(struct RequestData), MEMF_PUBLIC|MEMF_CLEAR)))
    return XPKERR_NOMEM;

/* set defaults */
  rd->rd_Time = 120;
  rd->rd_PassUserData.pud_Flags = XPKPASSFLG_PRINTABLE;

  while((ti = NextTagItem(&tags)))
  {
    switch(ti->ti_Tag)
    {
    case XPK_PassChars: rd->rd_PassUserData.pud_Flags = ti->ti_Data; break;
    case XPK_PasswordBuf:
      rd->rd_GivenBuffer = (STRPTR) ti->ti_Data; mode += 10; break;
    case XPK_PassBufSize: passsize = ti->ti_Data; break;
    case XPK_Key16BitPtr:
      rd->rd_GivenBuffer = (STRPTR) ti->ti_Data; mode += 11; break;
    case XPK_Key32BitPtr:
      rd->rd_GivenBuffer = (STRPTR) ti->ti_Data; mode += 12; break;
    case XPK_PubScreen: rd->rd_Screen = (struct Screen *) ti->ti_Data; break;
    case XPK_PassTitle: err |= 1; rd->rd_Title = (STRPTR) ti->ti_Data; break;
    case XPK_TimeOut: err &= ~2; rd->rd_Time = ti->ti_Data; break;
    case XPK_Preferences: if(!ti->ti_Data) err &= ~2; break;
    };
  }

  if(!mode || (mode > 12) || (mode == 10 && !passsize) ||
  !rd->rd_GivenBuffer)
  {
    FreeMem(rd, sizeof(struct RequestData));
    return XPKERR_BADPARAMS;
  }

  if(err&2) // call the preferences
  {
    struct XpkPrefsSemaphore *sem;

    if((sem = GetPrefsSem()))
    {
      if(sem->xps_MainPrefs)
        rd->rd_Time = sem->xps_MainPrefs->xmp_Timeout;
      ReleaseSemaphore((struct SignalSemaphore *) sem);
    }
  }

  if(!(err&1)) /* create title text */
  {
    if(mode == 10)
      rd->rd_Title = strings[TXT_REQ_PASSWORD];
    else
    {
      rd->rd_Title = rd->rd_ScreenTitle;
      sprintf(rd->rd_ScreenTitle, strings[TXT_REQ_KEY], (mode == 11 ? 16 : 32));
    }
  }

  if(mode > 10)
  {
    passsize = (mode == 11 ? 5 : 9);
    passbuf = rd->rd_KeyBuffer;
    rd->rd_PassUserData.pud_Flags = XPKPASSFLG_HEXADECIMAL;
  }
  else
    passbuf = rd->rd_GivenBuffer;
  
  if(!(err = DoRequest(rd)))
  {
    if(mode == 11)
      *((UWORD *) rd->rd_GivenBuffer) = strtoul(passbuf, 0, 16);
    else if(mode == 12)
      *((ULONG *) rd->rd_GivenBuffer) = strtoul(passbuf, 0, 16);
  }

  FreeMem(rd, sizeof(struct RequestData));

  return err;
}

#endif	/* XPKMASTER_PASSWORD_C */

