/* commands.c 
 *
 * Copyright 1994 thomas insel
 * Copyright 1995 sven oliver moll
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 dated June, 1991.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/file.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

#include "config.h"
#include "cdtool.h"
#include "commands.h"
#include "hardware.h"
#include "shuffle.h"
#include "util.h"
#include "info.h"

/************************************************************************
 * Procedure:  cdtool_open
 * Purpose:    finds device, opens it, allocates and reads *hw.
 * Returns:
 *  -1     failure
 *   0     success
 ************************************************************************/

int cdtool_open(cdtool_t *cdtool, char *device)
{
    int fd;
    char *cddev;
    cdhw_t *hw;

    /* tries detection if device is NULL */
    if ((cddev = device_detect(device)) == NULL) {
        if (!device)
            errormsg("%P: no cdrom found: Try option -d <device>");
        return(-1);
    }

    if ((fd = open(cddev, O_RDONLY | O_NONBLOCK)) < 0) {
        errormsg("%P: can't open cdrom (%s): %s", cddev, strerror(errno));
        if (errno == EACCES)
            show_permissions(cddev);
        return(-1);
    }

    hw = read_hw(fd);
    if (!hw)
        return(-1);

    cdtool->fd = fd;
    cdtool->device = cddev;
    cdtool->hw = hw;
    return(0);
}

/************************************************************************/
/* Procedure:  do_play
 * Purpose:    to start playing
 * Inputs:     CD file descriptor, initial track, last track
 * Returns:    0< on error; 0 on success
 * 
 ************************************************************************/
int do_play(cdtool_t *cd, int trk0, int trk1)
{
    int newlast;
    struct cdrom_ti ti;
    cdhw_t *hw = cd->hw;

    debugmsg("do_play:  called, trk0=%d, trk1=%d", trk0, trk1);

    if (!check_disc(hw)) {
        errormsg("%P: can't play (%s)", cd_status_text(hw->iStatus));
        return(-1);
    }

    /* special cases for track "any" */
    if ((trk0 == 0) && (trk1 == 0)) {
        debugmsg("do_play:  trk[0.1]==0, status=%#x", 
          hw->subchnl.cdsc_audiostatus);

        /* subchannel status returns PLAY, PAUSED, COMPLETED, or none */
        switch (hw->subchnl.cdsc_audiostatus) {
            case CDROM_AUDIO_PAUSED:
                if (ioctl(cd->fd, CDROMRESUME) == -1) {
                    errormsg("%P: ioctl cdromresume: %s", strerror(errno));
                    return(-1);
                }
                return(0);

            case CDROM_AUDIO_PLAY:
                trk0 = hw->subchnl.cdsc_trk;
                break;

            case CDROM_AUDIO_COMPLETED:  /* WWH added */
                trk0 = 1;
                break;

            case CDROM_AUDIO_NO_STATUS:  /* ignore */
                debugmsg("%P: ioctl subchnl: NO_STATUS");
                break;

            case CDROM_AUDIO_INVALID:	 /* ignore */
                debugmsg("%P: ioctl subchnl: INVALID");
                break;
		
            case CDROM_AUDIO_ERROR:
                errormsg("%P: ioctl subchnl: ERROR");
                return(-1);

            default:
                errormsg("%P: ioctl subchnl: unknown status: subchnl=%#x",
                  hw->subchnl.cdsc_audiostatus);
                return(-1);
        }
    }

    /* default to TOC start/end tracks */
    ti.cdti_trk0 = trk0 > 0 ? trk0 : hw->tochdr.cdth_trk0;
    ti.cdti_trk1 = trk1 > 0 ? trk1 : hw->tochdr.cdth_trk1;

    /* set track indices (XX what for?) */
    ti.cdti_ind0 = 1;
    ti.cdti_ind1 = 99;
    
    /* XXXX */
    if (ti.cdti_trk0 < first_track(hw))
        ti.cdti_trk0 = first_track(hw);

    /* XXX */
    if (ti.cdti_trk1 > last_track(hw))
        ti.cdti_trk1 = last_track(hw);
    
    /* check range for non-audio tracks */
    newlast = check_audio_range(hw, ti.cdti_trk0, ti.cdti_trk1);
    if (newlast < 0) {
        errormsg("%P: no audio tracks within requested range");
        return(-1);
    }

    /* range may include data tracks; play up to last audio track */
    if (newlast < ti.cdti_trk1)
        ti.cdti_trk1 = newlast;
    
    verbosemsg("Playing from track %d to %d", ti.cdti_trk0, ti.cdti_trk1);
    return do_play_ti(cd->fd, cd->hw, &ti);
}

/************************************************************************/
/* Procedure:  do_skip
 * Purpose:    skip forwards or backwards and (re-)start playing
 * Returns:    0< on error; 0 on success
 *
 *   if direction is -1, skip back
 *   if direction is -2, skip forward
 * 
 ************************************************************************/
int do_skip(cdtool_t *cd, int direction, int skip)
{
    cdhw_t *hw = cd->hw;
    int track = 0;

    if (!check_disc(hw)) {
        errormsg("%P: can't skip (%s)", cd_status_text(hw->iStatus));
        return(-1);
    }

    if (skip < 1)
        skip = 1;

    /* skip back/forward from current track */
    switch (direction) {
        case SKIPBACK:
            track = hw->subchnl.cdsc_trk - skip;
            break;
        case SKIPFORWARD:
            track = hw->subchnl.cdsc_trk + skip;
    }

    /* check against TOC range */
    if ((track < hw->tochdr.cdth_trk0) || (track > hw->tochdr.cdth_trk1))
        track = 1;

    /* play from 'track' to last track */
    return do_play(cd, track, hw->tochdr.cdth_trk1);
}

