#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <string.h>
#include <fcntl.h>

#define ROOTDIR_OFFSET		0x00e00
#define MAX_ROOT_ENTRIES	512

void print_files();
int modify_entries();

struct fentry {
	unsigned char fnam[8];
	unsigned char fexten[3];
	unsigned char fattrib;
	unsigned char freserv;
	unsigned char ftimres;
	uint16_t fctime;
	uint16_t fcdate;
	uint16_t fadate;
	uint16_t feaind;
	uint16_t fmtime;
	uint16_t fmdate;
	uint16_t ffclust;
	uint32_t fsize;
};

union fatdirent {
	unsigned char buffer[32];
	struct fentry entry;
};

struct dirtable {
	unsigned long foffset;
	int modified;
	union fatdirent entry;
};

char *prgname;
char *imagefile;

struct dirtable dirtable[MAX_ROOT_ENTRIES];
char filenames[MAX_ROOT_ENTRIES][13];
int num_entries;

char *attrib_names[] = {
	"Read Only",
	"Hidden",
	"System",
	"Volume Label",
	"Subdirectory",
	"Archive",
	"Device",
	"Undefined"
};

char *opd_meta[] = {
	"DATA",
	"PROGRAM",
	"BASIC",
	"LABEL",
	"SAVE",
	"UNDEF"
};

int fd;

int main(int argc, char **argv)
{
	int finished = 0;
	int current_entry = 0, numbytes;
	unsigned long fileptr;
	char filename[13];
	int i, len, pos;
	unsigned char type, attrib;
	char character, *line;
	size_t linelen;

	prgname = argv[0];

	if (argc != 2)
	{
		fprintf(stderr, "%s: Incorrect number of arguments.\n", prgname);
		exit(-1);
	}

	imagefile = argv[1];

	if ((fd = open(imagefile, O_RDWR)) < 0)
	{
		fprintf(stderr, "%s: ", prgname);
		perror(imagefile);
		exit(-1);
	}

	printf("\nComputer One \"ICL One Per Desk\" floppy disk interface.\nImage file metadata modification program.\n\n");

	fileptr = ROOTDIR_OFFSET;

	if (lseek(fd, fileptr, SEEK_SET) < 0)
	{
		fprintf(stderr, "%s: Can't seek to beginning of root directory\n", prgname);
		exit(-1);
	}

	while((finished == 0) || (current_entry >= MAX_ROOT_ENTRIES))
	{
		dirtable[current_entry].foffset = fileptr;
		dirtable[current_entry].modified = 0;

		numbytes = read(fd, dirtable[current_entry].entry.buffer, sizeof(union fatdirent));

		fileptr = fileptr + numbytes;
		
		if (numbytes != sizeof(union fatdirent))
		{
			fprintf(stderr, "%s: ", prgname);
			perror("Short read of directory entry: ");
			exit(-1);
		}

		switch(dirtable[current_entry].entry.entry.fnam[0])
		{
		case 0x00:
			finished = 1;
		case 0x2e:	/* Dot entry */
		case 0xe5:	/* Deleted entry */
			continue;
		}

		for (len = 0; (len < 8) && (dirtable[current_entry].entry.entry.fnam[len] != ' '); len++);

		for (pos = 0; pos < len; pos++)
			filename[pos] = dirtable[current_entry].entry.entry.fnam[pos];

		if (dirtable[current_entry].entry.entry.fexten[0] != ' ')
		{
			filename[pos++] = '.';
		}
	
		for (len = 0; (len < 3) && (dirtable[current_entry].entry.entry.fexten[len] != ' '); len++);

		for (i = 0; i < len; i++)
			filename[pos++] = dirtable[current_entry].entry.entry.fexten[i];

		filename[pos] = '\0';

		strncpy(filenames[current_entry], filename, 12);
		filenames[current_entry][12] = '\0';
		
		current_entry++;
	}

	num_entries = current_entry;

	finished = 0;

	printf("\nCurrent file list and metadata in floppy image '%s'.\n\n", imagefile);

	print_files();

	while (finished == 0)
	{
		printf("Modify file metadata? (Y for yes, any other key to quit): ");
		fflush(stdout);

		line = fgetln(stdin, &linelen);
		character = line[0];

		if ((character == 'y') || (character == 'Y'))
		{
			finished = modify_entries();
		}
		else
		{	printf("\nGood-bye\n");
			finished = 1;
		}
	}

	close(fd);
}

