/*
 *macros 
 */

#include <tcl.h>
#include <string.h>
#include "cdplayCmds.h"

#define strEQ(a,b) (*(a)==*(b)&&!strcmp(a,b))

#define Set_Int(interp, scratch, varname, f) \
	sprintf(scratch, "%d", f), Tcl_SetVar(interp, varname, scratch)

/*
 * Some functions for pervasive error messages in this module
 */
static int 
no_args_allowed(interp, arg0) 
Tcl_Interp *interp; char *arg0;
{
    Tcl_AppendResult(interp, 
	" ", arg0, " does not accept arguments", (char *)NULL);
    return TCL_ERROR;
}

static int 
args_should_be(interp, arg0, should)
Tcl_Interp *interp; char *arg0; char *should;
{
    Tcl_AppendResult(interp, 
	" ", arg0, ": wrong # of args: should be '", arg0, " ", should, "'", 
	(char *)NULL);
    return TCL_ERROR;
}

/* right now just one cd player is supported */
struct cdinfo {
    int fd;
    int initialized;
    int first_track;
    int last_track;
    unsigned char result[256];
};
struct cdinfo XX_cd_info;
struct cdinfo *cdInfoPtr = &XX_cd_info;
#define hp_cd_fd(name)		(cdInfoPtr->fd)
#define hp_cd_first_track(name)	(cdInfoPtr->first_track)
#define hp_cd_last_track(name)  (cdInfoPtr->last_track)
#define hp_cd_result(name)  	(cdInfoPtr->result)
#define hp_cd_initialized(name)	(cdInfoPtr->initialized)
#define hp_cd_result_clear(name) memset(cdInfoPtr->result, \
					'\0', sizeof(cdInfoPtr->result))

static int
getPlayer(interp, supplied, ret) 
Tcl_Interp *interp; 
char *supplied; 
char **ret;
{
    static char *n = (char *)0;
    if (!supplied && (*ret = n)) {
	return TCL_OK;
    } else {
	/* supplied */
	if (n) {
	    if (strcmp(n, supplied)) {
		free(n);
		n = strdup(supplied);
		*ret = supplied;
		return TCL_OK;
	    }
	} else {
	    n = strdup(supplied);
	    *ret = supplied;
	    return TCL_OK;
	}
    }
    Tcl_AppendResult(interp, "no default player", (char *)0);
    return TCL_ERROR;
}

static int
getTrack(interp, name, n, np)
Tcl_Interp *interp;
char *name;
char *n;
int *np;
{
    if (!strcmp("end", n)) {
	*np = hp_cd_last_track(name);
	return TCL_OK;
    }
    if (!strcmp("start", n)) {
	*np = hp_cd_first_track(name);
	return TCL_OK;
    }
    if (Tcl_GetInt(interp, n, np) == TCL_OK) {
	if ((*np>=hp_cd_first_track(name)) && 
	    (*np<=hp_cd_last_track(name)))
	return TCL_OK;
    }
    Tcl_AppendResult(interp, "bogus tracknumber", " '", n , "'", (char *)0);
    return TCL_ERROR;
}

int
playerCmd(cdata, interp, argc, argv)
    char *(* cdata)(); Tcl_Interp *interp; int argc; char **argv;
{
    char *ret, *p;
    int x;
    switch(argc) {
    case 2:
	x = getPlayer(interp, argv[1], &p);
	break;
    case 1:
	x = getPlayer(interp, NULL, &p);
	break;
    }
    if (x!= TCL_OK)
	    return args_should_be(interp, argv[0], "cdPlayerName");
    if ((ret = (char *)(*cdata)(p))){
	Tcl_AppendResult(interp, argv[0], ": '", ret, "'", (char *)NULL);
	return TCL_ERROR;
    }
    return TCL_OK;
}

int
playerRetStrCmd(cdata, interp, argc, argv)
    char *(* cdata)(); Tcl_Interp *interp; int argc; char **argv;
{
    char *ret, *p;
    int x;
    switch(argc) {
    case 2:
	x = getPlayer(interp, argv[1], &p);
	break;
    case 1:
	x = getPlayer(interp, NULL, &p);
	break;
    }
    if (x!= TCL_OK)
	    return args_should_be(interp, argv[0], "cdPlayerName");
    if ((ret = (char *)(*cdata)(p))){
	Tcl_AppendResult(interp, ret, (char *)NULL);
	return TCL_OK;
    }
    return TCL_ERROR;
}

/* cmd ?dev? trk */
int
playerTrkCmd(cdata, interp, argc, argv)
    char *(* cdata)(); Tcl_Interp *interp; int argc; char **argv;
{
    char *ret = (char *)0, *p = (char *)0;
    int track;
    switch(argc) {
    case 3:
	p = argv[1];
    case 2:
	p = (char *)0;
	break;
    default:
	return args_should_be(interp, argv[0], "?cdPlayerName? trackNumber");
    }
    if ((getPlayer(interp, p, &p)!=TCL_OK)||
        (getTrack(interp, p, argv[argc-1], &track)!=TCL_OK)) {
	ret = "";
    }
    if (ret) {
	return args_should_be(interp, argv[0], "?cdPlayerName? trackNumber");
    }
    if ((ret = (*cdata)(p, track))) {
	Tcl_AppendResult(interp, argv[0], ": '", ret, "'", (char *)NULL);
	return TCL_ERROR;
    }
    return TCL_OK;
}

#include <stdio.h>
#include <sys/scsi.h>
#include <fcntl.h>
#include <unistd.h>

struct scsi_cmd_parms CD_tracksearch = {
    10, 1, 500,
    0xc0, 0x00, 0x01, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x80
};