/************************************************************************/
/* Procedure:  do_pause
 * Purpose:    to pause CD-ROM
 * 
 * Inputs:     program name, CD file des.
 * Outputs:    to CD-ROM, debug output
 * Returns:    0< on error; 0 on success
 * Notes:  
 *   1.
 */
/************************************************************************/
int do_pause(cdtool_t *cd) 
{
    cdhw_t *hw = cd->hw;
    verbosemsg("Pausing");
    
    /* pause only if disc inserted */
    if (!check_disc(hw)) {
        errormsg("%P: can't pause (%s)", cd_status_text(hw->iStatus));
        return(-1);
    }

    debugmsg("do_pause: iStatus=%d subchnl_status=%d",
        hw->iStatus,  hw->subchnl.cdsc_audiostatus);

    /* pause only if currently playing */
    if (hw->subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY) {
        debugmsg("do_pause: not pausing: status != play");
        return(0);
    }

    if (ioctl(cd->fd, CDROMPAUSE) == -1) {
        errormsg("%P: ioctl cdrompause: %s", strerror(errno));
        return(-1);
    }

    return(0);
}

/************************************************************************/
/* Procedure:  do_stop
 * Purpose:    to stop playing a CD-ROM
 * 
 * Inputs:     program name, CD file des.
 * Outputs:    to CD-ROM, debug output
 * Returns:    0< on error; 0 on success
 * Notes:  
 *   1.
 */
/************************************************************************/
int do_stop(cdtool_t *cd, int iCDNum) 
{
    debugmsg("do_stop: called, cdfd=%d", cd->fd);
    verbosemsg("Stopping");

    /* if shuffle is running, stop it... */
    shuffle_unlock(iCDNum, 1);

    /* send stop to the CDROM device */
    if (ioctl(cd->fd, CDROMSTOP) == -1) {
	errormsg("%P: ioctl cdromstop: %s", strerror(errno));
	return(-1);
    }

    return(0);
}

/************************************************************************/
/* Procedure:  do_eject
 * Purpose:    to eject the CD-ROM 
 * 
 * Inputs:     program name, CD file des.
 * Outputs:    to CD-ROM, debug output
 * Returns:    0< on error; 0 on success
 * Notes:  
 *   1.
 */
/************************************************************************/
int do_eject(cdtool_t *cd)
{
    debugmsg("do_eject:  called, cdfd=%d", cd->fd);
    verbosemsg("Ejecting CD-ROM");

    if (ioctl(cd->fd, CDROMEJECT) == -1) {
        errormsg("%P: ioctl cdromeject: %s", strerror(errno));
        return(-1);
    }

    return(0);
}

/************************************************************************/
/* Procedure:  do_close
 * Purpose:    to close the CD-ROM 
 * 
 * Inputs:     program name, CD file des.
 * Outputs:    to CD-ROM, debug output
 * Returns:    0< on error; 0 on success
 * Notes:  
 *   1.
 */
/************************************************************************/
int do_close(cdtool_t *cd)
{
    debugmsg("do_close: called, cdfd=%d", cd->fd);
    verbosemsg("Closing CD-ROM tray\n");

#ifdef HAVE_IOCTL_CDROMCLOSETRAY
    if (ioctl(cd->fd, CDROMCLOSETRAY) == -1) {
        errormsg("%P: ioctl cdromclose: %s", strerror(errno));
        return(-1);
    }
#endif

    return(0);
}

/************************************************************************/
/* Procedure:  do_reset
 * Purpose:    to reset the CD-ROM
 * 
 * Inputs:     program name, CD file des.
 * Outputs:    to CD-ROM, debug output
 * Returns:    n/a
 * Notes:  
 *   1.
 */
/************************************************************************/
int do_reset(cdtool_t *cd)
{
    debugmsg("do_reset:  called, cdfd=%d", cd->fd);
    verbosemsg("Reseting CDROM\n");

#ifdef HAVE_IOCTL_CDROMRESET
    if (ioctl(cd->fd, CDROMRESET) == -1) {
        errormsg("%P: ioctl cdromreset: %s", strerror(errno));
        return(-1);
    }
#else
    /* Solaris doesn't know CDROMRESET */
    if (ioctl(cd->fd, CDROMSTOP) == -1) {
        errormsg("%P: ioctl cdromstop: %s", strerror(errno));
        return(-1);
    }
#endif

    if (ioctl(cd->fd, CDROMSTART) == -1) {
        errormsg("%P: ioctl cdromstart: %s", strerror(errno));
        return(-1);
    }

    return(0);
}

/************************************************************************/
/* Procedure:  do_volume
 * Purpose:    to adjust CD-ROM volume level
 * 
 * Inputs:     program name, CD file des., volume level (uchar)
 * Outputs:    to CD-ROM, to stdout
 * Returns:    0 on success, -1 on failure
 * Notes:  
 *   1.
 */
/************************************************************************/
int do_volume(cdtool_t *cd, int level)
{
    struct cdrom_volctrl volctrl;
    debugmsg("do_volume:  called, volume=%d, cdfd=%d", level, cd->fd);

    if ((level < 0) || (level > 255)) {
        errormsg("%P: illegal volume (allowed values 0-255)");
        return -1;
    }

    verbosemsg("Setting volume to level %d", level);

    volctrl.channel0=level;
    volctrl.channel1=level;
    volctrl.channel2=level;
    volctrl.channel3=level;

    if (ioctl(cd->fd, CDROMVOLCTRL, &volctrl) == -1) {
        errormsg("%P: CDROMVOLCTRL ioctl failed: %s\n", strerror(errno));
        return -1;
    }

    return 0;
}

