#include <intuition/intuition.h>
#include <libraries/dosextens.h>
#include <exec/memory.h>
#include "reqdir.h"
#include "req.h"

typedef struct
{
short x0, y0, x1, y1;
} Rect;
typedef struct
{
char *line;
long len, x, y, color;
} Txt;

typedef struct
{
struct TextFont *font;
struct Window *window;
struct RastPort *rp;
struct MsgPort *port;
PFV func;
struct Gadget *gad;
short countdown;
short gfx_open;
short intui_open;
short finished;
UWORD top;
UWORD left;
UWORD width;
UWORD most;
UBYTE ubuf[512];
UBYTE pbuf[512];
UBYTE fbuf[512];
char scr_buf[6][28];
UBYTE *buffer;
Entry **ent;
long bufsize;
long max_entries;
long num_entries;
FileReq *req;
} Base;

extern void shadow_area(), render_text(), border_area(), center_title();
extern void write_screen(), init_dir(), pathgad(), close_libs();
extern void cancel(), vert_scroll(), scrollh(), right_htik(), left_htik();
extern void down_vtik(), up_vtik(), filegad(), okay_pressed();
extern void *strcpy(), *strncpy(), *AllocMem(), *trim();
extern short init();
extern long reqdir(), TextLength(), Wait(), process_message();
extern struct Library *OpenLibrary();
extern struct ViewPort *ViewPortAddress();
extern struct Window *OpenWindow();
extern struct TextFont *OpenFont();
extern struct Message *GetMsg();

extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase *GfxBase;

static UWORD NaturalColors[NUM_COLORS] =
   {
   0x2,
   0xeee,
   0x44f,
   0xd00,
   };

static struct TextAttr _ta =
{
(STRPTR) "topaz.font",
8,
FS_NORMAL,
FPF_ROMFONT,
};

#define HV GADGHCOMP
#define HI (GADGIMAGE|GADGHNONE)
#define HC (GADGIMAGE|GADGHCOMP)
#define RELV RELVERIFY
#define BOOLG BOOLGADGET
#define BACT (RELVERIFY|GADGIMMEDIATE)
#define PGAD PROPGADGET
#define PACT (RELVERIFY|GADGIMMEDIATE|FOLLOWMOUSE)
#define SGAD STRGADGET

extern struct Image UpImg, DownImg, LeftImg, RightImg, vi, hi;

static struct PropInfo p[] =
{
{FREEVERT|PROPBORDERLESS,0,0,0xffff,0x0fff,0,0,0,0,0,0,},
{FREEVERT|PROPBORDERLESS,0,0,0xffff,0xfff,0,0,0,0,0,0,},
{FREEHORIZ|PROPBORDERLESS,0,0,0xfff,0xffff,0,0,0,0,0,0,},
};

static struct StringInfo sin[] =
{
{NULL,NULL,0,512,0,0,0,0,0,0,0L,0L,0L},
{NULL,NULL,0,512,0,0,0,0,0,0,0L,0L,0L},
};

