#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;
}
