#include <exec/memory.h>
#include <libraries/asl.h>
#include <intuition/intuition.h>
#include <utility/tagitem.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/asl.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/locale.h>
#include <string.h>
#include <math.h>

/* for debug purposes */
#include <stdio.h>

#include "split.h"

STRPTR msg_notenoughspace, msg_notasplfile, msg_missingparm,
	msg_cancelled, msg_filealreadyread, msg_warnmangleddest,
	msg_warnmangledsource, msg_cantfindfile, msg_selectpath,
	msg_selectsourcefile, msg_invalidchunk, msg_fileexists,
	msg_existsgads, msg_ok, msg_filerebuilt;

#define VERSION "1.0"
char *ver = "$VER: Build "VERSION" "__AMIGADATE__;

char drawer[96], name[32];
struct TagItem tags[] = {
{ ASLFR_TitleText, NULL },
{ ASLFR_InitialDrawer, (ULONG)drawer },
{ ASLFR_InitialFile, (ULONG)name },
{ ASLFR_InitialHeight, 210L },
{ ASLFR_RejectIcons, TRUE },
{ TAG_END, NULL } };

struct EasyStruct easyr = {
	sizeof(struct EasyStruct), 0, NULL, NULL, NULL };

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Library *AslBase, *LocaleBase;

struct Window *window;
struct VisualInfo *vi;

struct RDArgs *args;
STRPTR res[3] = { 0, 0, 0 };
char **chunknames;

#define CATCOMP_NUMBERS
#define CATCOMP_BLOCK
#define CATCOMP_CODE
#include "Build_strings.h"

#define XLocaleBase LocaleBase

extern STRPTR __asm GetString(register __a0 struct LocaleInfo *li,
                       register __d0 LONG stringNum);

struct LocaleInfo li;

ULONG filesize;

void initLocale( void )
{
li.li_Catalog = NULL;
if ( LocaleBase = OpenLibrary( "locale.library", 38L ) )
	{
	li.li_LocaleBase = LocaleBase;
	li.li_Catalog = OpenCatalogA( NULL, "Build.catalog", NULL );
	}

easyr.es_Title = GetString( &li, MSG_BUILDREQUEST );
msg_notenoughspace = GetString( &li, MSG_NOTENOUGHSPACE );
msg_notasplfile = GetString( &li, MSG_NOTASPLFILE );
msg_missingparm = GetString( &li, MSG_MISSINGPARM );
msg_cancelled = GetString( &li, MSG_CANCELLED );
msg_filealreadyread = GetString( &li, MSG_FILEALREADYREAD );
msg_warnmangleddest = GetString( &li, MSG_WARNMANGLEDDEST );
msg_warnmangledsource = GetString( &li, MSG_WARNMANGLEDSOURCE );
msg_cantfindfile = GetString( &li, MSG_CANTFINDFILE );
msg_selectpath = GetString( &li, MSG_SELECTPATH );
msg_selectsourcefile = GetString( &li, MSG_SELECTSOURCEFILE );
msg_invalidchunk = GetString( &li, MSG_INVALIDCHUNK );
msg_ok = GetString( &li, MSG_OK );
msg_fileexists = GetString( &li, MSG_FILEEXISTS );
msg_existsgads = GetString( &li, MSG_EXISTSGADS );
msg_filerebuilt = GetString( &li, MSG_FILEREBUILT );
}


void flushLocale( void )
{
if ( LocaleBase )
	{
	if ( li.li_Catalog )
		CloseCatalog( li.li_Catalog );
	CloseLibrary( LocaleBase );
	}
}


struct IntuiText itext = { 1, 0, JAM2, 0, 0, NULL, NULL, NULL };
UWORD boxleft, boxtop;
WORD whitelines[8] = { 0, 0, 0, 10, 1, 9, 1, 1 };
WORD blacklines[8] = { 199, 10, 199, 0, 198, 1, 198, 10 };

BOOL openGUI( char *name )
{
register struct RastPort *rp;
struct Screen *wbs = LockPubScreen( NULL );
BOOL result = FALSE;
UWORD wwidth, wheight;
register UWORD ilength, n;

itext.ITextFont = wbs->Font;
itext.IText = name;

ilength = IntuiTextLength( &itext );
wwidth = max( ilength, 200 );
wheight = 2 * wbs->Font->ta_YSize + wbs->WBorBottom + 30;

if ( window = OpenWindowTags( NULL,
	WA_Left, ( wbs->Width - wwidth ) >> 1,
	WA_Top, ( wbs->Height - wheight ) >> 1,
	WA_Width, wwidth + wbs->WBorLeft + wbs->WBorRight + 8,
	WA_Height, wheight,
	WA_Title, "Build v"VERSION,
	WA_ScreenTitle, "Build v"VERSION" Copyright © 1995 by Stefano Reksten of 3AM",
	WA_Flags, WFLG_DEPTHGADGET|WFLG_DRAGBAR|WFLG_ACTIVATE|WFLG_RMBTRAP,
	TAG_END ) )
	{
	rp = window->RPort;
	boxleft = wbs->WBorLeft + 4;
	boxtop = wbs->BarHeight + wbs->Font->ta_YSize + 6;
	for ( n = 0; n < 4; n++ )
		{
		whitelines[n<<1] += boxleft;
		whitelines[(n<<1)+1] += boxtop;
		blacklines[n<<1] += boxleft;
		blacklines[(n<<1)+1] += boxtop;
		}
	PrintIText( rp, &itext, boxleft, wbs->BarHeight + 4 );
	SetAPen( rp, 1 );
	Move( rp, boxleft + 199, boxtop );
	PolyDraw( rp, 4, whitelines );
	SetAPen( rp, 2 );
	Move( rp, 1 + boxleft, 10 + boxtop );
	PolyDraw( rp, 4, blacklines );
	SetAPen( rp, 3 );
	result = TRUE;
	}

UnlockPubScreen( NULL, wbs );
return result;
}