static struct Gadget g[] =
{
{&g[1],6,11,17,7,HC,BACT,BOOLG,(APTR)&UpImg,0L,0L,0L,0L,1,(APTR)up_vtik,},
{&g[2],254,11,17,7,HC,BACT,BOOLG,(APTR)&UpImg,0L,0L,0L,0L,2,(APTR)up_vtik,},
{&g[3],6,68,17,7,HC,BACT,BOOLG,(APTR)&DownImg,0L,0L,0L,0L,5,(APTR)down_vtik,},
{&g[4],254,68,17,7,HC,BACT,BOOLG,(APTR)&DownImg,0L,0L,0L,0L,6,(APTR)down_vtik,},
{&g[5],29,77,15,9,HC,BACT,BOOLG,(APTR)&LeftImg,0L,0L,0L,0L,7,(APTR)left_htik,},
{&g[6],232,77,15,9,HC,BACT,BOOLG,(APTR)&RightImg,0L,0L,0L,0L,9,(APTR)right_htik,},
{&g[7],218,90,53,11,HV,RELV,BOOLG,0L,0L,0L,0L,0L,12,(APTR)okay_pressed,},
{&g[8],218,104,53,11,HV,RELV,BOOLG,0L,0L,0L,0L,0L,13,(APTR)cancel,},
{&g[9],7,21,15,44,HI,PACT,PGAD,(APTR)&vi,0L,0L,0L,(APTR)&p[0],3,(APTR)vert_scroll,},
{&g[10],255,21,15,44,HI,PACT,PGAD,(APTR)&vi,0L,0L,0L,(APTR)&p[1],4,(APTR)vert_scroll,},
{&g[11],48,78,180,7,HI,PACT,PGAD,(APTR)&hi,0L,0L,0L,(APTR)&p[2],8,(APTR)scrollh,},
{&g[12],55,94,140,10,HV,RELV,SGAD,0L,0L,0L,0L,(APTR)&sin[0],10,(APTR)pathgad,},
{&g[13],55,104,140,10,HV,RELV,SGAD,0L,0L,0L,0L,(APTR)&sin[1],11,NULL,},

{&g[14],30,12,217,10,HV,RELV,BOOLG,0L,0L,0L,0L,0L,0,(APTR)filegad,},
{&g[15],30,22,217,10,HV,RELV,BOOLG,0L,0L,0L,0L,0L,1,(APTR)filegad,},
{&g[16],30,32,217,10,HV,RELV,BOOLG,0L,0L,0L,0L,0L,2,(APTR)filegad,},
{&g[17],30,42,217,10,HV,RELV,BOOLG,0L,0L,0L,0L,0L,3,(APTR)filegad,},
{&g[18],30,52,217,10,HV,RELV,BOOLG,0L,0L,0L,0L,0L,4,(APTR)filegad,},
{NULL,30,62,217,10,HV,RELV,BOOLG,0L,0L,0L,0L,0L,5,(APTR)filegad,},
};


static struct NewWindow _nw =
{
20, 20, 282, 122,
(UBYTE) 1,
(UBYTE) 1,
MOUSEMOVE | GADGETDOWN | GADGETUP | RAWKEY | ACTIVEWINDOW |
INTUITICKS | REFRESHWINDOW,
BORDERLESS | ACTIVATE | SIMPLE_REFRESH | WINDOWDRAG,
NULL,       /* gadgets */
0L,            /* check mark */
0L,            /* title */
0L,            /* screen */
0L,
282, 122,
282, 122,
WBENCHSCREEN,
};


long file_requester(r)
REGS FileReq *r;
{
REGS struct IntuiMessage *imsg;
REGS ULONG signals, mysig;
Base *base;
long alloc_size = 0L, rv = FILEREQ_ERROR;
struct IntuiMessage I;

/* Allocate temporary data storage */
if( !(base = AllocMem((long)sizeof(Base),(long)MEMF_CLEAR)) )
   return(rv);

/* If no buffer is provided attempt to allocate memory for it. */
if( !r->buf )
   {
   if( !(r->buf = AllocMem(r->bufsize,(long)MEMF_CLEAR)) )
      {
      FreeMem(base,(long)sizeof(Base));
      return(rv);
      }
   alloc_size = r->bufsize;
   }

base->ent = (Entry **)r->buf;
base->buffer = r->buf + (r->n_entries * sizeof(Entry *));
base->bufsize = r->bufsize - (r->n_entries * sizeof(Entry *));
base->max_entries = r->n_entries;
base->req = r;

strcpy(base->pbuf,r->path);
strcpy(base->fbuf,r->file);

if( _nw.Screen = r->screen )
   _nw.Type = CUSTOMSCREEN;
else
   _nw.Type = WBENCHSCREEN;


/* The requested number of entries has to fit in the
 * buffer or it is an error. */
if( base->bufsize >= (r->n_entries * sizeof(Entry)) )
   {
   if( init(base) )
      {

      /*** The main IO loop ***/

      for(rv = CANCEL, mysig = (1L<<base->port->mp_SigBit);!base->finished;)
         {
         signals = Wait( mysig | r->user_signals );

         if( signals & mysig )
            {
            while( imsg = (struct IntuiMessage *)GetMsg(base->port) )
               {
               I = *imsg;
               ReplyMsg(imsg);
               process_message(base,&I);
               }
            }

         if( signals & r->user_signals )
            if( r->user_func )
               (*r->user_func)(signals);
         }
      }
   }

/* close up some stuff */
if( base->window )
   CloseWindow(base->window);
if( base->font )
   CloseFont(base->font);
close_libs(base);

if( base->finished == OKAY )
   rv = OKAY;

if( alloc_size )
   {
   FreeMem(r->buf,alloc_size);
   r->buf = 0L;
   }

FreeMem(base,(long)sizeof(Base));

return(rv);
}


