/**********************************************************
**                                                       **
**      $VER: RecentFiles.c 1.0 (may 31, 2002)           **
**      Keep track of recent files accessed.             **
**                                                       **
**      © T.Pierron, C.Guilaume. Free software under     **
**      terms of GNU public license.                     **
**                                                       **
**********************************************************/

#include <libraries/gadtools.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include "Project.h"
#include "RecentFiles.h"
#include "Rawkey.h"
#include "Utility.h"
#include "Gui.h"

#include "ProtoTypes.h"

#define  CATCOMP_NUMBERS		/* We will need the string id */
#include "Jed_Strings.h"

static struct MinList recent_list;
static UWORD          nb_files = 0;

/* Simply add a file to the recent list */
static RecentFile register_recent( STRPTR filename, LONG linecoltab[] )
{
	UWORD      len = strlen( filename ) + 1;
	RecentFile new = (APTR) AllocVec( sizeof(*new)+len, MEMF_PUBLIC | MEMF_CLEAR );

	if( new )
	{
		AddHead( (APTR) &recent_list, &new->rf_Node ); nb_files++;
		new->rf_FileName = (STRPTR) (new + 1);
		new->rf_FileSize = len;
		CopyMem( linecoltab, &new->rf_ModifLine, 3*sizeof(ULONG) );
		CopyMem( filename,    new->rf_FileName,  len );
	}
	return new;
}

/* Add a recent file from a project */
Project check_or_create_recent( ULONG * ref, STRPTR filename )
{
	RecentFile list = RECENT_HEAD;

	while( ! END_OF_RECENT(list) )
	{
		if( !Stricmp( filename, list->rf_FileName ) )
		{
			/* Move this project to the beginning of the list */
			Remove( &list->rf_Node );
			AddHead( (APTR) &recent_list, &list->rf_Node );

			/* Udpate project structure */
			*ref = (ULONG) list;
			return list->rf_Project;
		}
		list = RECENT_NEXT(list);
	}
	/* Not yet registered */
	{	LONG args[3];
	
		args[0] = args[1] = 0; args[2] = prefs.tabsize;

		/* Recent project does not exists yet, add it to the list */
		*ref = (ULONG) register_recent( filename, args );
	}
	return NULL;
}

/* Copy recent */
void update_project( Project p )
{
	RecentFile recent;
	if( (recent = p->recent) )
	{
		p->nbl     = recent->rf_ModifLine;
		p->nbrwc   = recent->rf_ModifCol;
		p->tabsize = recent->rf_TabSize;
		recent->rf_Project = p;
	}
}

/* Update the content of a recent file, before closing project */
void update_recent( Project p, BOOL close )
{
	RecentFile list = RECENT_HEAD;

	while( ! END_OF_RECENT(list) && list->rf_Project != p )
		list = RECENT_NEXT(list);

	/* New project maybe unreferenced yet */
	if( ! END_OF_RECENT(list) )
	{
		list->rf_TabSize   = p->tabsize;
		list->rf_ModifLine = p->nbl;
		list->rf_ModifCol  = p->nbrc;
		if( close )
			list->rf_Project = NULL; /* Project will be closed after */
	}
}

/* Free all memory allocated for recent files */
void free_recent( void )
{
	register RecentFile list = RECENT_HEAD, next;

	for( ; (next = RECENT_NEXT(list)); list = next )
		FreeVec( list );
}

/* Save the list of recent files before editor quits */
BOOL save_recent_files( STRPTR filename )
{
	BPTR fh = Open( filename, MODE_NEWFILE );

	if( fh )
	{
		RecentFile list = RECENT_HEAD;
		UWORD      nb   = MAX_RECENT_FILES;

		while( ! END_OF_RECENT(list) && nb-- )
		{
			my_FPrintf( fh, "L=%ld C=%ld TAB=%ld FILE=%s\n", &list->rf_ModifLine );
			list = RECENT_NEXT( list );
		}
		Close( fh );

		return TRUE;
	}
	return FALSE;
}

/* Get recent file list */
BOOL read_recent_files( STRPTR filename )
{
	BPTR fh = Open( filename, MODE_OLDFILE ), old_stdin;
	WORD ch, is_ok = TRUE;

	NewList( (APTR) &recent_list );

	if( fh )
	{
		old_stdin = SelectInput( fh );
		do
		{
			struct RDArgs ra, *ret;
			LONG   opt[4];

			memset(&ra,  0, sizeof(ra));
			memset(&opt, 0, sizeof(opt));

			/* Use stdin instead of anything else */
			ra.RDA_Flags = RDAF_NOPROMPT | RDAF_STDIN;

			if( (ret = (APTR) ReadArgs("L/N,C/N,TAB/N,FILE/F/A", opt, &ra)) )
			{
				if( opt[0] ) opt[0] = * (ULONG *) opt[0];
				if( opt[1] ) opt[1] = * (ULONG *) opt[1];
				if( opt[2] ) opt[2] = * (ULONG *) opt[2];
				is_ok = (register_recent( (STRPTR) opt[3], opt ) != NULL);

				FreeArgs( ret );
			}

			ch = FGetC( fh );
		} while( is_ok && ch != -1 && UnGetC(fh, ch) );

		Close( SelectInput( old_stdin ) );
	}
	if( !is_ok ) free_recent();
	return is_ok;
}

