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

/*Handling for 'sector based' image formats (like .IMG) as opposed to 'stream based' formats (eg .FDI)*/

#define MAX_SECTORS 256

typedef struct
{
        uint8_t c, h, r, n;
        int rate;
        uint8_t *data;
} sector_t;

static sector_t disc_sector_data[2][2][MAX_SECTORS];
static int disc_sector_count[2][2];
void (*disc_sector_writeback[2])(int drive, int track);

enum
{
        STATE_IDLE,
        STATE_READ_FIND_SECTOR,
        STATE_READ_SECTOR,
        STATE_READ_FIND_FIRST_SECTOR,
        STATE_READ_FIRST_SECTOR,
        STATE_READ_FIND_NEXT_SECTOR,
        STATE_READ_NEXT_SECTOR,
        STATE_WRITE_FIND_SECTOR,
        STATE_WRITE_SECTOR,
        STATE_READ_FIND_ADDRESS,
        STATE_READ_ADDRESS,
        STATE_FORMAT_FIND,
        STATE_FORMAT
};

static int disc_sector_state[2] = {0, 0};
static int disc_sector_track[2] = {0, 0};
static int disc_sector_side[2] = {0, 0};
static int disc_sector_drive;
static int disc_sector_sector[2] = {0, 0};
static int disc_sector_n[2] = {0, 0};
static int disc_intersector_delay[2] = {0, 0};
static int disc_postdata_delay[2] = {0, 0};
static int disc_track_delay[2] = {0, 0};
static int disc_gap4_delay[2] = {0, 0};
static uint8_t disc_sector_fill[2] = {0, 0};
static int cur_sector[2], cur_byte[2];
static int index_count[2];
        
int raw_tsize[2] = {6250, 6250};
int gap2_size[2] = {22, 22};
int gap3_size[2] = {0, 0};
int gap4_size[2] = {0, 0};

void disc_sector_reset(int drive, int side)
{
        disc_sector_count[drive][side] = 0;

	disc_intersector_delay[drive] = 0;
	disc_postdata_delay[drive] = 0;
	disc_track_delay[drive] = 0;
	disc_gap4_delay[drive] = 0;
	cur_sector[drive] = 0;
	cur_byte[drive] = 0;
	index_count[drive] = 0;
}

void disc_sector_add(int drive, int side, uint8_t c, uint8_t h, uint8_t r, uint8_t n, int rate, uint8_t *data)
{
        sector_t *s = &disc_sector_data[drive][side][disc_sector_count[drive][side]];
//pclog("disc_sector_add: drive=%i side=%i %i r=%i\n", drive, side,         disc_sector_count[drive][side],r );
        if (disc_sector_count[drive][side] >= MAX_SECTORS)
                return;

        s->c = c;
        s->h = h;
        s->r = r;
        s->n = n;
        s->rate = rate;
        s->data = data;
        
        disc_sector_count[drive][side]++;
}

static int get_bitcell_period(int drive)
{
        return (disc_sector_data[drive][disc_sector_side[drive]][cur_sector[drive]].rate * 300) / fdd_getrpm(drive);
}

void disc_sector_readsector(int drive, int sector, int track, int side, int rate, int sector_size)
{
        pclog("disc_sector_readsector: fdc_period=%i img_period=%i rate=%i sector=%i track=%i side=%i\n", fdc_get_bitcell_period(), get_bitcell_period(drive), rate, sector, track, side);

        disc_sector_track[drive] = track;
        disc_sector_side[drive]  = side;
        disc_sector_drive = drive;
        disc_sector_sector[drive] = sector;
	disc_sector_n[drive] = sector_size;
	if ((cur_sector[drive] == 0) && (cur_byte[drive] == 0) && !disc_track_delay[drive])  disc_track_delay[drive] = pre_track;
        index_count[drive] = 0;
        if (sector == SECTOR_FIRST)
                disc_sector_state[drive] = STATE_READ_FIND_FIRST_SECTOR;
        else if (sector == SECTOR_NEXT)
                disc_sector_state[drive] = STATE_READ_FIND_NEXT_SECTOR;
        else
                disc_sector_state[drive] = STATE_READ_FIND_SECTOR;
}