#define COLUMNS 2

void print_files()
{
	int i, j, k, numlines, lastline, counter, meta, len;
	char my_filename[13];

	my_filename[12] = '\0';

	counter = 1;

	numlines = (num_entries - 1) / COLUMNS;
	lastline = (num_entries - 1) % COLUMNS;

	for (i = 0; i < numlines; i++)
	{
		for (j = 0; j < COLUMNS; j++)
		{
			meta = dirtable[counter].entry.entry.ftimres;

			strcpy(my_filename, filenames[counter]);

			len = strlen(my_filename);

			for (k = len; k < 12; k++)
				my_filename[k] = ' ';
			
			printf("%d.\t%s (%s)\t", counter, my_filename, opd_meta[meta]);
			counter++;
		}

		printf("\n");
	}

	if (lastline > 0)
	{
		for (j = 0; j < lastline; j++)
		{
			meta = dirtable[counter].entry.entry.ftimres;

			strcpy(my_filename, filenames[counter]);

			len = strlen(my_filename);

			for (k = len; k < 12; k++)
				my_filename[k] = ' ';
			
			printf("%d\t%s (%s)\t\t", counter, my_filename, opd_meta[meta]);
			counter++;
		}

		printf("\n");
	}

	printf("\n");
}

void update_image();

int modify_entries()
{
	int finished = 0, doneone = 0, moddone = 0;
	int filenum, meta, newmeta;
	int i;
	char character, *line;
	size_t linelen;

	while(finished == 0)
	{
		printf("Please select %s file to modify by typing the entry's number, or any other key to finish: ", doneone ? "another" : "a");
		fflush(stdout);

		line = fgetln(stdin, &linelen);

		if ((line[0] < '1') || (line[0] > '9'))
		{
			finished = 1;
			continue;
		}

		filenum = atoi(line);

		if ((filenum < 0) || (filenum > num_entries))
		{
			printf("Error: invalid file number %d\n", filenum);
			continue;
		}

		meta = dirtable[filenum].entry.entry.ftimres;

		printf("\nSelected file:\t\t%s\nCurrent metadata value:\t%d (%s)\n\n", filenames[filenum], meta, opd_meta[meta]);

		printf("Modify metadata (Y/N)? ");
		fflush(stdout);
		
		line = fgetln(stdin, &linelen);

		if ((line[0] == 'Y') || (line[0] == 'y'))
		{
			for (i = 0; i < 6; i++)
				printf("%d - %s\t", i, opd_meta[i]);

			printf("\n\nType in the new metadata value (0 - 5. Any other value aborts): ");
			fflush(stdout);

			line = fgetln(stdin, &linelen);

			if ((line[0] >= '0') && (line[0] <= '5'))
			{
				moddone = 1;
				doneone = 1;
				dirtable[filenum].modified = 1;
				dirtable[filenum].entry.entry.ftimres = (unsigned char) (line[0] - '0');

				meta = dirtable[filenum].entry.entry.ftimres;

				printf("\nSelected file:\t\t%s\nNew metadata value:\t%d (%s)\n\n", filenames[filenum], meta, opd_meta[meta]);
			}
			else
			{
				printf("\nAborting.\n");
			}

			printf("\n\n");
			print_files();
		}
	}

	if (moddone == 1)
	{
		printf("Metadata has been modified. Update floppy image file? (Type W to write, any other key to abort.)");
		fflush(stdout);

		line = fgetln(stdin, &linelen);

		if ((line[0] == 'W') || (line[0] == 'w'))
		{
			update_image();
		}
	}

	return moddone;
}

void update_image()
{
	int i, filepos;

	printf("Writing modfied entries.\n");

	for (i = 1; i < num_entries; i++)
	{
		if (dirtable[i].modified == 1)
		{
			filepos = dirtable[i].foffset;

			if (lseek(fd, filepos, SEEK_SET) != filepos)
			{
				perror(imagefile);
				exit(-1);
			}

			if (write(fd, dirtable[i].entry.buffer, sizeof(union fatdirent)) != sizeof(union fatdirent))
			{
				perror(imagefile);
				exit(-1);
			}
		}
	}
}