/* Show the list of registered files and let user load one */
void show_recent_files( Project current, STRPTR title )
{
	struct Window * win;
	int             width, height, selected = 0;

	/* No care to see list if there is only one project */
	if( nb_files > 1 )
	{
		register RecentFile list = RECENT_HEAD;
		register STRPTR     name;
		register int        tmp, i;
		for( i = width = 0; ! END_OF_RECENT(list); list = RECENT_NEXT(list), i++ )
		{
			/* Takes project name, thus we can see modified project */
			name = (list->rf_Project ? ((Project)list->rf_Project)->path : list->rf_FileName);

			tmp  = TextLength(&RPT, list->rf_Node.ln_Name = name, strlen(name));

			if( tmp > width ) width = tmp;
			if( list->rf_Project == current ) selected = i;
		}
	}
	else return;
	height = nb_files * prefs.scrfont->tf_YSize;
	width += 30;

	win = (APTR) OpenWindowTags( NULL,
			WA_InnerWidth,  width  + 10,
			WA_InnerHeight, height + 12,
			WA_Left,        Wnd->LeftEdge + (Wnd->Width  - width)  / 2,
			WA_Top,         Wnd->TopEdge  + (Wnd->Height - height) / 2,
			WA_Title,       (ULONG) title,
			WA_IDCMP,       IDCMP_CLOSEWINDOW | LISTVIEWIDCMP | IDCMP_RAWKEY,
			WA_Flags,       WFLG_CLOSEGADGET  | WFLG_ACTIVATE | WFLG_RMBTRAP |
			                WFLG_DEPTHGADGET  | WFLG_DRAGBAR,
			WA_PubScreen,   (ULONG) Scr,
			TAG_DONE);

	if( win )
	{
		extern struct IntuiMessage *msg, msgbuf;
		struct NewGadget ng;
		struct Gadget *  glist = NULL, * gad;
		BOOL             quit  = FALSE;

		memset( &ng, 0, sizeof(ng) );
		ng.ng_LeftEdge   = 5 + win->BorderLeft;
		ng.ng_TopEdge    = 4 + win->BorderTop;
		ng.ng_Width      = width;
		ng.ng_Height     = height+4;
		ng.ng_TextAttr   = &prefs.attrscr;
		ng.ng_VisualInfo = Vi;

		BusyWindow( Wnd );

		/* Listview gadget containing recent files list */
		gad = CreateGadget(LISTVIEW_KIND, CreateContext(&glist), &ng, 
				GTLV_Labels,       &recent_list,
				GTLV_ShowSelected, NULL,
				GTLV_Selected,     selected, TAG_DONE );

		/* Refresh everything */
		AddGList     (win,  glist, (UWORD)-1, (UWORD)-1, NULL);
		RefreshGList (glist,  win, NULL,      (UWORD)-1);

		GT_RefreshWindow(win, NULL);

		/* Quickly process events */
		while( ! quit )
		{
			WaitPort( win->UserPort );

			while( (msg = (APTR) GT_GetIMsg( win->UserPort )) )
			{
				CopyMemQuick(msg, &msgbuf, sizeof(*msg));
				GT_ReplyIMsg(msg);

				switch( msgbuf.Class )
				{
					case IDCMP_CLOSEWINDOW: quit = TRUE; selected = -1; break;
					case IDCMP_GADGETUP:    quit = TRUE; selected = msgbuf.Code; break;
					case IDCMP_RAWKEY:
						switch( msgbuf.Code )
						{
							case DOWN_KEY:
								if( selected >= nb_files-1 ) continue;
								selected++;
								break;
							case UP_KEY:
								if( selected <= 0 ) continue;
								selected--;
								break;
							case ESC_KEY: selected = -1;
							case RETURN_KEY:
							case NENTER_KEY: quit = TRUE;
							default: continue;
						}
						GT_SetGadgetAttrs(gad, win, NULL, GTLV_Selected, selected,
								GTLV_MakeVisible, selected, TAG_DONE);
				}
			}
		}
		CloseWindow( win );
		FreeGadgets( glist );
		WakeUp( Wnd );

		if( selected >= 0 )
		{
			register RecentFile list = RECENT_HEAD;
			register STRPTR     name;
			extern   Project    edit;

			while( selected > 0 )
				selected--, list = RECENT_NEXT(list);

			if( !(name = (STRPTR) AllocVec( list->rf_FileSize, MEMF_PUBLIC )) )
				return;

			CopyMem( list->rf_FileName, name, list->rf_FileSize );

			if( !(list->rf_Project = load_and_activate( edit, name,
					 msgbuf.Qualifier & SHIFTKEYS ? USE_CURRENT_PROJECT : USE_NEW_PROJECT )) )
				return;

			edit = list->rf_Project;
		}
	}
	else ThrowError(Wnd, ErrMsg(ERR_NOGUI));
}