static void draw_all(base,flg)
REGS Base *base;
long flg;
{
static Rect file_area = {31,12,246,73};
static Rect left_area = {7,20,21,65};
static Rect right_area = {255,20,269,65};
static Rect bottom_area = {46,78,229,84};
static Rect path_area = {7,90,200,114};
static Rect ok_area = {219,91,268,99};
static Rect can_area = {219,105,268,113};
static Rect area_path = {52,92,194,102};
static Rect area_file = {52,102,194,112};
static Txt path_txt = {"Path:",5L,11L,100L,1L};
static Txt file_txt = {"File:",5L,11L,110L,1L};
static Txt okay_txt = {"Okay",4L,229L,98L,1L};
static Txt can_txt = {"Cancel",6L,221L,112L,0L};

REGS struct RastPort *rp = base->rp;

if( flg )
   BeginRefresh(base->window);

SetRast(rp,1L);
center_title(base);
shadow_area(rp,file_area,1L,0L);
shadow_area(rp,left_area,1L,0L);
shadow_area(rp,right_area,1L,0L);
shadow_area(rp,bottom_area,1L,0L);
shadow_area(rp,path_area,2L,0L);
shadow_area(rp,ok_area,2L,0L);
shadow_area(rp,can_area,3L,0L);
render_text(rp,&path_txt);
render_text(rp,&file_txt);
render_text(rp,&okay_txt);
render_text(rp,&can_txt);
border_area(rp,area_path,0L,1L);
border_area(rp,area_file,0L,1L);
if( flg )
   {
   write_screen(base);
   RefreshGList(g,base->window,0L,-1);
   EndRefresh(base->window,1L);
   }
}

static void close_libs(base)
REGS Base *base;
{
if( !base->intui_open && IntuitionBase )
   CloseLibrary(IntuitionBase);
if( !base->gfx_open && GfxBase )
   CloseLibrary(GfxBase);
}

static long open_libs(base)
REGS Base *base;
{

if( !GfxBase )
   {
   GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",33L);
   base->gfx_open = 0;
   }
else base->gfx_open = 1;
if( !GfxBase )
   return(0L);

if( !IntuitionBase )
   {
   IntuitionBase = (struct IntuitionBase *)
   OpenLibrary("intuition.library",33L);
   base->intui_open = 0;
   }
else base->intui_open = 0;
if( !IntuitionBase )
   return(0L);

return(1L);
}

static void end(base,code)
REGS Base *base;
short code;
{
base->finished = code;
if( code == OKAY )
   {
   strcpy(base->req->path,base->pbuf);
   strcpy(base->req->file,base->fbuf);
   }
}


static short init(base)
REGS Base *base;
{
if( open_libs(base) )
   {
   if( base->font = OpenFont(&_ta) )
      {
      if( base->window = OpenWindow(&_nw) )
         {
         if( base->req->flags & NATURAL_COLOR_BIT ) /* use the natural colors */
            {
            REGS struct ViewPort *vp = ViewPortAddress(base->window);
            LoadRGB4(vp,NaturalColors,NUM_COLORS);
            }

         /* Attach the buffers to the gadgets */
         sin[0].Buffer = base->pbuf;
         sin[1].Buffer = base->fbuf;
         sin[0].UndoBuffer = sin[1].UndoBuffer = base->ubuf;

         base->rp = base->window->RPort;
         base->port = base->window->UserPort;
         SetFont(base->rp,base->font);
         draw_all(base,0L);
         if( !base->pbuf[0] )
            init_dir(base->pbuf,(UWORD)(base->req->flags & CLI_BIT));
         AddGList(base->window,g,0,-1,0L);
         RefreshGList(g,base->window,0L,-1);
         pathgad(base,NULL);
         }
      else return(0);
      }
   else return(0);
   }
else return(0);

return(1);
}


