#include <sys/types.h>
#include <sys/stat.h>
#include <sys/cdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "cd_ll_bsd.h"

// the cd-rom device file
#define DEVICE "/dev/acd0c"

cd_ll_bsd::cd_ll_bsd()
    : m_fd(-1)
{

}

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

void cd_ll_bsd::open(void)
{
    if (m_fd != -1)
        return;

    m_fd = ::open(DEVICE, O_RDONLY);
    
    if (m_fd == -1)
        fprintf(stderr, "Cannot open %s: %s.\n",
                DEVICE, strerror(errno));
}

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

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

int cd_ll_bsd::read_toc_header(cd_ll_toc_header* toc_hdr)
{
    struct ioc_toc_header tinfo;
    
    int x = ioctl(m_fd, CDIOREADTOCHEADER, &tinfo);
    if (x == -1)
        return -1;

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

int cd_ll_bsd::read_status(cd_ll_status* st)
{
    struct ioc_read_subchannel ch;
    struct cd_sub_channel_info ch_sub;
    
    bzero(&ch, sizeof(ch));
    ch.data = &ch_sub;
    ch.data_len = sizeof(ch_sub);
    
    ch.address_format = CD_MSF_FORMAT;
    ch.data_format = CD_CURRENT_POSITION;
    
    int x = ioctl(m_fd, CDIOCREADSUBCHANNEL, (char*)&ch);
    if (x == -1)
        return -1;

    switch (ch.data->header.audio_status) {
    case CD_AS_PLAY_IN_PROGRESS:
        st->audio_status = CD_LL_PLAYING;
        st->track = ch.data->what.track_info.track_number;
        break;
    case CD_AS_PLAY_PAUSED:
        st->audio_status = CD_LL_PAUSED;
        break;
    case CD_AS_NO_STATUS:
        st->audio_status = CD_LL_NO_STATUS;
        break;
    case CD_AS_AUDIO_INVALID:
        st->audio_status = CD_LL_INVALID;
        break;
    case CD_AS_PLAY_COMPLETED:
        st->audio_status = CD_LL_PLAY_COMPLETED;
        break;
    }

    return 0;
}

int cd_ll_bsd::start(void)
{
    ioctl(m_fd, CDIOCSTART);

    return 0;
}

int cd_ll_bsd::play(cd_ll_tracks* t)
{
    struct ioc_play_track ti;
    
    ti.start_track = t->start_track;
    ti.end_track = t->end_track;
    
    ioctl(m_fd, CDIOCPLAYTRACKS, &ti);

    return 0;
}

int cd_ll_bsd::stop(void)
{
    ioctl(m_fd, CDIOCSTOP);
    
    return 0;
}

int cd_ll_bsd::pause(void)
{
    ioctl(m_fd, CDIOCPAUSE);

    return 0;
}

int cd_ll_bsd::resume(void)
{
    ioctl(m_fd, CDIOCRESUME);

    return 0;
}

int cd_ll_bsd::eject(void)
{
    ioctl(m_fd, CDIOCEJECT);
    
    return 0;
}

int cd_ll_bsd::set_volume(cd_ll_volume* vol)
{
    struct ioc_vol volctrl;

    volctrl.vol[0] = vol->left;
    volctrl.vol[1] = vol->right;
    
    ioctl(m_fd, CDIOCSETVOL, &volctrl);

    return 0;
}

int cd_ll_bsd::read_volume(cd_ll_volume* vol)
{
    struct ioc_vol volctrl;
    
    int x = ioctl(m_fd, CDIOCGETVOL, &volctrl);
    if (x == -1)
        return -1;

    vol->left = volctrl.vol[0];
    vol->right = volctrl.vol[1];

    return 0;
}
