#ifndef FLOPDRV_H
#define FLOPDRV_H

/* flopdrv provides simple emulation of a disc drive */
/* the 8271, nec765 and wd179x use this */
#include "image.h"

typedef enum
{
	DEN_FM_LO = 0,
	DEN_FM_HI,
	DEN_MFM_LO,
	DEN_MFM_HI
} DENSITY;

/* sector has a deleted data address mark */
#define ID_FLAG_DELETED_DATA	0x0001
/* CRC error in id field */
#define ID_FLAG_CRC_ERROR_IN_ID_FIELD 0x0002
/* CRC error in data field */
#define ID_FLAG_CRC_ERROR_IN_DATA_FIELD 0x0004

int	floppy_status(mess_image *img, int new_status);

typedef struct chrn_id
{
	unsigned char C;
	unsigned char H;
	unsigned char R;
	unsigned char N;
	int data_id;			// id for read/write data command
	unsigned long flags;
} chrn_id;

/* set if disc is write protected - also set if drive is present but no disc in drive */
#define FLOPPY_DRIVE_DISK_WRITE_PROTECTED		0x0002
/* set if drive is connected and head is positioned over track 0 */
#define FLOPPY_DRIVE_HEAD_AT_TRACK_0			0x0004
/* set if drive is ready */
#define FLOPPY_DRIVE_READY						0x0010
/* set if index has just occured */
#define FLOPPY_DRIVE_INDEX						0x0020
/* motor state */
#define FLOPPY_DRIVE_MOTOR_ON					0x0040


typedef struct floppy_interface
{
	/* seek to physical track */
	void (*seek_callback)(mess_image *img, int physical_track);

	/* the following are not strictly floppy drive operations, but are used by the
	nec765 to get data from the track - really the whole track should be constructed
	into the raw format the nec765 would normally see and this would be totally accurate */
	/* the disc image would then have to re-interpret this back and update the image
	with the data */

	/* get number of sectors per track on side specified */
	int (*get_sectors_per_track)(mess_image *image, int physical_side);

	/* get id from current track and specified side */
	void (*get_id_callback)(mess_image *image, chrn_id *, int id_index, int physical_side);

	/* read sector data into buffer, length = number of bytes to read */
	void (*read_sector_data_into_buffer)(mess_image *image, int side,int data_id,char *, int length);

	/* write sector data from buffer, length = number of bytes to read  */
	void (*write_sector_data_from_buffer)(mess_image *image, int side,int data_id, const char *, int length, int ddam);

	/* Read track in buffer, length = number of bytes to read */
	void (*read_track_data_info_buffer)(mess_image *image, int side, void *ptr, int *length );

	/* Write track in buffer, length = number of bytes to read */
	void (*write_track_data_info_buffer)(mess_image *image, int side, const void *ptr, int *length );

	/* format */
	void (*format_sector)(mess_image *img, int side, int sector_index,int c, int h, int r, int n, int filler);
} floppy_interface;

struct floppy_drive
{
	/* flags */
	int flags;
	/* maximum track allowed */
	int max_track;
	/* num sides */
	int num_sides;
	/* current track - this may or may not relate to the present cylinder number
	stored by the fdc */
	int current_track;

	/* index pulse timer */
	void	*index_timer;
	/* index pulse callback */
	void	(*index_pulse_callback)(mess_image *image, int state);
	/* rotation per minute => gives index pulse frequency */
	float rpm;
	/* current index pulse value */
	int index;

	void	(*ready_state_change_callback)(mess_image *img, int state);

	unsigned char id_buffer[4];

	int id_index;
    chrn_id ids[32];

	floppy_interface interface_;
};

/* a callback which will be executed if the ready state of the drive changes e.g. not ready->ready, ready->not ready */
void floppy_drive_set_ready_state_change_callback(mess_image *img, void (*callback)(mess_image *img, int state));

/* floppy drive types */
typedef enum
{
	FLOPPY_DRIVE_SS_40,
	FLOPPY_DRIVE_DS_80
} floppy_type;

void floppy_drive_set_index_pulse_callback(mess_image *img, void (*callback)(mess_image *image, int state));

/* set flag state */
int floppy_drive_get_flag_state(mess_image *img, int flag);
/* get flag state */
void floppy_drive_set_flag_state(mess_image *img, int flag, int state);
/* get current physical track drive is on */
int floppy_drive_get_current_track(mess_image *img);

void floppy_drive_set_geometry(mess_image *img, floppy_type type);
void floppy_drive_set_geometry_absolute(mess_image *img, int tracks, int sides);

/* called in device init/exit functions */
int floppy_drive_init(mess_image *img, const floppy_interface *iface);

/* get next id from track, 1 if got a id, 0 if no id was got */
int floppy_drive_get_next_id(mess_image *img, int side, chrn_id *);
/* set ready state of drive. If flag == 1, set ready state only if drive present,
disk is in drive, and motor is on. Otherwise set ready state to the state passed */
void floppy_drive_set_ready_state(mess_image *img, int state, int flag);

void floppy_drive_set_motor_state(mess_image *img, int state);

/* deprecated; set interface for disk image functions */
void floppy_drive_set_disk_image_interface(mess_image *img, const floppy_interface *iface);

/* seek up or down */
void floppy_drive_seek(mess_image *img, signed int signed_tracks);

void floppy_drive_read_track_data_info_buffer(mess_image *img, int side, void *ptr, int *length );
void floppy_drive_write_track_data_info_buffer(mess_image *img, int side, const void *ptr, int *length );
void floppy_drive_format_sector(mess_image *img, int side, int sector_index, int c, int h, int r, int n, int filler);
void floppy_drive_read_sector_data(mess_image *img, int side, int index1, void *pBuffer, int length);
void floppy_drive_write_sector_data(mess_image *img, int side, int index1, const void *pBuffer, int length, int ddam);
int	floppy_drive_get_datarate_in_us(DENSITY density);

/* set motor speed to get correct index pulses
   standard RPM are 300 RPM (common) and 360 RPM
   Note: this actually only works for soft sectored disks: one index pulse per
   track.
*/
void floppy_drive_set_rpm(mess_image *image, float rpm);

#endif /* FLOPDRV_H */