static long process_message(base,i)
REGS Base *base;
REGS struct IntuiMessage *i;
{
REGS long rv = 0L;

switch( i->Class )
   {
   case MOUSEMOVE:
      if( base->func )
        (*base->func)(base,i);
      break;
   case INTUITICKS:
      if( base->func )
         {
         if( base->gad->Flags & SELECTED )
            {
            if( !base->countdown )
               (*base->func)(base,i);
            else base->countdown--;
            }
         }
      break;
   case RAWKEY:
      break;
   case GADGETDOWN:
      base->gad = (struct Gadget *)i->IAddress;
      if( base->func = (PFV) base->gad->UserData )
         (*base->func)(base,i);
      base->countdown = 4;
      break;
   case GADGETUP:
      base->gad = (struct Gadget *)i->IAddress;
      if( base->func = (PFV) base->gad->UserData )
         (*base->func)(base,i);
      base->func = 0L;
      base->gad = 0L;
      base->countdown = 0;
      break;
   case ACTIVEWINDOW:
      center_title(base);
      break;
   case REFRESHWINDOW:
      draw_all(base,1L);
      break;

   default: break;
   }

return(rv);
}

static void center_title(base)
REGS Base *base;
{

REGS struct RastPort *rp = base->rp;
REGS short text_width, text_length, x;

if( text_length = strlen(base->req->heading) )
   {
   text_width = TextLength(rp,base->req->heading,(long)text_length);
   x = (base->window->Width - text_width) >> 1;
   SetAPen(rp,2L);
   SetBPen(rp,1L);
   Move(rp,(long)x,8L);
   Text(rp,base->req->heading,(long)text_length);
   }
}

static void cancel(base,i)
REGS Base *base;
REGS struct IntuiMessage *i;
{
end(base,CANCEL);
}

static UWORD write_entry(base,e,buf)
REGS Base *base;
REGS Entry *e;
REGS char *buf;
{

REGS char *pr;
REGS UWORD len;
char tmp_buf[130];
char spc[6];

if( e->dir )
   pr = "-Dir-";
else
   {
   spc[0] = e->archive ? 'a' : '.';
   spc[1] = e->read ? 'r' : '.';
   spc[2] = e->write ? 'w' : '.';
   spc[3] = e->execute ? 'e' : '.';
   spc[4] = e->delete ? 'd' : '.';
   spc[5] = '\0';
   pr = spc;
   }
len = sprintf(tmp_buf,"%-10s %-5s %02d-%02d-%02d",
                EntryName(e),pr,
                e->month,e->day,e->year+78);

if( len > base->width ) base->width = len;
if( (len = len-base->left) > 27 ) len = 27;

strncpy(buf,&tmp_buf[base->left],len);
return(len);
}

static void entries_write(base,lens)
REGS Base *base;
UWORD lens[];
{
REGS UWORD i, j;
for( i = base->top, j = 0, base->width = 27;
     j < 6 && i < base->num_entries; i++, j++)
     lens[j] = write_entry(base,base->ent[i],base->scr_buf[j]);
base->most = base->width - 27;
}

static void write_screen(base)
REGS Base *base;
{
static Rect wipe_area = {31L,12L,246L,72L};
REGS UWORD i, j;
UWORD lens[6];

entries_write(base,lens);

if( base->left > base->most )
   {
   base->left = base->most;
   entries_write(base,lens);
   }

SetAPen(base->rp,1L);
RectFill(base->rp,(long)wipe_area.x0,(long)wipe_area.y0,
                 (long)wipe_area.x1,(long)wipe_area.y1);
SetAPen(base->rp,2L);
SetBPen(base->rp,1L);

for( i = 0, j = 19; i < 6 && i < base->num_entries; i++, j+=10)
   {
   Move(base->rp,31L,(long)j);
   Text(base->rp,base->scr_buf[i],(long)lens[i]);
   }
}

static void calc_vpot(base)
REGS Base *base;
{
REGS struct PropInfo *pa, *pb;
REGS UWORD most = base->num_entries - 6;
REGS ULONG pot, body;
REGS UWORD old_pot;

pa = (struct PropInfo *)g[8].SpecialInfo;
pb = (struct PropInfo *)g[9].SpecialInfo;
old_pot = pa->VertPot;

if( most <= 0 )
   {
   pa->VertPot = 0;
   pa->VertBody = 0xFFFF;
   }
else
   {
   pot = base->top;
   pot *= 0xFFFF;
   pot /= most;
   body = 6 * 0xFFFF;
   body /= base->num_entries;

   pa->VertPot = pot;
   pa->VertBody = body;
   }

pb->VertBody = pa->VertBody;
pb->VertPot = pa->VertPot;

if( pot != old_pot )
   RefreshGList(&g[8],base->window,0L,2);
}