void closeGUI( void )
{
CloseWindow( window );
}


void updateGUI( ULONG offset, ULONG size )
{
register UWORD xmin, ymin;

xmin = boxleft + 2 + 196 * offset / filesize;
ymin = boxtop + 1;
RectFill( window->RPort, xmin, ymin, xmin + size * 196 / filesize, ymin + 8 );
}



BOOL askFile( STRPTR dest )
{
struct FileRequester *request;
BOOL result = FALSE;

if ( AslBase = OpenLibrary( "asl.library", 0L ) )
	{
	if ( request = (struct FileRequester *)AllocAslRequest( ASL_FileRequest, tags ) )
		{
		if ( AslRequest( request, tags ) )
			{
			strcpy( drawer, request->fr_Drawer );
			strcpy( name, request->fr_File );

			strcpy( dest, request->fr_Drawer );
			if ( dest[0] )
				{
				strcpy( drawer, dest );
				if ( dest[ strlen(dest)-1 ] != ':' && dest[ strlen(dest)-1 ] != '/' )
					strcat( dest, "/" );
				}
			strcat( dest, FilePart(request->fr_File) );
			result = TRUE;
			}
		FreeAslRequest( request );
		}
	CloseLibrary( AslBase );
	}
return result;
}


void error( STRPTR cause )
{
easyr.es_TextFormat = cause;
easyr.es_GadgetFormat = msg_ok;
EasyRequestArgs( NULL, &easyr, NULL, NULL );
}


UBYTE *mptr;

BOOL transferbytes( BPTR source, BPTR dest, ULONG offset, ULONG chunksize )
{
ULONG bytes_copied = 0, bytes_transferred, bytes_to_transfer, current_offset = offset;

Seek( dest, offset, OFFSET_BEGINNING );
while ( bytes_copied != chunksize )
	{
	bytes_to_transfer = ( chunksize - bytes_copied > TRANSFER_SIZE ? TRANSFER_SIZE : chunksize - bytes_copied );
	bytes_transferred = Read( source, (APTR)mptr, bytes_to_transfer );
	if ( bytes_transferred != bytes_to_transfer )
		{
		easyr.es_TextFormat = msg_warnmangledsource;
		easyr.es_GadgetFormat = msg_ok;
		EasyRequestArgs( NULL, &easyr, NULL, NULL );
		goto end;
		}
	bytes_transferred = Write( dest, (APTR)mptr, bytes_to_transfer );
	if ( bytes_transferred != bytes_to_transfer )
		{
		easyr.es_TextFormat = msg_warnmangleddest;
		easyr.es_GadgetFormat = msg_ok;
		EasyRequestArgs( NULL, &easyr, NULL, NULL );
		goto end;
		}
	bytes_copied += bytes_transferred;

	updateGUI( current_offset, bytes_transferred );
	current_offset += bytes_to_transfer;
	}
return TRUE;
end:
return FALSE;
}


struct List chunkSeenList;
struct chunkseennode {
	struct Node cs_Node;
	ULONG cs_Offset; };

BOOL checkchunk( ULONG offset )
{
struct chunkseennode *node;
for ( node = (struct chunkseennode *)chunkSeenList.lh_Head; node->cs_Node.ln_Succ; node = (struct chunkseennode *)node->cs_Node.ln_Succ )
	if ( node->cs_Offset == offset )
		return FALSE;
if ( node = AllocVec( sizeof(struct chunkseennode), MEMF_ANY ) )
	{
	node->cs_Offset = offset;
	AddTail( &chunkSeenList, (struct Node *)node );
	return TRUE;
	}
return FALSE;
}


void freeList( void )
{
struct Node *node;
while( node = RemHead( &chunkSeenList ) )
	FreeVec( node );
}


