#ifdef __WATCOMC__
#include <dos.h>
#else
#include <io.h>
#endif
#include <direct.h>
#include <ctype.h>
#include "system/xstddef.h"
#include "system/xstring.h"
#include "misc/dbfs.h"

// Structures //

typedef struct
{
	char name[256];
	DWORD length;
} ENTRY;

// Options //

static char recursive = FALSE;

// ############## //
// Command - List //
// ############## //

static void list( char *dbname)
{
	DBFS *db;
	DBFILE *dbfile;
	int length,plength;

	if( (db = dbfsopen( dbname)) == NULL)
	{
		printf("Unable to open %s for reading!\n", dbname);
		return;
	}

	printf(" Original | Packed  | Flags | Name\n");
	printf("----------+---------+-------+--------------------------------------------------\n");

	length = plength = 0;

	dbfile = db->filelist;
	while( dbfile != NULL)
	{
		printf(" %8d | %7d | %c%c--- | %s\n",
			dbfile->length,
			dbfile->plength,
			dbfile->flags&DBFILEFLAG_COMPRESSED?'P':'p',
			dbfile->flags&DBFILEFLAG_CRYPTED?'C':'c',
			dbfile->name);
		length += dbfile->length;
		plength += dbfile->plength;
		dbfile = dbfile->next;
	}
	printf("-------------------------------------------------------------------------------\n");
	printf("Total: %d bytes, Packed: %d bytes\n", length, plength);
	dbfsclose( db);
}

// ############# //
// Command - Add //
// ############# //

static void getpath( char *dst, char *src)
{
	int i,j = strlen( src);

	while( (j != -1) && (src[j] != '\\') && (src[j] != '/'))
		j--;

	if ( j == -1)
		dst[0] = '\0';
	else
	{
		for( i=0; i<=j; i++)
      dst[i] = src[i];
		dst[i] = '\0';
	}
}

static ENTRY *entry = NULL;
static int entries = 0;

static void adddir( char *path)
{
#ifdef __WATCOMC__
  struct find_t find;
#else
  struct _finddata_t find;
	int findhandle;
#endif
	char dir[256],file[256],dirpath[256],newpath[256];

	getpath( dir, path);
	strcpy( file, &path[ strlen( dir)]);

	// Search current dir ..

#ifdef __WATCOMC__
	if ( _dos_findfirst( path, _A_NORMAL, &find) == 0 )
#else
	if ( ( findhandle = _findfirst( path, &find)) != -1 )
#endif
	{
	  do
	  {
#ifndef __WATCOMC__
			if( !(find.attrib & _A_SUBDIR))
			{
#endif
				entry = realloc( entry, sizeof( ENTRY) * (entries+1));
				sprintf( entry[ entries].name, "%s%s", dir, find.name);
				entry[ entries].length = find.size;
				entries++;
#ifndef __WATCOMC__
			}
#endif
#ifdef __WATCOMC__
	  } while( _dos_findnext(&find) == 0);
	  _dos_findclose(&find);
#else
	  } while( _findnext( findhandle, &find) == 0);
	  _findclose( findhandle);
#endif
	
	}

	// Search sub-dirs ..
	if( recursive)
	{
		sprintf( dirpath, "%s*.*", dir);
#ifdef __WATCOMC__
		if ( _dos_findfirst( dirpath, _A_SUBDIR, &find) == 0 )
#else
		if ( ( findhandle = _findfirst( dirpath, &find)) != -1 )
#endif
		{
		  do
		  {
#ifndef __WATCOMC__
				if( find.attrib & _A_SUBDIR)
				{
#endif
					if( (_stricmp( find.name, "..") != 0) && (strcmp( find.name, ".") != 0))
					{
						sprintf( newpath, "%s%s\\%s", dir, find.name, file);
						adddir( newpath);
					}
#ifndef __WATCOMC__
				}
#endif
#ifdef __WATCOMC__
			} while( _dos_findnext(&find) == 0);
			_dos_findclose(&find);
#else
		  } while( _findnext( findhandle, &find) == 0);
		  _findclose( findhandle);
#endif
		}
	}
}

