#include <sys/types.h>
#include <linux/cdrom.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#include "cd_ll_linux.h"

cd_ll_linux::cd_ll_linux()
    : m_fd(-1)
{

}

cd_ll_linux::~cd_ll_linux()
{
    close();
}

void cd_ll_linux::open(void)
{
    if (m_fd != -1)
        return;
    
    m_fd = ::open("/dev/cdrom", O_RDONLY | O_NONBLOCK);
    
    if (m_fd == -1)
        fprintf(stderr, "Cannot open /dev/cdrom in non-blocking mode.\n");
}

void cd_ll_linux::close(void)
{
    if (m_fd == -1)
        return;
    
    ::close(m_fd);
}

bool cd_ll_linux::is_open(void)
{
    return (m_fd != -1);
}

int cd_ll_linux::read_toc_header(cd_ll_toc_header* toc_hdr)
{
    struct cdrom_tochdr tinfo;
    
    int x = ioctl(m_fd, CDROMREADTOCHDR, &tinfo);
    if (x == -1)
        return -1;

    toc_hdr->tracks = tinfo.cdth_trk1;
    
    return 0;
}

int cd_ll_linux::read_status(cd_ll_status* st)
{
    struct cdrom_subchnl ch;
    
    ch.cdsc_format=CDROM_MSF;
    
    int x = ioctl(m_fd, CDROMSUBCHNL, &ch);
    if (x == -1)
        return -1;

    switch (ch.cdsc_audiostatus) {
    case CDROM_AUDIO_PLAY:
        st->audio_status = CD_LL_PLAYING;
        st->track = ch.cdsc_trk;
        break;
    case CDROM_AUDIO_PAUSED:
        st->audio_status = CD_LL_PAUSED;
        break;
    case CDROM_AUDIO_NO_STATUS:
        st->audio_status = CD_LL_NO_STATUS;
        break;
    case CDROM_AUDIO_INVALID:
        st->audio_status = CD_LL_INVALID;
        break;
    case CDROM_AUDIO_COMPLETED:
        st->audio_status = CD_LL_PLAY_COMPLETED;
        break;
    }

    return 0;
}

int cd_ll_linux::start(void)
{
    ioctl(m_fd, CDROMSTART);

    return 0;
}

int cd_ll_linux::play(cd_ll_tracks* t)
{
    struct cdrom_ti ti;
    
    ti.cdti_trk0 = t->start_track;
    ti.cdti_trk1 = t->end_track;
    
    ioctl(m_fd, CDROMPLAYTRKIND, &ti);

    return 0;
}

int cd_ll_linux::stop(void)
{
    ioctl(m_fd, CDROMSTOP);
    
    return 0;
}

int cd_ll_linux::pause(void)
{
    ioctl(m_fd, CDROMPAUSE);

    return 0;
}

int cd_ll_linux::resume(void)
{
    ioctl(m_fd, CDROMRESUME);

    return 0;
}

int cd_ll_linux::eject(void)
{
    ioctl(m_fd, CDROMEJECT);
    
    return 0;
}

int cd_ll_linux::set_volume(cd_ll_volume* vol)
{
    struct cdrom_volctrl volctrl;
    
    volctrl.channel0 = vol->left;
    volctrl.channel1 = vol->right;
    
    ioctl(m_fd, CDROMVOLCTRL, &volctrl);

    return 0;
}

int cd_ll_linux::read_volume(cd_ll_volume* vol)
{
    struct cdrom_volctrl volctrl;
    
    int x = ioctl(m_fd, CDROMVOLREAD, &volctrl);
    if (x == -1)
        return -1;

    vol->left = volctrl.channel0;
    vol->right = volctrl.channel1;

    return 0;
}