struct scsi_cmd_parms CD_play = {
    10, 1, 500,
    0xc1, 0x03, 0x02, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x80
};

struct scsi_cmd_parms CD_continue = {
    10, 1, 500,
    0xc1, 0x03, 0x02, 0x00, 0x00, 0x00,
    0xc0, 0x00, 0x00, 0x80
};

struct scsi_cmd_parms CD_trackinfo = {
    10, 1, 500,
    0xc7, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00
};

struct scsi_cmd_parms CD_playstatus = {
    10, 1, 500,
    0xc6, 0x0a, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00
};

struct scsi_cmd_parms CD_stop = {
    6, 1, 500,
    0x1b, 0x00, 0x00, 0x00, 0x00, 0x00
};

struct scsi_cmd_parms CD_eject = {
    10, 1, 500,
    0xc4, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00
};

struct scsi_cmd_parms CD_pause = {
    10, 1, 500,
    0xc2, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00
};



char *
hp_cd_cmd(name, cmd, arg) 
char *name; 
int cmd; 
char *arg;
{
    char *cp = hp_cd_init(name);
    if (cp) 
	return cp;
    if (ioctl(hp_cd_fd(name), cmd, arg) < 0)
	perror("hp_cd_cmd");
    hp_cd_result_clear(name);
    read(hp_cd_fd(name), hp_cd_result(name), 0xff);
    return (char *)NULL;
}

char *
hp_cd_trackinfo(name)
char *name;
{
    unsigned char *result;
    static char buf[100];

    /* find first,last track */
    hp_cd_cmd(name, SIOC_SET_CMD, &CD_trackinfo);
    result = hp_cd_result(name);

    hp_cd_first_track(name) = convy(result[0]); /* comes out in bcd */
    hp_cd_last_track(name) = convy(result[1]);
    sprintf(buf, "%d %d", hp_cd_first_track(name), hp_cd_last_track(name));
    return buf;
}

char *
hp_cd_search(name, track)
char *name;
int track;
{
    CD_tracksearch.command[2] = (unsigned char) (convx(track));
    return hp_cd_cmd(name, SIOC_SET_CMD, &CD_tracksearch);
}

char *
hp_cd_init(name)
char *name;
{
    int ret;
    int flag = 1;
    if (!name)
	return "no name supplied";
    if (hp_cd_initialized(name))
	return (char *)0;
    if (access(name, R_OK))
	return "not readable";
    /*
       Normally would open with  O_RDONLY, but O_RDWR may be necessary as a bug
       workaround
    */
    if ((hp_cd_fd(name) = open(name, O_RDWR)) < 0)
	return "open error";
    if ((ret = ioctl(hp_cd_fd(name), SIOC_CMD_MODE, &flag)) < 0) {
	close(hp_cd_fd(name));
	return "error setting CMD MODE";
    }
    hp_cd_initialized(name) = 1;
    hp_cd_trackinfo(name);
    return (char *)NULL;
}


char *
hp_cd_destroy(name)
char *name;
{
    if ((hp_cd_fd(name) > -1) && (close(hp_cd_fd(name))))
	return "close error";
    return (char *)NULL;
}

char *
hp_cd_eject(name)
char *name;
{
    hp_cd_cmd(name, SIOC_SET_CMD, &CD_eject);
    hp_cd_destroy(name);
    return (char *)0;
}

char *
hp_cd_stop(name)
char *name;
{
    return hp_cd_cmd(name, SIOC_SET_CMD, &CD_stop);
}

char *
hp_cd_pause(name)
char *name;
{
    return hp_cd_cmd(name, SIOC_SET_CMD, &CD_pause);
}

char *
hp_cd_continue(name)
char *name;
{
    CD_play.command[9] = 0xc0;
    return hp_cd_cmd(name, SIOC_SET_CMD, &CD_play);
}
	

char *
hp_cd_play(name, track)
char *name;
int track;
{
    if (track <0)
	track = hp_cd_last_track(name);
    CD_play.command[2] = (unsigned char) (convx(track + 1));
    if (track >= hp_cd_last_track(name))
	CD_play.command[2] = (unsigned char)0;
    return hp_cd_cmd(name, SIOC_SET_CMD, &CD_play);
}


/*
   Status=0 if playing, 
	 =1 if paused,
         =2 if audio track search completed, 
	 =3 if playback done.
*/
#define PLAYSTATUS_PLAYING 	0
#define PLAYSTATUS_PAUSED 	1
#define PLAYSTATUS_SEARCHDONE 	2
#define PLAYSTATUS_PLAYBACKDONE 3
#define PLAYSTATUS_NIL          4
static char *PLAYSTATUS_strings[] = {
    "playing",
    "paused",
    "done search",
    "done playback",
    (char *)0,
};

char *
hp_cd_playstatus(name)
char *name;
{
    static char ret[100];
    unsigned char *res;
    int status, number=-1, minutes=-1, seconds=-1; 

    hp_cd_cmd(name, SIOC_SET_CMD, &CD_playstatus);
    res = hp_cd_result(name);
    status = convy(res[0]);
    if (status==PLAYSTATUS_PLAYING) {
	sprintf(ret, "playing %d %d %d",
	    convy(res[2]), convy(res[4]), convy(res[5]));
	return ret;
    }
    if (status < 0 || status >= PLAYSTATUS_NIL) {
	sprintf(ret, "error %d", status);
	return ret;
    }
    return PLAYSTATUS_strings[status];
}


int
convx(number)	/* do an integer to bcd conversion */
    int     number;
{
    return (number + 6 * (number / 10)) & 0xff;
}

int
convy(number)	/* do a bcd to integer conversion */
    int     number;
{
    return (number / 16) * 10 + number % 16;
}