static void calc_hpot(base)
REGS Base *base;
{
REGS ULONG pot;
REGS struct PropInfo *pi = (struct PropInfo *)g[10].SpecialInfo;
REGS UWORD old_pot;

old_pot = pi->HorizPot;

if( base->most )
   {
   pot = base->left;
   pot *= 0xFFFF;
   pot /= base->most;
   pi->HorizPot = pot;
   pi->HorizBody = 0xFFFF/base->width;
   pi->HorizBody *= 27;
   }
else pi->HorizPot = 0;

if( old_pot != pi->HorizPot )
   RefreshGList(&g[10],base->window,0L,1);
}


static void vert_scroll(base,i)
REGS Base *base;
REGS struct IntuiMessage *i;
{
REGS struct Gadget *a, *b;
REGS long top, range;

if( i->Class == GADGETUP )
   ModifyIDCMP(base->window,base->window->IDCMPFlags | INTUITICKS);
else if( i->Class == GADGETDOWN )
   ModifyIDCMP(base->window,base->window->IDCMPFlags & ~INTUITICKS);

a = base->gad;
b = (a == &g[8]) ? &g[9] : &g[8];

if( ((struct PropInfo *)b->SpecialInfo)->VertPot !=
    ((struct PropInfo *)a->SpecialInfo)->VertPot )
   {
   ((struct PropInfo *)b->SpecialInfo)->VertPot =
   ((struct PropInfo *)a->SpecialInfo)->VertPot;
   RefreshGList(b,base->window,0L,1);

   top = ((struct PropInfo *)b->SpecialInfo)->VertPot;

   range = base->num_entries - 6;
   if( range <= 0 )
      return;
   top *= range;
   top /= 0xFFFF;

   if( top != base->top )
      {
      base->top = top;
      write_screen(base);
      calc_hpot(base);
      }
   }
}


static void up_vtik(base,i)
REGS Base *base;
REGS struct IntuiMessage *i;
{

if( base->top > 0 && i->Class != GADGETDOWN )
   {
   base->top--;
   write_screen(base);
   calc_vpot(base);
   }
}

static void down_vtik(base,i)
REGS Base *base;
REGS struct IntuiMessage *i;
{

if( base->top < (base->num_entries - 6) && i->Class != GADGETDOWN )
   {
   base->top++;
   write_screen(base);
   calc_vpot(base);
   }
}

static void up_one_dir(base,path,len)
REGS Base *base;
UBYTE *path;
REGS UWORD len;
{

REGS UBYTE *end;

if( len )
   {
   end = path+(len-1);

   while( end >= path )
      {
      if( *end == '/' )
         {
         *end = '\0';
         return;
         }
      else if( *end == ':' )
         {
         *(end+1) = '\0';
         return;
         }
      end--;
      }

   *path = '\0';
   }
}


static void pathgad(base,i)
REGS Base *base;
REGS struct IntuiMessage *i;
{
long num_entries;

if( (num_entries = reqdir(trim(base->pbuf),base->buffer,
    (ULONG)base->bufsize,base->ent,(ULONG)base->max_entries)) > 0L )
   {
   base->num_entries = num_entries;
   base->top = base->left = 0;
   write_screen(base);
   calc_hpot(base);
   calc_vpot(base);
   }
else if( num_entries < 0L )
   {
   strcpy(base->pbuf,base->ubuf);
   RefreshGList(&g[11],base->window,0L,1);
   }
}