void disc_sector_writesector(int drive, int sector, int track, int side, int rate, int sector_size)
{
//        pclog("disc_sector_writesector: fdc_period=%i img_period=%i rate=%i\n", fdc_get_bitcell_period(), get_bitcell_period(), rate);

        disc_sector_track[drive] = track;
        disc_sector_side[drive]  = side;
        disc_sector_drive = drive;
        disc_sector_sector[drive] = sector;
	disc_sector_n[drive] = sector_size;
	if ((cur_sector[drive] == 0) && (cur_byte[drive] == 0) && !disc_track_delay[drive])  disc_track_delay[drive] = pre_track;
        index_count[drive] = 0;
        disc_sector_state[drive] = STATE_WRITE_FIND_SECTOR;
}

void disc_sector_readaddress(int drive, int track, int side, int rate)
{
//        pclog("disc_sector_readaddress: fdc_period=%i img_period=%i rate=%i track=%i side=%i\n", fdc_get_bitcell_period(), get_bitcell_period(), rate, track, side);

        disc_sector_track[drive] = track;
        disc_sector_side[drive]  = side;
        disc_sector_drive = drive;
	if ((cur_sector[drive] == 0) && (cur_byte[drive] == 0) && !disc_track_delay[drive])
	{
		disc_track_delay[drive] = pre_track;
		index_count[drive] = -1;
	}
	else
	        index_count[drive] = 0;
        disc_sector_state[drive] = STATE_READ_FIND_ADDRESS;
}

void disc_sector_format(int drive, int track, int side, int rate, uint8_t fill)
{
        disc_sector_track[drive] = track;
        disc_sector_side[drive]  = side;
        disc_sector_drive = drive;
        disc_sector_fill[drive]  = fill;
        index_count[drive] = 0;
        disc_sector_state[drive] = STATE_FORMAT_FIND;
}

void disc_sector_stop(int drive)
{
        disc_sector_state[drive] = STATE_IDLE;
}

static void index_pulse(int drive)
{
	if (disc_sector_state[drive] != STATE_IDLE)  fdc_indexpulse();
}

void disc_sector_prepare_track_layout(int drive, int side)
{
}

static void advance_byte()
{
	int drive = disc_sector_drive;

	if (disc_postdata_delay[drive])
	{
		disc_postdata_delay[drive]--;
		return;
	}
	if (disc_gap4_delay[drive])
	{
		disc_gap4_delay[drive]--;
		return;
	}
	if (disc_track_delay[drive])
	{
		if (disc_track_delay[drive] == pre_track)
		{
			index_pulse(drive);
			if (disc_sector_state[drive] != STATE_IDLE)  index_count[drive]++;
		}
		disc_track_delay[drive]--;
		return;
	}
        if (disc_intersector_delay[drive])
        {
                disc_intersector_delay[drive]--;
                return;
        }
        cur_byte[drive]++;
        if (cur_byte[drive] >= (128 << disc_sector_data[drive][disc_sector_side[drive]][cur_sector[drive]].n))
        {
                cur_byte[drive] = 0;
                cur_sector[drive]++;
		disc_postdata_delay[drive] = post_gap + (gap3_size[drive]);
                if (cur_sector[drive] >= disc_sector_count[drive][disc_sector_side[drive]])
                {
                        cur_sector[drive] = 0;
			disc_gap4_delay[drive] = (gap4_size[drive]);
			disc_track_delay[drive] = pre_track;
			disc_intersector_delay[drive] = pre_gap + gap2_size[drive] + pre_data;
                }
		else
		{
			disc_gap4_delay[drive] = 0;
			disc_track_delay[drive] = 0;
			disc_intersector_delay[drive] = pre_gap + gap2_size[drive] + pre_data;
		}
        }
	return;
}

int disc_gap_has_ended(int drive)
{
	return (disc_postdata_delay[drive] == (post_gap + (gap3_size[drive]) - fdc_get_gap() - length_crc));
}

