#include <exec/memory.h>
#include <intuition/intuition.h>
#include <libraries/gadtools.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <clib/alib_protos.h>
#include <string.h>

#include "split.h"

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

extern struct Window *window;
extern struct Gadget *glist, *gad, *vlist_gad, *chunk_gad, *chunknum_gad,
	*chunksize_gad, *chunkoffs_gad, *write_gad, *chunknam1_gad, *chunknam2_gad,
	*filenam1_gad, *filenam2_gad, *filesize_gad, *status_gad;

extern struct List *vlist;
extern struct List *getVolumeList( struct List * );

extern struct EasyStruct easyr;

int spltype = SPLTYPE1;

char filename[128];
ULONG filesize;

ULONG chunkwritten;

STRPTR status_title, msg_warnmangledsource, msg_warnmangleddest, msg_ok;
STRPTR statname[4];

UBYTE status = STATUS_IDLE;

char chunkname[128];
UBYTE chunk_number = 1;
ULONG chunksize, chunkoffset;

struct ExtHeader header;
ULONG dummy; /* 4 CurrentDate( ... &dummy ) */

struct chunkNode {
	struct Node cn_Node;
	ULONG cn_Size;
	ULONG cn_Offset; };

struct List chunkList;


void readyChunkList( void )
{
NewList( &chunkList );
}


void insertChunkList( ULONG csize, ULONG coffs )
{
struct chunkNode *newnode, *node;
if ( newnode = AllocVec( sizeof(struct chunkNode), MEMF_ANY|MEMF_CLEAR ) )
	{
	newnode->cn_Size = csize;
	newnode->cn_Offset = coffs;
	node = (struct chunkNode *)chunkList.lh_Head;
	while( node->cn_Node.ln_Succ && node->cn_Offset < coffs )
		node = (struct chunkNode *)node->cn_Node.ln_Succ;
	Insert( &chunkList, newnode, node );
	}
}


void newChunkList( void )
{
insertChunkList( filesize, 0 );
}


void freeChunkList( void )
{
struct chunkNode *cn;
while( cn = (struct chunkNode *)RemHead( &chunkList ) )
	FreeVec( cn );
}


void clearHeader( void )
{
memset( &header, 0, sizeof(struct ExtHeader) );
}


void updateGadgets( void )
{
if ( window )
	{
	if ( !filesize )
		OffGadget( write_gad, window, NULL );
	else
		OnGadget( write_gad, window, NULL );

	GT_SetGadgetAttrs( chunknum_gad, window, NULL, GTNM_Number, chunk_number, TAG_END );
	GT_SetGadgetAttrs( chunksize_gad, window, NULL, GTIN_Number, chunksize, TAG_END );
	GT_SetGadgetAttrs( chunkoffs_gad, window, NULL, GTIN_Number, chunkoffset, TAG_END );
	GT_SetGadgetAttrs( chunknam2_gad, window, NULL, GTST_String, chunkname, TAG_END );
	GT_SetGadgetAttrs( filenam2_gad, window, NULL, GTTX_Text, FilePart(filename), TAG_END );
	GT_SetGadgetAttrs( filesize_gad, window, NULL, GTNM_Number, filesize, TAG_END );
	GT_SetGadgetAttrs( status_gad, window, NULL, GTTX_Text, statname[status], TAG_END );
	}
}


BOOL checkChunk( void )
{
struct chunkNode *node = (struct chunkNode *)chunkList.lh_Head;
while( node->cn_Node.ln_Succ && node->cn_Offset + node->cn_Size < chunkoffset )
	{
	node = (struct chunkNode *)node->cn_Node.ln_Succ;
	}
if ( !node->cn_Node.ln_Succ )
	/* An error within list ??? */
	return FALSE;
else if ( node->cn_Offset + node->cn_Size < chunkoffset + chunksize )
	return FALSE;
else
	return TRUE;
}


void getNextChunk( void )
{
struct chunkNode *node = (struct chunkNode *)chunkList.lh_Head;
chunkoffset = node->cn_Offset;
chunksize = node->cn_Size;
updateGadgets();
}


