#include "unalf.h"
#include "addrs.h"
#include "sanity.h"

static const char *monthnames[] = {
	"???",
	"Jan", "Feb", "Mar", "Apr",
	"May", "Jun", "Jul", "Aug",
	"Sep", "Oct", "Nov", "Dec",
	"???", "???", "???",
};

u16 getword(int offs) {
	return mem[offs] | (mem[offs + 1] << 8);
}

unsigned int getquad(int offs) {
	return getword(offs) | ((getword(offs + 2) << 16));
}

/* see Arcinfo for details */
void format_msdos_time(char *buf) {
	u16 t, hour, min, sec;
	char ampm;

	t = getword(alf_hdr_time0);

	sec = (t & 0x1f) << 1;
	hour = t >> 11;
	min = (t >> 5) & 0x3f;

	/* midnight is 12:00a (unlike arc, which uses 0:00a). noon
		is 12:00p (same as arc). could just use a 24h clock like
		unzip does, but AM/PM are more intuitive to me. */
	ampm = (hour > 11 ? 'p' : 'a');
	hour %= 12;

	/* midnight and noon print as 12, not 0 */
	if(hour == 0) hour = 12;

	snprintf(buf, 10, "%2d:%02d:%02d%c", hour, min, sec, ampm);
}

/* see Arcinfo for details */
void format_msdos_date(char *buf) {
	u16 d, year, month, day;

	d = getword(alf_hdr_date0);

	/* year actually ranges 1980 to 2107... */
	year = (d >> 9) + 1980;

	/* valid months range 1 to 12 */
	month = (d >> 5) & 0x0f;

	/* valid day range is 1 to 31. 0 is invalid. */
	day = d & 0x1f;

	if(day == 0 || month == 0 || month > 12)
		snprintf(buf, 12, "%11s", "<none>");
	else
		snprintf(buf, 12, "%2d %3s %04d", day, monthnames[month], year);
}

/* small files may be "compressed" larger than the original, so this
   has to return a signed type. */
static int comp_percent(unsigned int orig, unsigned int comp) {
	if(orig == 0) return 0; /* no division by zero please */
	return 100 - (int)((float)comp / (float)orig * 100.0);
}

/* output similar to "arc v", except we omit the compression type column
   since there's only one type. also, don't call the checksum a CRC, it
   isn't. */
void list_alf(void) {
	unsigned int c = 0, orig_size, comp_size, total_osize = 0, total_csize = 0;
	char buf[100];
	extern char *out_filename;
	int fnlen;

	while(read_alf_header()) {
		c++;

		if(opts.verbose_list && c == 1) {
			puts("Name          Size was  Size now  Comp  Date         Time       CkSum");
			puts("============  ========  ========  ====  ===========  =========  =====");
		}

		orig_size = getquad(alf_hdr_origsize0);
		comp_size = getquad(alf_hdr_compsize0);
		out_filename = (char *)(mem + alf_hdr_filename);

		if(opts.verbose_list)
			sanity_check_filename(out_filename);
		else
			fix_filename();

		total_osize += orig_size;
		total_csize += comp_size;

		if(opts.verbose_list) {
			fnlen = safe_print_filename(out_filename, stdout);
			while(fnlen++ < 14)
				putchar(' ');
			printf("%8d  ", orig_size);
			printf("%8d  ", comp_size);
			printf("%3d%%  ", comp_percent(orig_size, comp_size));
			format_msdos_date(buf);
			printf("%9s  ", buf);
			format_msdos_time(buf);
			printf("%6s  ", buf);
			printf(" %04x", getword(alf_hdr_cksum_l));
			putchar('\n');
		} else {
			if(file_wanted(out_filename)) {
				safe_print_filename(out_filename, stdout);
				putchar('\n');
			}
		}

		if(fseek(in_file, comp_size, SEEK_CUR) != 0) {
			fprintf(stderr, "%s: fatal: seek failed on input!\n", self);
			exit(1);
		}
	}

	if(opts.verbose_list) {
		fputs("        ====  ========  ========  ====\nTotal   ", stdout);
		printf("%4d  ", c);
		printf("%8d  ", total_osize);
		printf("%8d  ", total_csize);
		printf("%3d%%  ", comp_percent(total_osize, total_csize));
		putchar('\n');
	}
}
