#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/ports.h>
#include <exec/devices.h>
#include <exec/exec.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfxbase.h>
#include <graphics/gfx.h>
#include <graphics/text.h>
#include <graphics/regions.h>
#include <graphics/copper.h>
#include <graphics/gels.h>
#include <graphics/clip.h>
#include <graphics/view.h>
#include <graphics/rastport.h>
#include <graphics/layers.h>
#include <devices/serial.h>
#include <devices/keymap.h>
#include <hardware/blit.h>
#include <stdio.h>
#include <ctype.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>

extern APTR AllocMem();

unsigned int mask = 0;
#define INTUITION 0x00000001
#define GRAPHICS  0x00000002
#define SCREEN    0x00000004
#define WINDOW    0x00000008
#define COLORMAP  0x00000010
#define MATH      0x00000020
#define MATHTRANS 0x00000040

#define FBSIZE (sizeof (struct FileInfoBlock))

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct IntuiMessage *message;
struct RastPort *rp;
struct Window *w;

#define MAXFNAME 15
#define MAXCHAR 30

char File_name[MAXFNAME][MAXCHAR+1];

#define CURDIR_GADGET 5

#define MAXCD 64
char cd_buf[MAXCD+2];

USHORT str_pairs [] = { 0,0,  517,0,  517,9,  0,9,  0,0,};
struct Border str_border =
{
    -1,-1,              /* Leftedge, Topedge */
     1, 0, JAM1,        /* FrontPen, BackPen, Drawmode */
     5,                 /* Number of Pairs */
    (APTR) &str_pairs,      /* XY, Pointer to XY pairs */
    NULL,               /* More borders */
};

struct IntuiText Curdir_text = {2,0,JAM2,0,-9,NULL,"CURRENT DIRECTORY",NULL,};

struct StringInfo Curdir_str = 
{
	cd_buf,
    NULL,
	0,
	MAXCD,  
	0, 0, 0, 0,
	2,0,
	NULL,
	0,
	NULL,
};

struct Gadget Curdir_gad =
{
    NULL,
	21,159,
	516, 9,
    GADGHCOMP, GADGIMMEDIATE | RELVERIFY,
	STRGADGET,
    (APTR) &str_border,
	NULL,
	&Curdir_text,
	0,
	&Curdir_str,
	CURDIR_GADGET,
	NULL,
};

char cp_buf[MAXCD+1];

#define CPDIR_GADGET 7

struct IntuiText Cpdir_text = {2,0,JAM2,0,-9,NULL,"DESTINATION",NULL,};

struct StringInfo Cpdir_str = 
{
	cp_buf,
    NULL,
	0,
	MAXCD,  
	0, 0, 0, 0,
	2,0,
	NULL,
	0,
	NULL,
};

struct Gadget Cpdir_gad =
{
    &Curdir_gad,
	21,185,
	516, 9,
    GADGHCOMP, GADGIMMEDIATE | RELVERIFY,
	STRGADGET,
    (APTR) &str_border,
	NULL,
	&Cpdir_text,
	0,
	&Cpdir_str,
	CPDIR_GADGET,
	NULL,
};

/* SLIDE GADGET DEFINITION */
struct IntuiText slide_text =
{
    2,          /* Front Pen */
    2,          /* Back Pen */
    JAM1,       /* Draw Mode */
    -9,         /* Left Edge */
    2,          /* Top Edge */
    NULL,       /* Font */
    (UBYTE *) "SLIDE GADGET",   /* Actual text */
    NULL,
};
#define SLIDE_GADGET 0

struct Image slide_img;
struct PropInfo slide_prop;

struct Gadget Slide_gad =
{
    &Cpdir_gad,         /* Next gadget */
    20, 14,             /* Left, Top edge */
    20, (8*MAXFNAME)+4, /* Width Hieght */
    GADGHCOMP,          /* Flags */
    GADGIMMEDIATE       /* More flags */
    | RELVERIFY,
    PROPGADGET,         /* Gadget type */
    (APTR)&slide_img,   /* Border definition */
    NULL,               /* Select render */
    NULL,               /* Gadget text */
    0,                  /* MutualExclude */
    (APTR) &slide_prop, /* Special Info - proportional specification */
    SLIDE_GADGET,
    NULL,
};

