/*
  cdda input class based on cdparanoia
  Copyright (C) 2000  Martin Vogt

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU Library General Public License as published by
  the Free Software Foundation.

  For more information look at the file COPYRIGHT in this package

 */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef CDDA_PARANOIA

#include <iostream>

using namespace std;

#include "cddaInputStream.h"
#include "inputDetector.h"


void paranoiaCallback(long, int) {
  //cout << "long:"<<a<<" int:"<<b<<endl;
}

CDDAInputStream::CDDAInputStream() {
  drive = NULL;
  paranoia=NULL;
  device=NULL;
  track=1;
}


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

// here we should encdoe the track Nr. as well
// eg: /dev/cdrom#1
int CDDAInputStream::getTrackAndDevice(const char* url) {
  int matches=0;
  // dest = "cdda:/dev/cdrom/track01.cda"
  char* noprotoString=InputDetector::removeProtocol(url);
  // noprotoString="/dev/cdrom/track01.cda"
  char* filename=InputDetector::getFilename(noprotoString);
  // filename="track01.cda"
  char* filenameNoExt=InputDetector::getWithoutExtension(filename);
  // filenameNoExt="track01"
  char* dir=InputDetector::removeExtension(noprotoString,filename);
  // dir="/dev/cdrom/"
  device=InputDetector::removeSlash(dir);
  track=1;
  if (filenameNoExt != NULL) {
    matches=sscanf(filenameNoExt,"track%02d",&track);
  }
  if (matches == 0) {
    cout << "no trackNumber found using default"<<endl;
  } 
  cout << "device:"<<device<<" track:"<<track<<endl;

  if (noprotoString != NULL) {
    delete noprotoString;
  }
  if (filename != NULL) {
    delete filename;
  }
  if (filenameNoExt != NULL) {
    delete filenameNoExt;
  }
  if (dir != NULL) {
    delete dir;
  }
  if (device == NULL) {
    cout << "no device found, using any"<<endl;
    return false;
  }
  return true;
}

int CDDAInputStream::open(const char* dest) {
  if (getTrackAndDevice(dest) == true) {
    drive = cdda_identify(device, CDDA_MESSAGE_PRINTIT, 0);
  }

  if (drive == NULL) {
    cout << "cdda_identify failed trying to find a device"<<endl;
    drive=cdda_find_a_cdrom(CDDA_MESSAGE_PRINTIT, 0);
  }
  if (drive == NULL) {
    cout << "nope. nothing found. give up"<<endl;
    return false;
  }
  cout << "cdda_open -s"<<endl;
  if (cdda_open(drive) != 0) {
    cout << "cdda_open(drive) failed"<<endl;
    close();
    return false;
  }
  cout << "cdda_open -e"<<endl;

  // debug things a bit
  int trackCount = drive->tracks;
  for (int i = 1; i <= trackCount; i++) {
    if (IS_AUDIO(drive, i)) {
      printf("track%02d.cda\n", i);
    } else {
      printf("no audio:%d\n",i);
    }
  }

  paranoia = paranoia_init(drive);
  if (paranoia == NULL) {
    cout << "paranoia init failed"<<endl;
    close();
    return false;
  }

  firstSector=cdda_track_firstsector(drive, track);
  lastSector=cdda_track_lastsector(drive, track);
  currentSector=firstSector;
  // paranoia && drive != NULL -> initialized!

  int paranoiaLevel = PARANOIA_MODE_FULL ^ PARANOIA_MODE_NEVERSKIP;
  paranoia_modeset(paranoia, paranoiaLevel);
  cdda_verbose_set(drive, CDDA_MESSAGE_PRINTIT, CDDA_MESSAGE_PRINTIT);
  paranoia_seek(paranoia, firstSector, SEEK_SET);

  return true;
}


void CDDAInputStream::close() {
  if (isOpen() == false) {
    return;
  }
  cdda_close(drive);
  drive=NULL;
  if (paranoia != NULL) {
    paranoia_free(paranoia);
    paranoia = 0;
  }
  if (device != NULL) {
    delete device;
    device=NULL;
  }
}


int CDDAInputStream::isOpen() {
  return (drive != NULL); 
}


int CDDAInputStream::eof() {
  if (isOpen()==false) {
    return true;
  }
  if (currentSector >= lastSector) {
    return true;  
  }
  return false;
}


int CDDAInputStream::read(char* dest,int len) {
  if (len != 2*CD_FRAMESIZE_RAW) {
    cout << "len must be 2*CD_FRAMESIZE_RAW"<<endl;
    exit(0);
  }
  int16_t * buf = paranoia_read(paranoia, paranoiaCallback);
  currentSector++;
  if (buf == NULL) {
    cout << "paranoia_read failed"<<endl;
    close();
    return 0;
  }
  memcpy(dest,buf,sizeof(int16_t)*CD_FRAMESIZE_RAW);
  return CD_FRAMESIZE_RAW;  
}


int CDDAInputStream::seek(long bytePos) {
  int byteLength=getByteLength();
  float ratio=(float)bytePos/(float)(byteLength+1);
  float wantSector=ratio*(float)((lastSector-firstSector));
  if (isOpen()) {
    currentSector=(int)wantSector;
    cout << "paranoia_seek:"<<currentSector<<endl;
    paranoia_seek(paranoia, currentSector, SEEK_SET);
  }
  return true;  
}

void CDDAInputStream::clear() {
  cout << "direct virtual call CDDAInputStream::clear:"<<endl;
}


long CDDAInputStream::getByteLength() {
  int sectors=lastSector-firstSector;
  int bytes=sectors*CD_FRAMESIZE_RAW*sizeof(int16_t);
  cout << "getByteLength:"<<bytes<<endl;
  return bytes;
}


long CDDAInputStream::getBytePosition() {
  int readSectors=currentSector-firstSector;
  int bytes=readSectors*CD_FRAMESIZE_RAW*sizeof(int16_t);
  return bytes;
}

#endif
//CDDA_PARANOIA