void disc_sector_poll()
{
        sector_t *s;
        int data;
	int drive = disc_sector_drive;

        if (cur_sector[drive] >= disc_sector_count[drive][disc_sector_side[drive]])
                cur_sector[drive] = 0;
	if (disc_sector_n[drive])
	{
	        if (cur_byte[drive] >= (128 << disc_sector_data[drive][disc_sector_side[drive]][cur_sector[drive]].n))
        	        cur_byte[drive] = 0;
	}
	else
	{
	        if (cur_byte[drive] >= fdc_get_dtl())
        	        cur_byte[drive] = 0;
	}

	/* Note: Side to read from should be chosen from FDC head select rather than from the sector ID. */
        s = &disc_sector_data[drive][disc_sector_side[drive]][cur_sector[drive]];

        switch (disc_sector_state[drive])
        {
                case STATE_IDLE:
		advance_byte();
                break;
                
                case STATE_READ_FIND_SECTOR:
                if (index_count[drive] > 1)
                {
			pclog("READ: Sector (%i %i %i %i) not found (last: %i %i %i %i) (period=%i,%i) (ic=%i)\n", s->c, s->h, s->r, s->n, disc_sector_track[drive], disc_sector_side[drive], disc_sector_sector[drive], disc_sector_n[drive], fdc_get_bitcell_period(), get_bitcell_period(drive), index_count[drive]);
                        fdc_notfound();
                        disc_sector_state[drive] = STATE_IDLE;
                        break;
                }
                if (/*cur_byte[drive] || */ disc_sector_track[drive] != s->c ||
                        disc_sector_side[drive] != s->h ||
                        disc_sector_sector[drive] != s->r ||
		        ((disc_sector_n[drive] != s->n) && (disc_sector_n[drive])) ||
                        (fdc_get_bitcell_period() != get_bitcell_period(drive)) ||
		        !fdd_can_read_medium(drive ^ fdd_swap) ||
                        disc_intersector_delay[drive] || disc_postdata_delay[drive] || disc_track_delay[drive] || disc_gap4_delay[drive])
                {
                        advance_byte();
                        break;
                }
                disc_sector_state[drive] = STATE_READ_SECTOR;

                case STATE_READ_SECTOR:
		if (!disc_postdata_delay[drive])
		{
	                if (fdc_data(s->data[cur_byte[drive]]))
        	        {
                	        return;
	                }
		}
                advance_byte();
                if (!cur_byte[drive] && disc_gap_has_ended(drive))
                {
                        disc_sector_state[drive] = STATE_IDLE;
                        fdc_finishread(drive);
                }
                break;

                case STATE_READ_FIND_FIRST_SECTOR:
		if (!fdd_can_read_medium(drive ^ fdd_swap))
		{
                        fdc_notfound();
                        disc_sector_state[drive] = STATE_IDLE;
                        break;
		}
                if (cur_byte[drive] || !index_count[drive] || fdc_get_bitcell_period() != get_bitcell_period(drive) ||
                        disc_intersector_delay[drive] || disc_postdata_delay[drive] || disc_track_delay[drive] || disc_gap4_delay[drive])
                {
                        advance_byte();
                        break;
                }
                disc_sector_state[drive] = STATE_READ_FIRST_SECTOR;

                case STATE_READ_FIRST_SECTOR:
		if (!disc_postdata_delay[drive])
		{
	                if (fdc_data(s->data[cur_byte[drive]]))
        	                return;
		}
                advance_byte();
                if (!cur_byte[drive] && disc_gap_has_ended(drive))
                {
                        disc_sector_state[drive] = STATE_IDLE;
                        fdc_finishread(drive);
                }
                break;

                case STATE_READ_FIND_NEXT_SECTOR:
		if (!fdd_can_read_medium(drive ^ fdd_swap))
		{
                        fdc_notfound();
                        disc_sector_state[drive] = STATE_IDLE;
                        break;
		}
                if (index_count[drive] > 0)
                {
                        fdc_notfound();
                        disc_sector_state[drive] = STATE_IDLE;
                        break;
                }
                if (cur_byte[drive] || (fdc_get_bitcell_period() != get_bitcell_period(drive)) ||
                        disc_intersector_delay[drive] || disc_postdata_delay[drive] || disc_track_delay[drive] || disc_gap4_delay[drive])
                {
                        advance_byte();
                        break;
                }
                disc_sector_state[drive] = STATE_READ_NEXT_SECTOR;

                case STATE_READ_NEXT_SECTOR:
		if (!disc_postdata_delay[drive])
		{
	                if (fdc_data(s->data[cur_byte[drive]]))
	                        break;
		}
                advance_byte();
                if (!cur_byte[drive] && disc_gap_has_ended(drive))
                {
                        disc_sector_state[drive] = STATE_IDLE;
                        fdc_finishread(drive);
                }
                break;

                case STATE_WRITE_FIND_SECTOR:
		if (!fdd_can_read_medium(drive ^ fdd_swap))
		{
                        fdc_notfound();
                        disc_sector_state[drive] = STATE_IDLE;
                        break;
		}
                if (writeprot[drive] || swwp)
                {
                        fdc_writeprotect();
                }
                if (index_count[drive] > 1)
                {
                        fdc_notfound();
                        disc_sector_state[drive] = STATE_IDLE;
                        break;
                }
                if (cur_byte[drive] || disc_sector_track[drive] != s->c ||
                    disc_sector_side[drive] != s->h ||
                    disc_sector_sector[drive] != s->r ||
		    ((disc_sector_n[drive] != s->n) && (disc_sector_n[drive])) ||
                    (fdc_get_bitcell_period() != get_bitcell_period(drive)) ||
                        disc_intersector_delay[drive] || disc_postdata_delay[drive] || disc_track_delay[drive] || disc_gap4_delay[drive])
                {
                        advance_byte();
                        break;
                }
                disc_sector_state[drive] = STATE_WRITE_SECTOR;

                case STATE_WRITE_SECTOR:
		if (!disc_postdata_delay[drive])
		{
	                data = fdc_getdata(cur_byte[drive] == ((128 << s->n) - 1));
        	        if (data == -1)
                	        break;
	                if (!disable_write)  s->data[cur_byte[drive]] = data;
		}
                advance_byte();
                if (!cur_byte[drive] && disc_gap_has_ended(drive))
                {
                        disc_sector_state[drive] = STATE_IDLE;
                        if (!disable_write)  disc_sector_writeback[drive](drive, disc_sector_track[drive]);
                        fdc_finishread(drive);
                }
                break;

                case STATE_READ_FIND_ADDRESS:
		if (!fdd_can_read_medium(drive ^ fdd_swap))
		{
                        fdc_notfound();
                        disc_sector_state[drive] = STATE_IDLE;
                        break;
		}
                if (index_count[drive] > 0)
                {
                        fdc_notfound();
                        disc_sector_state[drive] = STATE_IDLE;
                        break;
                }
                if (cur_byte[drive] || (fdc_get_bitcell_period() != get_bitcell_period(drive)) ||
                        disc_intersector_delay[drive] || disc_postdata_delay[drive] || disc_track_delay[drive] || disc_gap4_delay[drive])
                {
                        advance_byte();
                        break;
                }
                disc_sector_state[drive] = STATE_READ_ADDRESS;
                case STATE_READ_ADDRESS:
                fdc_sectorid(s->c, s->h, s->r, s->n, 0, 0);
                disc_sector_state[drive] = STATE_IDLE;
                break;
                
                case STATE_FORMAT_FIND:
                if (writeprot[drive] || swwp)
                {
                        fdc_writeprotect();
                }
                if (!index_count[drive] || (fdc_get_bitcell_period() != get_bitcell_period(drive)) ||
                        disc_intersector_delay[drive] || disc_postdata_delay[drive] || disc_track_delay[drive] || disc_gap4_delay[drive])
                {
                        advance_byte();
                        break;
                }
		if (!(fdd_can_read_medium(drive ^ fdd_swap)))
		{
                        fdc_notfound();
                        disc_sector_state[drive] = STATE_IDLE;
                        break;
		}
                if (fdc_get_bitcell_period() != get_bitcell_period(drive))
                {
                        fdc_notfound();
                        disc_sector_state[drive] = STATE_IDLE;
                        break;
                }
                disc_sector_state[drive] = STATE_FORMAT;

                case STATE_FORMAT:
                if (!disc_intersector_delay[drive] && fdc_get_bitcell_period() == get_bitcell_period(drive) && !disable_write)
                        s->data[cur_byte[drive]] = disc_sector_fill[drive];
                advance_byte();
                if (index_count[drive] == 2)
                {
                        if (!disable_write)  disc_sector_writeback[drive](drive, disc_sector_track[drive]);
                        fdc_finishread(drive);
                        disc_sector_state[drive] = STATE_IDLE;
                }
                break;
        }
}