USHORT Pairs [] = { 0,0,  81,0,  81,10,  0,10,  0,0,};
struct Border but_border =
{
    -1,-1,              /* Leftedge, Topedge */
     1, 0, JAM1,        /* FrontPen, BackPen, Drawmode */
     5,                 /* Number of Pairs */
    (APTR) Pairs,      /* XY, Pointer to XY pairs */
    NULL,               /* More borders */
};

/* Button Gadget IDs */
#define ALL_GADGET    10
#define CLEAR_GADGET  11
#define COPY_GADGET   12
#define DELETE_GADGET 13
#define PARENT_GADGET 14

#define BUT_FIRST ALL_GADGET
#define BUT_LAST  PARENT_GADGET

#define BUTWIDTH 8

struct IntuiText But_text[] =
{
	2,0,JAM2,0,0,NULL,"   ALL   ",NULL,
    2,0,JAM2,0,0,NULL,"  CLEAR  ",NULL,
    2,0,JAM2,0,0,NULL,"   COPY  ",NULL,
    2,0,JAM2,0,0,NULL,"  DELETE ",NULL,
    2,0,JAM2,0,0,NULL,"  PARENT ",NULL,
};

struct Gadget But_gad[] =
{
    NULL,0,175,80,9,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    (APTR)&but_border,NULL,NULL,NULL,NULL,ALL_GADGET,NULL,
    NULL,0,175,80,9,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    (APTR)&but_border,NULL,NULL,NULL,NULL,CLEAR_GADGET,NULL,
    NULL,0,175,80,9,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    (APTR)&but_border,NULL,NULL,NULL,NULL,COPY_GADGET,NULL,
    NULL,0,175,80,9,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    (APTR) &but_border,NULL,NULL,NULL,NULL,DELETE_GADGET,NULL,
    NULL,0,175,80,9,GADGHCOMP,GADGIMMEDIATE | RELVERIFY,BOOLGADGET,
	    (APTR) &but_border,NULL,NULL,NULL,NULL,PARENT_GADGET,NULL,
};

#define FIRST_GADGET But_gad[4]

USHORT Fil_pairs [] = { 40,15,  356,15,  356,138,  40,138, 40,15, };
struct Border Fil_border =
{
    -1,-1,              /* Leftedge, Topedge */
     1, 0, JAM1,        /* FrontPen, BackPen, Drawmode */
     5,                 /* Number of Pairs */
    (APTR) Fil_pairs,  /* XY, Pointer to XY pairs */
    NULL,               /* More borders */
};

#define WinIDCMP  CLOSEWINDOW | REFRESHWINDOW | GADGETUP | MOUSEBUTTONS

struct NewWindow nw =
{
    0,0,                /* Start position */
    640,200,            /* Width, hieght */
    0,1,                /* Detail, block pens */
	WinIDCMP,			/* IDCMP flags */
    WINDOWDEPTH
    | WINDOWDRAG
    | REPORTMOUSE
    | WINDOWCLOSE
    | ACTIVATE
    | SMART_REFRESH,
    &FIRST_GADGET,      /* First gadget in the list */
    NULL,               /* User checkmark */
    "Directory Utility by Chris Nicotra ",/* Window title */
    NULL,               /* Pointer to the screen */
    NULL,               /* Pointer to superbitmap */
    219, 16, 640, 200,  /* Ignored because not sizeable */
    WBENCHSCREEN,       /* Using the Workbench screen */
};


/* Dos structures and definitions */
extern struct FileLock *Lock();

/* The following structure is used to hold the directory entries */
struct DirTable
{
    char  dt_fname[108];         /* File name */
    short dt_select;             /* Select flag */
	short dt_dir;                /* Directory flag */
	long  dt_size;               /* File size */
};

