/*
 * cdda_lib.h: The disc drive access functions
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: cdda_device.h,v 1.17 2005/12/23 09:10:30 cm Exp $
 */

#ifndef __CDDADEVICE_H
#define __CDDADEVICE_H

#include <sys/types.h>
#include <cdio/cdtext.h>
#include <cdio/cdio.h>
#include <vdr/thread.h>
#include "gen_dynarray.h"

typedef enum {
  scLocked = -6,
  scCannotEjectMedia = -5,
  scOutOfMemory = -4,
  scCannotOpenDevice = -3,
  scFail = -2,
  scEOF = -1,
  scSuccess = 0,
} eCddaStatusCode;

const int CDDA_READ_MAX_SECTORS = 24;

inline unsigned long DataSizeToSeconds(unsigned long size) {
  // 44100 * 4
  return size / 176400;
}

inline float DataSizeToMSeconds(unsigned long size) {
  // (size / 176400) * 1000
  return float(size) / 176.4;
}

inline float DataSizeToFrames(unsigned long size) {
  // (size / 176.4) / 40
  return float(size) / 7056;
}

class cCddaTag {
  private:
    bool m_dirty;
    char *mp_title;
    char *mp_performer;
    char *mp_genre;
    char *mp_message;
    char *mp_arranger;
    char *mp_composer;
    char *mp_songwriter;
    char *mp_discid;
    char *mp_isrc;
    char *mp_tocinfo;
    char *mp_tocinfo2;
  public:
    cCddaTag();
    ~cCddaTag();
    bool isDirty(void) { return m_dirty; };
    bool SetDirty(bool dirty);
    const char *GetTitle(void) const { return mp_title; };
    const char *SetTitle(const char *title);
    const char *GetPerformer(void) const { return mp_performer; };
    const char *SetPerformer(const char *performer);
    const char *GetGenre(void) const { return mp_genre; };
    const char *SetGenre(const char *genre);
    const char *GetMessage(void) const { return mp_message; };
    const char *SetMessage(const char *message);
    const char *GetArranger(void) const { return mp_arranger; };
    const char *SetArranger(const char *arranger);
    const char *GetComposer(void) const { return mp_composer; };
    const char *SetComposer(const char *composer);
    const char *GetSongwriter(void) const { return mp_songwriter; };
    const char *SetSongwriter(const char *songwriter);
    const char *GetDiscId(void) const { return mp_discid; };
    const char *SetDiscId(const char *discid);
    const char *GetIsrc(void) const { return mp_isrc; };
    const char *SetIsrc(const char *isrc);
    const char *GetTocInfo(void) const { return mp_tocinfo; };
    const char *SetTocInfo(const char *tocinfo);
    const char *GetTocInfo2(void) const { return mp_tocinfo2; };
    const char *SetTocInfo2(const char *tocinfo2);
};

class cCddaTags {
  private:
    cDynamicArray<cCddaTag *> mo_tags;
    static int g_current;
    void Initialize(void);
  public:
    cCddaTags();
    ~cCddaTags();
    cCddaTag *Default(void) const { return Item(0); };
    cCddaTag *Current(void) const;
    int GetCurrent(void) const { return g_current; };
    cCddaTag *Next(void);
    int Count(void) const { return mo_tags.Count(); };
    int Contains(cCddaTag *tag) const { return mo_tags.Contains(tag); };
    cCddaTag *Item(int index) const { return mo_tags.Item(index); };
    cCddaTag *Add(cCddaTag *tag) { return mo_tags.Add(tag); };
    int Delete(int index);
};

class cCddaTrack {
  private:
    char m_numChannels;
    static uint8_t g_buffer[CDIO_CD_FRAMESIZE_RAW * CDDA_READ_MAX_SECTORS];
    track_t m_trackNum;
    CdIo_t *mp_cdIo; 
    lsn_t m_startLsn, m_endLsn;
    track_format_t m_trackFormat;
    cCddaTags o_tags;
    void ReadCDText(void);
    void Initialize(void);
  public:
    cCddaTrack(CdIo_t *cdIo, track_t index);
    ~cCddaTrack();
    bool isAudio(void);
    int GetChannels(void);
    int GetLba(void) const;
    unsigned long GetOffset(void) const;
    // returns the number of bytes
    unsigned long GetSize(void) const { return (m_endLsn - m_startLsn) * CDIO_CD_FRAMESIZE_RAW; };
    unsigned long GetSectorCount(void) const { return m_endLsn - m_startLsn; };
    uint8_t *ReadSector(long index, int& count);
    cCddaTags *Tags(void) { return &o_tags; };
};

class cCddaTracks {
  private:
    cCddaTrack *po_track[CDIO_CD_MAX_TRACKS];
    CdIo_t *p_cdIo;
    track_t firstTrack;
    track_t numTracks;
  public:
    cCddaTracks(CdIo_t *cdIo);
    ~cCddaTracks();
    unsigned int GetFirst(void);
    unsigned int Count(void);
    cCddaTrack *Item(track_t index);
    unsigned long GetSize(void);
};

class cCddaDisc {
  private:
    unsigned long cddbId;
    cCddaTracks *po_tracks;
    CdIo_t *p_cdIo;
    discmode_t discMode;
    cCddaTags o_tags;
    void ReadCDText(void);
    unsigned int GetSum(unsigned int number) const;
  public:
    cCddaDisc(CdIo_t *cdIo);
    ~cCddaDisc();
    bool isAudio(void);
    unsigned long GetCddbId(void);
    cCddaTracks *Tracks(void);
    cCddaTags *Tags(void) { return &o_tags; };
};

class cCddaDevice {
  private:
    unsigned char m_lock;
    char *mp_device;
    CdIo_t *mp_cdIo;
    cCddaDisc *mpo_disc;
    static cCddaDevice *gpo_device;
    cCddaDevice();
  public:
    ~cCddaDevice();
    static cCddaDevice& GetInstance(void);
    eCddaStatusCode Open(const char *name);
    eCddaStatusCode Close(void);
    cCddaDisc *Disc();
    eCddaStatusCode Eject(void);
    bool isOpen(void) const { return (NULL != mp_cdIo); };
    bool isLocked(void) const { return 0 != m_lock; };
    void Lock(void);
    void Unlock(void);
};

#endif