static void filegad(base,i)
REGS Base *base;
REGS struct IntuiMessage *i;
{
REGS Entry *e;
REGS UWORD len;
REGS struct StringInfo *si;

e = base->ent[base->top + base->gad->GadgetID];

if( e->dir )
   {
   len = strlen(trim(base->pbuf));

   if( strcmp("/",EntryName(e)) )
      {
      if( len )
         {
         if( base->pbuf[len-1] != ':' )
            strcat(base->pbuf,"/");
         }
      strcat(base->pbuf,EntryName(e));
      }
   else
      {
      si = (struct StringInfo *)g[11].SpecialInfo;
      si->DispPos = 0;
      up_one_dir(base,base->pbuf,len);
      }

   RefreshGList(&g[11],base->window,0L,1);
   pathgad(base,NULL);
   }
else
   {
   strcpy(base->fbuf,EntryName(e));
   RefreshGList(&g[12],base->window,0L,1);
   }
}


static void scrollh(base,i)
REGS Base *base;
REGS struct IntuiMessage *i;
{
REGS struct PropInfo *pi;
REGS ULONG pos;

pi = (struct PropInfo *)base->gad->SpecialInfo;

if( i->Class == GADGETUP )
   ModifyIDCMP(base->window,base->window->IDCMPFlags | INTUITICKS);
else if( i->Class == GADGETDOWN )
   ModifyIDCMP(base->window,base->window->IDCMPFlags & ~INTUITICKS);

if( base->most && i->Class != GADGETDOWN )
   {
   pos = base->most;
   pos *= pi->HorizPot;
   pos /= 0xFFFF;
   if( pos != base->left )
      {
      base->left = pos;
      write_screen(base);
      }
   }
}

static void right_htik(base,i)
REGS Base *base;
REGS struct IntuiMessage *i;
{

if( base->left < base->most && i->Class != GADGETDOWN )
   {
   base->left++;
   write_screen(base);
   calc_hpot(base);
   }

}

static void left_htik(base,i)
REGS Base *base;
REGS struct IntuiMessage *i;
{

if( base->left > 0 && i->Class != GADGETDOWN )
   {
   base->left--;
   write_screen(base);
   calc_hpot(base);
   }

}

static void okay_pressed(base,i)
REGS Base *base;
struct IntuiMessage *i;
{
end(base,OKAY);
}


static void shadow_area(rp,r,front_color,outline_color)
REGS struct RastPort *rp;
Rect r;
REGS long front_color, outline_color;
{
SetAPen(rp,(long)front_color);
RectFill(rp,(long)r.x0,(long)r.y0,(long)r.x1,(long)r.y1);

SetAPen(rp,(long)outline_color);
r.x0--;
r.y0--;
r.x1++;
r.y1++;
Move(rp,(long)r.x0,(long)r.y0);
Draw(rp,(long)r.x0,(long)r.y1);
Draw(rp,(long)r.x1,(long)r.y1);
Draw(rp,(long)r.x1,(long)r.y0);
Draw(rp,(long)r.x0,(long)r.y0);
r.x0 += 3;
r.y0++;
r.x1++;
r.y1++;
Move(rp,(long)r.x0,(long)r.y1);
Draw(rp,(long)r.x1,(long)r.y1);
Draw(rp,(long)r.x1,(long)r.y0);
r.x1++;
Move(rp,(long)r.x1,(long)r.y0);
Draw(rp,(long)r.x1,(long)r.y1);
r.x1++;
Move(rp,(long)r.x1,(long)r.y0);
Draw(rp,(long)r.x1,(long)r.y1);
}

static void render_text(rp,t)
REGS struct RastPort *rp;
Txt *t;
{
SetAPen(rp,t->color);
SetDrMd(rp,(long)JAM1);
Move(rp,t->x,t->y);
Text(rp,t->line,t->len);
}

static void border_area(rp,r,front_color,outline_color)
REGS struct RastPort *rp;
Rect r;
long front_color, outline_color;
{
SetAPen(rp,front_color);
RectFill(rp,(long)r.x0,(long)r.y0,(long)r.x1,(long)r.y1);

SetAPen(rp,outline_color);
Move(rp,(long)r.x0,(long)r.y0);
Draw(rp,(long)r.x0,(long)r.y1);
Draw(rp,(long)r.x1,(long)r.y1);
Draw(rp,(long)r.x1,(long)r.y0);
Draw(rp,(long)r.x0,(long)r.y0);
r.x0++;
r.x1--;
Move(rp,(long)r.x0,(long)r.y0);
Draw(rp,(long)r.x0,(long)r.y1);
Move(rp,(long)r.x1,(long)r.y0);
Draw(rp,(long)r.x1,(long)r.y1);
}