int cmp_dt();

#define MAXFILE 300
struct DirTable dirtable[MAXFILE];
int numdir;
int cur_index;
char curdir[121];

main()
{
    ULONG MessageClass;
    USHORT code;
	struct Gadget *gad_ptr;
	struct IntuiText *text_ptr;
	int i;

	*cp_buf = 0;

    slide_prop.Flags = FREEVERT | AUTOKNOB;
    slide_prop.VertBody = 0;
    slide_prop.VertPot = 0x8000;

	/* Setup the button gadgets */
    for (i=0;i<(BUT_LAST-BUT_FIRST)+1;i++)
	{
		/* Point to the next gadget */
	    gad_ptr = &But_gad[i];
		text_ptr = &But_text[i];

		gad_ptr->LeftEdge = ((i%2) * 105) + 400;
		gad_ptr->TopEdge = ((i/2) * 20) + 30;

		if (i)
		    gad_ptr->NextGadget = &But_gad[i-1];
		else
		    gad_ptr->NextGadget = &Slide_gad;

		gad_ptr->GadgetText = text_ptr;

		text_ptr->LeftEdge = 1;
		text_ptr->TopEdge = 1;
	}

    /*********************************************************************
    *
    *    Open the libraries and set each "read" mask bit.
    *
    *********************************************************************/
    if (!(GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",0)))
    {
        printf("no graphics library!!!\n");
        close_things();
        exit(1);
    }

    mask |= GRAPHICS;

    if (!(IntuitionBase = (struct IntuitionBase *)
        OpenLibrary("intuition.library",0)))
    {
        printf("no intuition here!!\n");
        close_things();
        exit(2);
    }

    mask |= INTUITION;

    /*********************************************************************
    *
    *    Open the window on top of the workbench screen
    *
    *********************************************************************/
    if (!(w = (struct Window *) OpenWindow(&nw) ))
    {
        printf("Could not open the window\n");
        close_things();
        exit(3);
    }

    mask |= WINDOW;

    /*******************************************************************
    *
    *    Initialization before entering main loop
    *
    ******************************************************************/
    rp = w->RPort;
	DrawBorder(rp,&Fil_border,0,0);

    /* Load up the directory */
    getdir();
    new_dir();

    RefreshGadgets(&FIRST_GADGET,w,NULL);

    /* Main event loop */
    for (;;)
    {
        if (message = (struct IntuiMessage *) GetMsg(w->UserPort))
        {
            MessageClass = message->Class;
            code = message->Code;
            ReplyMsg(message);
            switch (MessageClass)
            {
            case GADGETUP:
            case GADGETDOWN:
                do_gadgets(message,w);
                break;

            case CLOSEWINDOW:
                close_things();
                exit (0);
                break;

            case MOUSEBUTTONS:
				if (code == SELECTDOWN)
				    sel_file();
                break;

            default:
                break;
            }
        }
    }
}

do_gadgets(mes,win)
struct IntuiMessage *mes;
struct Window *win;
{
    struct Gadget *igad;        /* Ptr to gadget that intuition found */
    int gadgid;                 /* ID Code identifying which gadget */

    igad = (struct Gadget *) mes->IAddress;     /* PTR to gadget */
    gadgid = igad->GadgetID;
    switch (gadgid)
    {
	case CURDIR_GADGET:
	    offIDCMP();
		nullterm(cd_buf,MAXCD);
		Curdir_str.NumChars = strlen(cd_buf);
		Curdir_str.BufferPos = strlen(cd_buf);
	    strcpy(curdir,cd_buf);
		getdir();
		new_dir();
	    onIDCMP();
		break;

	case CPDIR_GADGET:
		nullterm(cp_buf,MAXCD);
		Curdir_str.NumChars = strlen(cp_buf);
		Curdir_str.BufferPos = strlen(cp_buf);
	    break;

    case SLIDE_GADGET:
		rdis_files();
        break;

	case PARENT_GADGET:
		par_dir();
	    break;

    case DELETE_GADGET:
		del_files();
        break;

    case COPY_GADGET:
	    copy_files();
        break;

    case CLEAR_GADGET:
	    clr_files();
        break;

	case ALL_GADGET:
	    all_files();
	    break;

    default:
        break;
    }
}

close_things()
{
    if (mask & WINDOW)
        CloseWindow(w);

    if (mask & GRAPHICS)
        CloseLibrary(GfxBase);

    (void) OpenWorkBench();

    if (mask & INTUITION)
        CloseLibrary(IntuitionBase);
}

getdir()
{
	struct FileLock *filelock;
	char *pwd();

	if (!(*curdir))
	    strcpy(curdir,pwd());

	Curdir_str.NumChars = strlen(curdir);
	Curdir_str.BufferPos = strlen(curdir);
	strcpy(cd_buf,curdir);
    RefreshGadgets(&Curdir_gad,w,NULL);

	numdir = 0;

    if (!(filelock = Lock(curdir,ACCESS_READ)))
	    return;

	rd_files(filelock);
    UnLock(filelock);

    return;
}

rd_files(fl)
struct FileLock *fl;
{
	struct FileInfoBlock *fb;

    numdir = 0;

	/* Allocate the file info block */
	fb = AllocMem(FBSIZE,MEMF_CLEAR | MEMF_PUBLIC);

    if (!Examine(fl,fb))
	{
		FreeMem(fb,FBSIZE);
		close_things();
		exit();
	    return;
	}

	/* Loop through each of the files in this directory */
    while (ExNext(fl,fb))
    {
        if (fb->fib_DirEntryType < 0)
		    dirtable[numdir].dt_dir = 0;
		else
		    dirtable[numdir].dt_dir = -1;

		dirtable[numdir].dt_select = 0;
		dirtable[numdir].dt_size = fb->fib_Size;

		strcpy(dirtable[numdir++].dt_fname,fb->fib_FileName);

		if (numdir == MAXFILE)
		    break;
	}

	/* Sort the table */
	qsort(dirtable,numdir,(sizeof (struct DirTable)),cmp_dt);

	FreeMem(fb,FBSIZE);
}


rdis_files()
{
    USHORT Vpot;
	int numd;
	int pos;

	if (numdir > MAXFNAME)
	{
	    Vpot = slide_prop.VertPot;
		Vpot >>= 1;
		Vpot &= 0x7fff;

		numd = numdir - MAXFNAME;

		pos = ((float) Vpot / (float) 0x7fff) * numd;

		cur_index = pos;

		dis_files(pos);
	}
}


/**********************************************************************
*
*    new_dir - New directory
*
*    Purpose: To display a new directory and initialize the slide
*        gadget.
*
*    Parameters: None
*
*    Returns: Nothing
*
**********************************************************************/
new_dir()
{
	USHORT Vbody;

	if (numdir <= MAXFNAME)
	{
		Vbody = 0xffff;
	}
	else
	{
	    Vbody = ((float) 0x7fff) / (((float) numdir) / ((float) MAXFNAME));
		Vbody <<= 1;
	}

	cur_index = 0;

	ModifyProp(&Slide_gad,w,NULL,slide_prop.Flags,0,0,0,Vbody);

	dis_files(0);
}

/**********************************************************************
*
*    dis_files - Display files
*
*    Purpose: To display a page of file names
*
*    Parameters:
*        pos - The position in the file list
*
*    Returns: Nothin
*
**********************************************************************/
dis_files(pos)
int pos;
{
    int i;

	for (i=0;i<MAXFNAME;i++)
	    dis_name(i+pos,i);
}

/***********************************************************************
*
*    dis_name - Display a file name
*
*    Purpose: To display a file name
*
*    Parameters:
*        file - File index (into dirtable) or -1 for blank entry
*        pos - The position in the file list
* 
*    Returns: Nothing
*
**********************************************************************/
dis_name(file,pos)
int file;
int pos;
{
	static char file_name[100];

	static struct IntuiText file_text =
	{
	    2,0,JAM2,45,15,NULL,(UBYTE *) file_name,NULL
	};

	/* Set the top position */
	file_text.TopEdge = (pos*8)+16;

	if (file == -1 || file >= numdir)
	{
	    sprintf(file_name,"%-38.38s","");
		file_text.FrontPen = 1;
		file_text.BackPen = 0;
	}
	else
	{
		if (dirtable[file].dt_dir)
		{
		    sprintf(file_name,"%-30.30s%-8.8s",dirtable[file].dt_fname,"");

			if (dirtable[file].dt_select)
			{
			    file_text.FrontPen = 3;
				file_text.BackPen = 2;
			}
			else
			{
			    file_text.FrontPen = 3;
				file_text.BackPen = 0;
			}
		}
		else
		{
		    sprintf(file_name,"%-30.30s %7ld",dirtable[file].dt_fname,
			    dirtable[file].dt_size);

			if (dirtable[file].dt_select)
			{
				file_text.FrontPen = 1;
				file_text.BackPen = 2;
			}
			else
			{
				file_text.FrontPen = 1;
				file_text.BackPen = 0;
			}
		}
	}

	/* Display the text */
	PrintIText(rp,&file_text,0,0);
}

/**********************************************************************
*
*    sel_file - Select file
*
*    Purpose: To check if a file has been selected and the select
*        or unselect it
*
*    Parameters: None
*
*    Returns: Nothing
*
**********************************************************************/
sel_file()
{
	int file;
	char *ptr;

    if (message->MouseX < 45 || message->MouseX > 351)
	    return;

	if (message->MouseY < 16 || message->MouseY >= (MAXFNAME*8+16))
	    return;

    file = message->MouseY - 16;
	file /= 8;

	if (file > MAXFNAME)
	    return;

	if ((file+cur_index) > numdir)
	    return;

	if (dirtable[file+cur_index].dt_dir)
	{
		ptr = curdir + strlen(curdir) - 1;

	    sprintf(curdir,"%s%s%s",curdir,((*ptr == ':') ? "":"/"),
		    dirtable[file+cur_index].dt_fname);
		getdir();
		new_dir();
	}
	else
	{
	    dirtable[file+cur_index].dt_select += 1;
	    dirtable[file+cur_index].dt_select *= -1;

		dis_name(file+cur_index,file);
	}
}

/**********************************************************************
*
*    The following routines are called for each of the different
*    types of commands:
*
**********************************************************************/

clr_files()
{
    int i;

	for (i=0;i<numdir;i++)
	    dirtable[i].dt_select = 0;

	dis_files(cur_index);
}

all_files()
{
    int i;

	for (i=0;i<numdir;i++)
		if (!dirtable[i].dt_dir)
		    dirtable[i].dt_select = -1;

	dis_files(cur_index);
}

del_files()
{
    int i;
	char filename[300];
	char *ptr;
	int j;

	offIDCMP();

	for (j=i=0;i<numdir;i++)
	{
		if (dirtable[i].dt_select)
		{
			ptr = curdir + strlen(curdir)-1;

			if (*ptr == ':')
				sprintf(filename,"%s%s",curdir,dirtable[i].dt_fname);
			else
				sprintf(filename,"%s/%s",curdir,dirtable[i].dt_fname);
		    unlink(filename);
			j++;
		}
	}

	if (j)
	{
		getdir();
		new_dir();
	}

	onIDCMP();
}

copy_files()
{
    int i;
	char filename[300];
	char *ptr;

	offIDCMP();

	for (i=0;i<numdir;i++)
	{
		if (dirtable[i].dt_select)
		{
			ptr = curdir + strlen(curdir)-1;

			if (*ptr == ':')
				sprintf(filename,"%s%s",curdir,dirtable[i].dt_fname);
			else
				sprintf(filename,"%s/%s",curdir,dirtable[i].dt_fname);
			copy(filename,cp_buf);
		}
	}

	getdir();
	new_dir();

	onIDCMP();
}

par_dir()
{
	char *ptr;

	ptr = curdir+strlen(curdir)-1;
	if (*ptr == ':')
	    return;

	offIDCMP();

	for (ptr = curdir+strlen(curdir)-1;ptr > curdir;ptr--)
	{
	    if (*ptr == '/')
		{
		    *ptr = 0;
			break;
		}
	}

	if (ptr == curdir)
	{
	    for (ptr=curdir;*ptr;ptr++)
		{
		    if (*ptr == ':')
			{
			    *(ptr+1) = 0;
				break;
			}
		}
	}

	getdir();
	new_dir();

	onIDCMP();
	return;
}

/* The following function is used to compare two directory entries */
cmp_dt(dt1,dt2)
struct DirTable *dt1,*dt2;
{
   register char *ptr1,*ptr2;
   register c1,c2;

   for (ptr1=dt1->dt_fname,ptr2=dt2->dt_fname;*ptr1 && *ptr2;ptr1++,ptr2++)
   {
       c1 = *ptr1;
	   c2 = *ptr2;

	   if (c1 >= 'A' && c1 <= 'Z')
	   {
	       c1 -= 'A';
		   c1 += 'a';
	   }

	   if (c2 >= 'A' && c2 <= 'Z')
	   {
	       c2 -= 'A';
		   c2 += 'a';
	   }

	   if (c1 > c2)
	       return (-1);

	   if (c1 < c2)
	       return (1);
   }

   if (*ptr1)
       return (-1);

   if (*ptr2)
       return (1);

   return (0);
}


onIDCMP()
{
    ModifyIDCMP(w,WinIDCMP);
}

offIDCMP()
{
    ModifyIDCMP(w,0);
}

copy(s,pc)
char *s;
char *pc;
{
    struct FileHandle  *copyin;
    struct FileHandle  *copyout;
    int iosize;
    int actual;
    char *copybuf;
	char work[300];
	struct FileLock *fl;
	char *ptr;
	char *ptr1;

	if (!(copyin = Open(s,MODE_OLDFILE)))
	    return;

    if ((fl = Lock(pc,ACCESS_READ)))
	{
	    UnLock(fl);

		ptr = strlen(pc) + pc - 1;

		for (ptr1=s+strlen(s)-1;ptr1 > s;ptr1--)
		{
		    if (*ptr1 == '/' || *ptr1 == ':')
				break;
		}

		if (*ptr1 == '/' || *ptr1 == ':')
		    ptr1++;

		if (*ptr == ':')
			sprintf(work,"%s%s",pc,ptr1);
		else
		    sprintf(work,"%s/%s",pc,ptr1);
	}
	else
	{
		Close(copyin);
		return;
	}

	if (!strcmp(work,s))
	{
		Close(copyin);
		return;
	}

	if (!(copyout = Open(work, MODE_NEWFILE)))
	{
         Close(copyin);
		 return;
	}
    else
	{
		/* Determine the length of the file */
        iosize = Seek(copyin, 0, OFFSET_END);
        iosize = Seek(copyin, 0, OFFSET_BEGINING);

		/* Allocate memory for the copy buffer */
        do
		{
           copybuf = AllocMem(iosize, MEMF_PUBLIC|MEMF_CLEAR);

           if (copybuf == 0)
              iosize = iosize/2;

        } while (copybuf == 0 & iosize > 512);

		/* Copy the file */
		do
		{
		   actual = Read(copyin, copybuf, iosize);

		   if (Write(copyout, copybuf, actual) != actual)
			  break;

	    } while (actual == iosize);

		/* Free the copy buffer */
	    FreeMem(copybuf, iosize);

		/* Close the input and output buffers */
	    Close(copyout);
		Close(copyin);
	}
}

nullterm(str,len)
char *str;
int len;
{
	int i;

    str[len] = 0;

	for (i=len;len >= 0;len--)
	{
	    if (str[i] != ' ' && str[i])
		    break;

		str[i] = 0;
	}
}