static void add( char *dbname, char *filelist[], int files)
{
	DBFS *dbfs;
	int i;

	for( i=0; i<files; i++)
	{
		adddir( filelist[i]);
	}
	if( entries == 0)
	{
		printf("No Files.\n");
		return;
	}

	dbfs = dbfsopen( dbname);
	if( dbfs == NULL)
		if( (dbfs = dbfscreate( dbname)) == NULL)
		{
			printf("Unable to create %s for writing!\n", dbname);
			return;
		}

	for( i=0; i<entries; i++)
	{
		DBFILE *dbfile;
		FILE *file;
		BYTE *buff;
		unsigned int length = entry[i].length;
		char *str = entry[ i].name;

    while( *str != '\0')
    {
      if( *str == '\\') *str = '/';
      str++;
    }

    str = entry[ i].name;
    if( str[1] == ':')
			str++;
		while( (*str == '.') || (*str == '\\') || (*str == '/') || (*str == ':'))
			str++;


		file = fopen( entry[ i].name, "rb");
		if( file != NULL)
		{
			if( length != 0)
			{
				buff = (BYTE *)xmalloc( length);
				if( buff == NULL)
				{
					printf("Unable to allocate memory to load file(%d bytes)!\n", length);
					fclose( file);
					dbfsclose( dbfs);
					return;
				}
				if( fread( buff, sizeof( BYTE), length, file) != length)
				{
					printf("Error while reading file!\n");
					xfree( buff);
					fclose( file);
					dbfsclose( dbfs);
					return;
				}

				if( (dbfile = dbfilefind( dbfs, str)) != NULL)
					printf("Replacing '%s', ", str);
				else if( (dbfile = dbfilecreate( dbfs, str)) != NULL)
					printf("Adding '%s', ", str);
				else
				{
					printf("Unable to create new entry!\n");
					xfree( buff);
					fclose( file);
					dbfsclose( dbfs);
					return;
				}
				fflush( stdout);
				if( dbfilewrite( dbfile, buff, length))
					printf("%d%%, OK!\n", (dbfile->plength * 100) / dbfile->length);
				else
					printf("Failed!\n");
        xfree( buff);
			}
			else
			{
				if( (dbfile = dbfilefind( dbfs, str)) != NULL)
          printf("Replacing '%s', ", str);
				else if( (dbfile = dbfilecreate( dbfs, str)) != NULL)
          printf("Storing '%s', ", str);
				else
				{
					printf("Unable to create new entry!\n");
					fclose( file);
					return;
				}
				fflush(stdout);
				if( dbfilewrite( dbfile, NULL, 0))
					printf("OK!\n");
				else
					printf("Failed!\n");
			}
			fclose( file);
		}
		else
			printf("Unable to open '%s'.\n", entry[ i].name);
	}
	dbfsclose( dbfs);
}

// ############# //
// Command - Del //
// ############# //

static void del( char *dbname, char *filelist[], int files)
{
	DBFILE *dbfile;
	DBFS *dbfs;
	int i;

	if( (dbfs = dbfsopen( dbname)) == NULL)
	{
		printf("Unable to open %s for reading!\n", dbname);
		return;
	}

	for( i=0; i<files; i++)
	{
		if( (dbfile = dbfilefind( dbfs, filelist[i])) != NULL)
		{
			printf("Deleting '%s', ", filelist[i]); fflush( stdout);
			if( !dbfiledelete( dbfile))
				printf("Failed!\n");
			else
				printf("OK!\n");
		}
		else
			printf("File '%s' not found\n", filelist[i]);
	}
	dbfsclose( dbfs);
}

// ################# //
// Command - Extract //
// ################# //

static void createpaths( char *filename)
{
	char path[256];
	char *str = path;

	memset( str, 0, 256);

	while( *filename != '\0')
	{
		while( (*filename != '\\') && (*filename != '/') && (*filename != '\0'))
			*(str++) = *(filename++);
		if( *filename != '\0')
		{
			mkdir( path);
			*(str++) = *(filename++);
		}
	}
}