void goIdle( void )
{
clearHeader();
freeChunkList();
filesize = 0;
filename[0] = 0;
chunkname[0] = 0;
chunkwritten = 0;
status = STATUS_IDLE;
OnMenu( window, FULLMENUNUM( 1, NOITEM, 0 ) );
chunk_number = 1;
updateGadgets();
}


void writeNextChunk( void )
{
UBYTE *mptr;
ULONG bytes_copied = 0, bytes_transferred, bytes_to_transfer;
BPTR source, dest;
BOOL mustDelete = FALSE;

if ( status != STATUS_SPLITTING )
	{
	CurrentTime( &header.exth_Date, &dummy );
	header.exth_Header.h_Identifier = (ULONG)(('S'<<24)+('P'<<16)+('L'<<8)+('0'+spltype));
	OffMenu( window, FULLMENUNUM( 1, NOITEM, 0 ) );
	}

if ( filesize && ( mptr = AllocVec( TRANSFER_SIZE, MEMF_ANY ) ) )
	{
	strcpy( FilePart(chunkname), FilePart(filename) );
	strcat( chunkname, ".00" );

	chunkname[strlen(chunkname)-1] = (chunk_number%10) + '0';
	chunkname[strlen(chunkname)-2] = (chunk_number/10) + '0';

	if ( source = Open( filename, MODE_OLDFILE ) )
		{
		if ( dest = Open( chunkname, MODE_NEWFILE ) )
			{
			strcpy( header.exth_Header.h_FileName, FilePart(filename) );
			header.exth_Header.h_FileSize = filesize;
			header.exth_Header.h_ChunkStart = chunkoffset;
			header.exth_Header.h_ChunkEnd = chunkoffset + chunksize;
			Write( dest, &header, spltype == SPLTYPE0 ? sizeof(struct Header) : sizeof(struct ExtHeader) );

			Seek( source, chunkoffset, OFFSET_BEGINNING );
			while ( bytes_copied != chunksize )
				{
				status = STATUS_READING;
				updateGadgets();
				bytes_to_transfer = ( chunksize - bytes_copied > TRANSFER_SIZE ? TRANSFER_SIZE : chunksize - bytes_copied );
				bytes_transferred = Read( source, mptr, bytes_to_transfer );
				if ( bytes_transferred != bytes_to_transfer )
					{
					easyr.es_TextFormat = msg_warnmangledsource;
					easyr.es_GadgetFormat = msg_ok;
					EasyRequestArgs( window, &easyr, NULL, NULL );
					goto end;
					}
				status = STATUS_WRITING;
				updateGadgets();
				bytes_transferred = Write( dest, mptr, bytes_to_transfer );
				if ( bytes_transferred != bytes_to_transfer )
					{
					easyr.es_TextFormat = msg_warnmangleddest;
					easyr.es_GadgetFormat = msg_ok;
					EasyRequestArgs( window, &easyr, NULL, NULL );
					mustDelete = TRUE;
					goto end;
					}
				bytes_copied += bytes_transferred;
				}
			status = STATUS_SPLITTING;

			chunkwritten += chunksize;
			if ( chunkwritten == filesize )
				goIdle();
			else
				{
				struct chunkNode *node = (struct chunkNode *)chunkList.lh_Head;
				while( node->cn_Offset > chunkoffset )
					node = (struct chunkNode *)node->cn_Node.ln_Succ;
				if ( node->cn_Offset == chunkoffset )
					{
					if ( node->cn_Size == chunksize )
						Remove( node );
					else
						{
						node->cn_Offset += chunksize;
						node->cn_Size -= chunksize;
						}
					}
				else
					{
					if ( node->cn_Offset + node->cn_Size == chunkoffset + chunksize )
						node->cn_Size -= chunksize;
					else
						{
						insertChunkList( node->cn_Offset + node->cn_Size - chunkoffset - chunksize, chunkoffset + chunksize );
						node->cn_Size = chunkoffset - node->cn_Offset;
						}
					}
				chunk_number++;
				getNextChunk();
				}
end:
			Close( dest );
			if ( mustDelete )
				DeleteFile( chunkname );
			}
		Close( source );
		}
	FreeVec( mptr );
	}

if ( status != STATUS_SPLITTING )
	OnMenu( window, FULLMENUNUM( 1, NOITEM, 0 ) );
}
