#include "ibm.h"
#include "disc.h"
#include "fdc.h"
#include "fdd.h"

static struct {
	int type;

	int track;

	int densel;

	int drate;

	int kbps;
	int fdc_kbps;
} fdd[2];

/* Flags:
   Bit 0:	300 rpm supported;
   Bit 1:	360 rpm supported;
   Bit 2:	size (0 = 3.5", 1 = 5.25");
   Bit 3:	double density supported;
   Bit 4:	high density supported;
   Bit 5:	extended density supported;
   Bit 6:	double step for 40-track media;
*/
#define FLAG_RPM_300                1
#define FLAG_RPM_360                2
#define FLAG_525                4
#define FLAG_HOLE0                8
#define FLAG_HOLE1                16
#define FLAG_HOLE2                32
#define FLAG_DOUBLE_STEP        64

static struct {
	int max_track;
	int flags;
} drive_types[] =
	{
		{       /*None*/
			.max_track = 0,
			.flags = 0
		},
		{       /*5.25" DD*/
			.max_track = 41,
			.flags = FLAG_RPM_300 | FLAG_525 | FLAG_HOLE0
		},
		{       /*5.25" HD*/
			.max_track = 82,
			.flags = FLAG_RPM_360 | FLAG_525 | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP
		},
		{       /*5.25" HD Dual RPM*/
			.max_track = 82,
			.flags = FLAG_RPM_300 | FLAG_RPM_360 | FLAG_525 | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP
		},
		{       /*3.5" DD*/
			.max_track = 82,
			.flags = FLAG_RPM_300 | FLAG_HOLE0
		},
		{       /*3.5" HD*/
			.max_track = 82,
			.flags = FLAG_RPM_300 | FLAG_HOLE0 | FLAG_HOLE1
		},
		{       /*3.5" HD 3-Mode*/
			.max_track = 82,
			.flags = FLAG_RPM_300 | FLAG_RPM_360 | FLAG_HOLE0 | FLAG_HOLE1
		},
		{       /*3.5" ED*/
			.max_track = 82,
			.flags = FLAG_RPM_300 | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_HOLE2
		}
	};

int fdd_swap = 0;

uint64_t fdd_seek(int drive, int track_diff) {
	drive ^= fdd_swap;

	if (drive >= 2)
		return 1000 * TIMER_USEC;

	fdd[drive].track += track_diff;

	if (fdd[drive].track < 0)
		fdd[drive].track = 0;

	if (fdd[drive].track > drive_types[fdd[drive].type].max_track)
		fdd[drive].track = drive_types[fdd[drive].type].max_track;

//        pclog("fdd_seek: drive=%i track_diff=%i old_track=%i track=%i\n", drive, track_diff, old_track, fdd[drive].track);

	fdc_discchange_clear(drive);

	disc_seek(drive, fdd[drive].track);
	return 1000 * TIMER_USEC;
}

void fdd_disc_changed(int drive) {
	drive ^= fdd_swap;

	/*Force reload of current track data*/
	if (drive < 2)
		disc_seek(drive, fdd[drive].track);
}

int fdd_track0(int drive) {
	drive ^= fdd_swap;

	if (drive >= 2)
		return 0;

	/* If drive is disabled, TRK0 never gets set. */
	if (!drive_types[fdd[drive].type].max_track)
		return 0;

	return !fdd[drive].track;
}

void fdd_set_densel(int densel) {
	fdd[0].densel = densel;
	fdd[1].densel = densel;
}

int fdd_getrpm(int drive) {
	int hole;

	drive ^= fdd_swap;

	if (drive >= 2)
		return 0;

	hole = disc_hole(drive);

	if (!(drive_types[fdd[drive].type].flags & FLAG_RPM_360))
		return 300;
	if (!(drive_types[fdd[drive].type].flags & FLAG_RPM_300))
		return 360;

	if (drive_types[fdd[drive].type].flags & FLAG_525) {
		return fdd[drive].densel ? 360 : 300;
	} else {
		/* disc_hole(drive) returns 0 for double density media, 1 for high density, and 2 for extended density. */
		if (hole == 1) {
			return fdd[drive].densel ? 300 : 360;
		} else {
			return 300;
		}
	}

	switch (fdd[drive].type) {
	case 0:return 300;
	case 1:return 360;
	}
}

void fdd_setswap(int swap) {
	fdd_swap = swap ? 1 : 0;
}

int fdd_can_read_medium(int drive) {
	int hole;

	drive ^= fdd_swap;

	if (drive >= 2)
		return 0;

	hole = disc_hole(drive);

	hole = 1 << (hole + 3);

//	pclog("Drive %02X, type %02X, hole flag %02X, flags %02X, result %02X\n", drive, fdd[drive].type, hole, drive_types[fdd[drive].type].flags, drive_types[fdd[drive].type].flags & hole);
	return (drive_types[fdd[drive].type].flags & hole) ? 1 : 0;
}

int fdd_doublestep_40(int drive) {
	if (drive >= 2)
		return 0;
	return drive_types[fdd[drive].type].flags & FLAG_DOUBLE_STEP;
}

void fdd_set_type(int drive, int type) {
	if (drive < 2)
		fdd[drive].type = type;
}

int fdd_get_type(int drive) {
	if (drive >= 2)
		return 0;
	return fdd[drive].type;
}

int fdd_is_525(int drive) {
	if (drive >= 2)
		return 0;
	return drive_types[fdd[drive].type].flags & FLAG_525;
}

int fdd_is_ed(int drive) {
	if (drive >= 2)
		return 0;
	return drive_types[fdd[drive].type].flags & FLAG_HOLE2;
}
