#include "sysheaders.h"
#include "cdpanel.h"
#include "CompactPlayer.h"

/********************************************************
 * SCSI
 */

struct MsgPort *CDPort;
struct IOStdReq *CDIO;
struct IOStdReq *CDIO2;
union CDTOC CDTOC[100];

#define BLOCKSPERSECOND 75

int __stdargs
Open_CD(void)
{
	if ((CDPort		= CreateMsgPort()) &&
		(CDIO		= CreateIORequest(CDPort, sizeof(struct IOStdReq))) &&
		(CDIO2		= CreateIORequest(CDPort, sizeof(struct IOStdReq))) &&
		!OpenDevice(Device, Unit, (struct IORequest *)CDIO, NULL))
	{
		*CDIO2 = *CDIO;
		CDIO2->io_Command = 0;

		CD_Eject = Eject_CD;
		CD_Stop = Stop_CD;
		CD_Play = Play_CD;
		CD_PauseResume = PauseResume_CD;
		CD_Seek = Seek_CD;
		CD_Snoop = Snoop_CD;
		CD_ReadTOC = ReadTOC_CD;
		CD_IsCD = IsCD_CD;
		CD_Close = (void (*)(void))Close_CD;

		if (CD_IsCD())
			return 0;
	}
	ErrorMsg(GS(DEVICE_FAIL), Device, Unit);
	Close_CD();
	return -1;
}

void __stdargs
Close_CD(void)
{
	if (CDIO2)
	{
		if (CDIO2->io_Command != 0)
		{
			AbortIO((struct IORequest *)CDIO2);
			WaitIO((struct IORequest *)CDIO2);
		}
		DeleteIORequest(CDIO2);
	}
	if (CDIO)
	{
		if (CDIO->io_Command != 0)
			CloseDevice((struct IORequest *)CDIO);
		DeleteIORequest(CDIO);
		CDIO = NULL;
	}
	if (CDPort)
	{
		DeleteMsgPort(CDPort);
		CDPort = NULL;
	}

	CD_Eject = (void (*)(UBYTE))Dummy;
	CD_Stop = (void (*)(void))Dummy;
	CD_Play = (void (*)(ULONG))Dummy;
	CD_PauseResume = (void (*)(UBYTE))Dummy;
	CD_Seek = (void (*)(LONG))Dummy;
	CD_Snoop = (ULONG (*)(ULONG *, ULONG *, ULONG *, ULONG))Dummy;
	CD_ReadTOC = (ULONG (*)(void))Dummy;
	CD_IsCD = (BOOL (*)(void))Dummy;
	CD_Close = (void (*)(void))Dummy;
}

static int
DoCD( ULONG cmd, APTR data, int dsize )
{
	if (CDIO)
	{
		CDIO->io_Command = cmd;
		CDIO->io_Offset = 0;
		CDIO->io_Data = data;
		CDIO->io_Length = dsize;
		
		DoIO((struct IORequest *)CDIO);
		
		return CDIO->io_Error;
	}
	return -1;
}

void
Eject_CD(UBYTE out)
{
	DoCD( CD_EJECT, NULL, out ? 1 : 0 );
}

void
Stop_CD(void)
{
	if (CDIO2->io_Command != 0)
	{
		AbortIO((struct IORequest *)CDIO2);
		WaitIO((struct IORequest *)CDIO2);
	}
}

void
Play_CD(ULONG track)
{
	ULONG address, length;
	
	/* Play a track. Basic program (random order) support in here, even
	 * though there is no program editor yet. */
	
	if (TOCP[0] != ~0)
	{
		if (TOCP[track] == ~0)
			return;
		address = TOCL[TOCP[track-1]]+1;
	}
	else
	{
		if (track > Tracks)
			return;
		address = TOCL[track-1]+1;
	}
	Track = track;

	CDIO2->io_Offset = address;
	
#ifdef SINGLE_TRACK_PLAY
	if (TOCP[0] != ~0)
		length = TOCL[TOCP[track]] - address - 1;
	else
		length = TOCL[Tracks-1] - address - 1;

	CDIO2->io_Length = length;
#else
	/* continuous play */
	if (TOCP[0] != ~0)
		EndAddress = TOCL[TOCP[track]] - 1;
	else
		EndAddress = TOCL[Tracks-1] - 1;

	CDIO2->io_Length = EndAddress - address;
#endif
	
	/* because a Toshiba XM3601B can abort a PLAY_AUDIO sent immediately
	 * after media change without any error code, we have a kludge that makes
	 * it restart. */
	JustStarted = TRUE;
	
	CDIO2->io_Command = CD_PLAYLSN;
	SendIO((struct IORequest *)CDIO2);
}