void rebuild( char *bname, char *bpath )
{
struct Header header;
BPTR source, dest;
char destname[128], otherchunkname[128];
char originalname[32];
ULONG originalsize, restored = 0;
BOOL valid_dest = FALSE;

if ( source = Open( bname, MODE_OLDFILE ) )
	{
	Read( source, &header, sizeof(struct Header) );
	if ( header.h_Identifier != (ULONG)(('S'<<24)+('P'<<16)+('L'<<8)+'0') )
		{
		error( msg_notasplfile );
		Close( source );
		return;
		}
	strcpy( originalname, header.h_FileName );
	originalsize = header.h_FileSize;
	Close( source );
	strcpy( otherchunkname, drawer );
	if ( *drawer && otherchunkname[ strlen(otherchunkname)-1 ] != ':' && otherchunkname[ strlen(otherchunkname)-1 ] != '/' )
			strcat( otherchunkname, "/" );
	strcat( otherchunkname, name );
	strcpy( name, header.h_FileName );

	if ( bpath )
		strcpy( destname, bpath );
	else
		destname[0] = 0;

	if ( openGUI( originalname ) )
		{
		tags[0].ti_Data = (ULONG)msg_selectpath;
		while ( !valid_dest )
			{
			while ( !*destname )
				if ( !askFile( destname ) )
					{
					error( msg_cancelled );
					closeGUI();
					return;
					}
			if ( !*FilePart(destname) )
				strcat( destname, originalname );

			if ( dest = Lock( destname, ACCESS_READ ) )
				{
				UnLock( dest );
				easyr.es_TextFormat = msg_fileexists;
				easyr.es_GadgetFormat = msg_existsgads;
				if ( EasyRequestArgs( NULL, &easyr, NULL, NULL ) )
					valid_dest = TRUE;
				else
					{
					closeGUI();
					return;
					}
				}
			else valid_dest = TRUE;
			}

		tags[0].ti_Data = (ULONG)msg_selectsourcefile;
		if ( dest = Open( destname, MODE_NEWFILE ) )
			{
			if ( mptr = AllocVec( TRANSFER_SIZE, MEMF_ANY ) )
				{
				NewList( &chunkSeenList );
				filesize = header.h_FileSize;
				if ( SetFileSize( dest, filesize, OFFSET_BEGINNING ) )
					{
					do	{
						if ( source = Open( otherchunkname, MODE_OLDFILE ) ) 
							{
							Read( source, &header, sizeof(struct Header) );
							if ( header.h_Identifier != (ULONG)(('S'<<24)+('P'<<16)+('L'<<8)+'0') )
								error( msg_notasplfile );
							else
							if ( strcmp( header.h_FileName, originalname ) )
								error( msg_invalidchunk );
							else
							if ( checkchunk( header.h_ChunkStart ) )
								{
								if ( transferbytes( source, dest, header.h_ChunkStart, header.h_ChunkEnd - header.h_ChunkStart ) )
									restored += header.h_ChunkEnd - header.h_ChunkStart;
								else break;
								}
							else error( msg_filealreadyread );
							Close( source );
							}
						if ( originalsize > restored )
							{
							if ( chunknames && *chunknames )
								strcpy( otherchunkname, *chunknames++ );
							else if ( !askFile( otherchunkname ) )
								{
								error( msg_cancelled );
								break;
								}
							}
						} while( originalsize > restored );
					}
				else
					 error( msg_notenoughspace );
				FreeVec( (APTR)mptr );
				}
			Close( dest );
			if ( originalsize > restored )
				DeleteFile( destname );
			else
				{
				easyr.es_TextFormat = msg_filerebuilt;
				easyr.es_GadgetFormat = msg_ok;
				EasyRequestArgs( NULL, &easyr, NULL, NULL );
				}
			}
		closeGUI();
		}
	}
else
	error( msg_cantfindfile );
}


void main( int argc, char *argv[] )
{
char filename[128];

if ( IntuitionBase = (struct IntuitionBase *)OpenLibrary( "intuition.library", 37L ) )
	{
	if ( GfxBase = (struct GfxBase *)OpenLibrary( "graphics.library", 37L ) )
		{
		initLocale();
		if ( !argc )
			{
			tags[0].ti_Data = (ULONG)msg_selectsourcefile;
			if ( askFile( filename ) )
				rebuild( filename, NULL );
			else
				error( msg_missingparm );
			}
		else
			{
			if ( args = ReadArgs( "CHUNK,PATH,OTHER_CHUNKS/M", (LONG *)res, NULL ) )
				{
				chunknames = (char **)res[2];
				if ( !res[0] )
					{
					tags[0].ti_Data = (ULONG)msg_selectsourcefile;
					if ( askFile( filename ) )
						rebuild( filename, NULL );
					else error( msg_missingparm );
					}
				else
					rebuild( res[0], res[1] );
				FreeArgs( args );
				}
			else PrintFault( IoErr(), NULL );
			}
		flushLocale();
		CloseLibrary( (struct Library *)GfxBase );
		}
	CloseLibrary( (struct Library *)IntuitionBase );
	}
}