static void extractfile( DBFS *dbfs, DBFILE *dbfile)
{
	if( dbfile != NULL)
	{
		FILE *file;
		createpaths( dbfile->name);
		if( (file = fopen( dbfile->name, "wb")) != NULL)
		{
			if( !(dbfile->flags & DBFILEFLAG_NULLFILE))
			{
				BYTE *buff = (BYTE *)xmalloc( dbfile->length);
				if( buff != NULL)
				{
					printf("Extracting '%s', ", dbfile->name); fflush( stdout);
					if( dbfileread( dbfile, buff))
					{
						printf("Writing, "); fflush( stdout);
						if( fwrite( buff, dbfile->length, sizeof( BYTE), file) != dbfile->length)
							printf("%d bytes, OK!\n", dbfile->length);
						else
							printf("Failed!\n");
					}
					else
						printf("Failed!\n");
					xfree( buff);
				}
				else
					printf("Unable to allocate memory for file(%d bytes)!\n", dbfile->length);
			}
			fclose( file);
		}
		else
			printf("Unable to open '%s' for writing!\n", dbfile->name);
	}
	else
		printf("File '%s' not found!\n", dbfile->name);
}

static void extract( char *dbname, char *filelist[], int files)
{
	DBFS *dbfs;
	DBFILE *dbfile;
	int i;

	if( (dbfs = dbfsopen( dbname)) == NULL)
	{
		printf("Unable to open %s for reading!\n", dbname);
		return;
	}

	for( i=0; i<files; i++)
	{
		dbfile = dbfilefind( dbfs, filelist[i]);
		extractfile( dbfs, dbfile);
	}
	if( files == 0)
	{
		dbfile = dbfs->filelist;
		while( dbfile != NULL)
		{
			extractfile( dbfs, dbfile);
			dbfile = dbfile->next;
		}
	}
	dbfsclose( dbfs);
}

int main( int argc, char *argv[])
{
	char cmd,opt,optbool;
	int arg = 1;

	if( (argc < 3) || (strlen( argv[1]) != 1))
	{
		printf("Usage: dbfs <command> [-<option[-/+]> -<option[-/+]> ...] <archive> [<files>]\n\n");
		printf("Commands:\n");
		printf(" l - List database contents\n");
		printf(" a - Add file(s)\n");
		printf(" e - Extract file(s)\n");
		printf(" d - Delete file(s)\n");
		printf("Options:\n");
		printf(" r - Recursive\n");
		exit( 0);
	}

	cmd = tolower(argv[ arg++][0]);

	while( argv[ arg][0] == '-')
	{
		if( (strlen( argv[ arg]) < 2) ||
		    (strlen( argv[ arg]) > 3) ||
				( (strlen( argv[ arg]) == 3) && ( (argv[ arg][2] != '+') && (argv[ arg][2] != '-') ))
			)
		{
			printf("Bad option '%s'.\n", argv[ arg]);
			exit( 1);
		}
		opt = tolower(argv[ arg][1]);
		if( strlen( argv[ arg]) == 3)
			optbool = (argv[ arg][2] == '+')?TRUE:FALSE;
		else
			optbool = TRUE;
		switch( opt)
		{
			case 'r': recursive = optbool; break;
			default : printf("Unknown option '%c', ignoring.\n", opt); break;
		}
		if( ++arg == argc)
		{
			printf("No archive name specified.\n");
			exit( 1);
		}
	}

	switch( cmd)
	{
		case 'l': list( argv[arg]); break;
		case 'a': add( argv[arg], &argv[ arg + 1], argc - (arg + 1)); break;
		case 'd': del( argv[arg], &argv[ arg + 1], argc - (arg + 1)); break;
		case 'e': extract( argv[arg], &argv[ arg + 1], argc - (arg + 1)); break;
		default : printf("Unknown command '%c'!\n", cmd); exit(1);
	}

	return( 0);
}