void
PauseResume_CD( UBYTE pause )
{
	DoCD( CD_PAUSE, NULL, pause ? 1 : 0 );
}

void
Seek_CD( LONG seconds )
{
	
}

ULONG
Snoop_CD( ULONG *track, ULONG *tracktime, ULONG *time, ULONG ostat )
{
	static struct QCode qc;
	static struct CDInfo ci;
	ULONG status;
	
	if (!DoCD( CD_QCODELSN, &qc, 0 ))
	{
		Track = *track = qc.Track;
		*time = qc.DiskPosition.LSN / BLOCKSPERSECOND;
		*tracktime = qc.TrackPosition.LSN / BLOCKSPERSECOND + 3 /* !!!! why the hell? */;
	}
	if (!DoCD( CD_INFO, &ci, sizeof(ci) ))
	{
		if (!(ci.Status & CDSTSF_CLOSED))
		{
			Ejected = TRUE;
			status = CDP_EJECTED;
		}
		else
		{
			Ejected = FALSE;
			status = CDP_EMPTY;

			if (ci.Status & CDSTSF_DISK)
			{
				Ejected = FALSE;
				status = CDP_STOPPED;
			}
			if (ci.Status & CDSTSF_PLAYING)
			{
#ifndef SINGLE_TRACK_PLAY
				if (*time >= EndAddress)
				{
					CD_Play(Track + 1);
					return CD_Snoop(track, tracktime, time, CDP_STOPPED);
				}
#endif
				status = CDP_PLAYING;
			}
			if (ci.Status & CDSTSF_PAUSED)
			{
				status = CDP_PAUSED;
			}
			if (!(ci.Status & (CDSTSF_PLAYING|CDSTSF_PAUSED)))
			{
				if (JustStarted && !(qc.CtlAdr & CTL_DATA))
				{
					CD_Play(Track);
					return CD_Snoop(track, tracktime, time, CDP_STOPPED);
				}
				else
				if (ostat == CDP_PLAYING)
				{
					CD_Play(Track + 1);
					return CD_Snoop(track, tracktime, time, CDP_STOPPED);
				}
				JustStarted = FALSE;
				*track = Track = 0;
				return CDP_STOPPED;
			}
			if (*tracktime > 1)
				JustStarted = TRUE;
		}
			
		return status;
	}
	return CDP_EMPTY;
}

ULONG
ReadTOC_CD(void)
{
	if (!DoCD( CD_TOCLSN, CDTOC, 100 ))
	{
		union CDTOC *toc;
		ULONG tracks = 0;
		
		for (toc = &CDTOC[1]; tracks < CDTOC[0].Summary.LastTrack; toc++)
		{
			TOCL[tracks] = toc->Entry.Position.LSN + 32; /* adding 32 makes it scsi.device compat */
			TOCT[tracks] = TOCL[tracks] / BLOCKSPERSECOND;
			TOCF[tracks] = toc->Entry.CtlAdr & CTL_DATA ? 1 : 0;
			TOCS[tracks] = &TitleBuffer[(tracks+2)*40];
			Sprintf(TOCS[tracks], GS(TRACK_NUM), tracks+1);
			tracks++;
		}
		TOCS[tracks] = NULL;
		TOCL[tracks] = CDTOC[0].Summary.LeadOut.LSN + 32;
		TOCT[tracks] = TOCL[tracks] / BLOCKSPERSECOND;

		if (!GetIndex( tracks, TOCL[2], TOCL[tracks]))
		{
			TITLE[0] = NULL;
			TITLE[1] = NULL;
		}
		
		if (!TITLE[0])
		{
			TITLE[0] = &TitleBuffer[0];
			strcpy(TITLE[0], GS(UNKNOWN_ARTIST));
		}

		if (!TITLE[1])
		{
			TITLE[1] = &TitleBuffer[40];
			strcpy(TITLE[1], GS(UNKNOWN_TITLE));
		}

		return tracks;
	}
	return 0;
}

BOOL
IsCD_CD(void)
{
	static struct CDInfo ci;
	
	return (BOOL)(DoCD( CD_INFO, &ci, sizeof(ci) ) ? FALSE : TRUE);
}
