#include "mcdp.h"

short cddb; /* if 0, we have no entries and no remote stuff */
static void cd_playmsf(struct mcdp *cd, struct cdrom_msf *msf);

static void cd_playmsf(struct mcdp *cd, struct cdrom_msf *msf) {
 if (cd->status == CDROM_AUDIO_PLAY)
  if (ioctl(cd->fd,CDROMPAUSE,NULL)<0)
   return;
 if (ioctl(cd->fd,CDROMPLAYMSF, msf)<0)
  draw_status("error playing",1);
 return;
}

/* GLOBAL */

/**
 * returns 1, if devfile is okay
 */
int cd_try_some_dev(const char *devfile, struct mcdp *cd)
{
	if (!devfile) return 0;

	cd->fd = open(devfile, O_RDONLY|O_NONBLOCK);
	if (cd->fd == -1) return 0;

	cd->status = ioctl(cd->fd,CDROM_DRIVE_STATUS, NULL);
	if (cd->status == -1) {
		close(cd->fd);
		return 0;
	}

	return 1;
}

/* open device or exit ! */
void cd_initdev(const char *cdpath, struct mcdp *cd) {
 int r;

 /* 1. check argv[1] */
 r = cd_try_some_dev(cdpath, cd);
 if (r) return;

 /* 2. check $CDROM */
 r = cd_try_some_dev(getenv("CDROM"), cd);
 if (r) return;

 /* 3. try some defaults */
 r = cd_try_some_dev("/dev/sr0", cd);
 if (r) return;
 r = cd_try_some_dev("/dev/scd0", cd);
 if (r) return;
 r = cd_try_some_dev("/dev/cdrom", cd);
 if (r) return;
 r = cd_try_some_dev("/dev/cdroms/cdrom0", cd);
 if (r) return;

 return;
}

/* look at the specs, should do, what they want :) */
unsigned cd_discid(struct mcdp *cd) {
 unsigned cd_cddbsum(register int n) {
  register unsigned int ret=0;
  while (n>0) {
   ret += (n%10); n /= 10;
  }
  return ret;
 }
 int i=1, n=0;
 while (i<=cd->title[1]) {
  n+=cd_cddbsum(cd->t[i].cddb/75);i++;
 }
 return ((n%0xff)<<24|(cd->t[0].cddb - cd->t[1].cddb/75)<<8|cd->title[1]);
}

/* needs correct cd->fd; seems very efficient (no division...) */
int cd_readtracks(struct mcdp *cd) {
 struct cdrom_tochdr cdth;        /* TocHdr Stuff */
 register int i;

 if (ioctl(cd->fd, CDROMREADTOCHDR, &cdth)<0) {
  draw_status("can't read disc",1);
  return -1;
 }
 /* track[1] - [last-1])*/
 for (i=1;i<=cdth.cdth_trk1 && i<=MCDP_CDAUDIO_MAX;i++) {
  cd->cdte[i].cdte_track=i;
  cd->cdte[i].cdte_format=CDROM_MSF;
  if (ioctl(cd->fd, CDROMREADTOCENTRY, &cd->cdte[i])<0)
   return -1;
  else {
   cd->t[i].audio=cd->cdte[i].cdte_ctrl & CDROM_DATA_TRACK;
   cd->t[i].min=cd->cdte[i].cdte_addr.msf.minute;
   cd->t[i].sec=cd->cdte[i].cdte_addr.msf.second;
   cd->t[i].cddb = cd->cdte[i].cdte_addr.msf.minute*60*75 +
     cd->cdte[i].cdte_addr.msf.second*75 +
     cd->cdte[i].cdte_addr.msf.frame; /* cddb[track] */
   cd->t[i-1].min=cd->t[i].min-cd->t[i-1].min;
   if ((cd->cdte[i].cdte_addr.msf.frame)>37) cd->t[i-1].sec++; /* rounding a bit :) */
   if ((cd->t[i-1].sec=cd->t[i].sec-cd->t[i-1].sec)<0) {
    cd->t[i-1].min--;cd->t[i-1].sec+=60; /* adjust minutes */
   }
  }
 }
 /* now the leadout for track[0] + track[last] */
 cd->cdte[0].cdte_track=CDROM_LEADOUT;
 cd->cdte[0].cdte_format=CDROM_MSF;
 if (ioctl(cd->fd, CDROMREADTOCENTRY, &cd->cdte[0])<0)
  return -1;
 else {
  cd->t[0].audio=cd->cdte[0].cdte_ctrl & CDROM_DATA_TRACK;
  cd->t[0].min=cd->cdte[0].cdte_addr.msf.minute;
  cd->t[0].sec=cd->cdte[0].cdte_addr.msf.second;
  cd->t[0].cddb =
    cd->cdte[0].cdte_addr.msf.minute*60 +
    cd->cdte[0].cdte_addr.msf.second; /* cddb[0] this last id */
  cd->t[i-1].min=cd->t[0].min-cd->t[i-1].min;
  cd->t[i-1].cddb = cd->cdte[i-1].cdte_addr.msf.minute*60*75 +
    cd->cdte[i-1].cdte_addr.msf.second*75 +
    cd->cdte[i-1].cdte_addr.msf.frame; /* cddb[last] */
  if ((cd->cdte[i].cdte_addr.msf.frame)>37) cd->t[i-1].sec++; /* rounding a bit :) */
  if ((cd->t[i-1].sec=cd->t[0].sec-cd->t[i-1].sec)<0) {
   cd->t[i-1].min--;cd->t[i-1].sec+=60; /* adjust minutes */
  }
 }
 updates[U_TRACKS]=1;
 cd->title[1]=cdth.cdth_trk1; /* init all these stuff */
 if ((cddb=wmdb_getentries(&cd[0]))) return cddb;
 return cddb=cddb_getentries(&cd[0]); /* one of them */
}


int cd_readsubchannel(struct mcdp *cd) {
 struct cdrom_volctrl cdv; /* Volume Stuff */

 /* trackinfos */
 cd->cds.cdsc_format = CDROM_MSF; /* reading MSF format */
 if (ioctl(cd->fd,CDROMSUBCHNL,&cd->cds)<0) return -1;

 /* audiostatus changed */
 if (cd->cds.cdsc_audiostatus!=cd->status) { /* also NO_[DISC|INFO] */
  cd->status=cd->cds.cdsc_audiostatus;
  /* intro */
  if (cd->method==M_INTRO && cd->status==CDROM_AUDIO_COMPLETED) {
   cd->title[0]++; updates[U_TRACKS]=1;
   cd_start(&cd[0]);
  }
  /* repeat cd */
  if (cd->method==M_REPEAT_CD &&
  cd->title[0]==cd->title[1] &&
  cd->status==CDROM_AUDIO_COMPLETED) {
   cd->title[0]=1; updates[U_TRACKS]=1;
   cd_start(&cd[0]);
  }
  /* needed for the last track */
  if (cd->method==M_REPEAT_TRK && cd->status==CDROM_AUDIO_COMPLETED) {
   cd_start(&cd[0]);
  }
  updates[U_STATUS]=1; /* show status */
 }

 /* volume(0..3) changed */
 if (ioctl(cd->fd,CDROMVOLREAD,&cdv)<0) return -1;
 if (cdv.channel0!=cd->vol.channel0) {
  cd->vol.channel0=cdv.channel0; updates[U_VOLUME]=1; }
 if (cdv.channel1!=cd->vol.channel1) {
  cd->vol.channel1=cdv.channel1; updates[U_VOLUME]=1; }
 if (cdv.channel2!=cd->vol.channel2) {
  cd->vol.channel2=cdv.channel2; updates[U_VOLUME]=1; }
 if (cdv.channel3!=cd->vol.channel3) {
  cd->vol.channel3=cdv.channel3; updates[U_VOLUME]=1; }

 /* track changed */
 if (cd->cds.cdsc_trk!=cd->title[0] && cd->method!=M_INTRO) { /* title[0]=current title[1]=max */
  if (cd->method==M_REPEAT_TRK) {
   cd_start(&cd[0]);
   return cd->title[0];
  }
  cd->title[0]=cd->cds.cdsc_trk;
  updates[U_TRACKS]=1;
 }

 return cd->cds.cdsc_trk; /* current title may be usefull :) */
}

/* set title[0] to new track before */
void cd_start(struct mcdp *cd) {
 struct cdrom_msf msf;

 /* checking for last/first */
 if (cd->title[0]>cd->title[1]) cd->title[0]=1;
 if (cd->title[0]<1) cd->title[0]=cd->title[1];

 msf.cdmsf_min0   = cd->cdte[cd->title[0]].cdte_addr.msf.minute;
 msf.cdmsf_sec0   = cd->cdte[cd->title[0]].cdte_addr.msf.second;
 msf.cdmsf_frame0 = cd->cdte[cd->title[0]].cdte_addr.msf.frame;

 if (cd->method==M_INTRO) {
  msf.cdmsf_min1   = cd->cdte[cd->title[0]].cdte_addr.msf.minute;
  msf.cdmsf_sec1   = cd->cdte[cd->title[0]].cdte_addr.msf.second+MCDP_INTRO_TIME;
  if (msf.cdmsf_sec1>=60) { msf.cdmsf_min1++; msf.cdmsf_sec1-=60; }
  msf.cdmsf_frame1 = cd->cdte[cd->title[0]].cdte_addr.msf.frame;
 } else {
  /* leadout */
  msf.cdmsf_sec1   = cd->cdte[0].cdte_addr.msf.second;
  msf.cdmsf_min1   = cd->cdte[0].cdte_addr.msf.minute;
  msf.cdmsf_frame1 = cd->cdte[0].cdte_addr.msf.frame;
 }

 cd_playmsf(&cd[0], &msf);
 return;
}

/* only for skipping, rest is done via cd_play(..)! */
void cd_skip(struct mcdp *cd, int secs) {
 struct cdrom_msf msf;

 if (cd->method==M_INTRO) return; /* skipping @intros ? */

 if ((cd->title[0]==1) && (secs<0)) {
  if (cd->cds.cdsc_absaddr.msf.minute*60+cd->cds.cdsc_absaddr.msf.second+secs<=2)
  return; /* we ignore such things -- start */
 }

 secs += cd->cds.cdsc_absaddr.msf.minute * 60 + cd->cds.cdsc_absaddr.msf.second;
 msf.cdmsf_min0   = secs / 60;
 msf.cdmsf_sec0   = secs % 60;
 msf.cdmsf_frame0 = 0;
 msf.cdmsf_min1   = cd->cdte[0].cdte_addr.msf.minute;
 msf.cdmsf_sec1   = cd->cdte[0].cdte_addr.msf.second;
 msf.cdmsf_frame1 = cd->cdte[0].cdte_addr.msf.frame;

 if (msf.cdmsf_min0*60*75+msf.cdmsf_sec0*75+msf.cdmsf_frame0
  >  msf.cdmsf_min1*60*75+msf.cdmsf_sec1*75+msf.cdmsf_frame1)
  return; /* we ignore also this -- end */

 cd_playmsf(&cd[0], &msf);
 return;
}

void cd_stop(struct mcdp *cd) {
 if (cd->cds.cdsc_audiostatus == CDROM_AUDIO_PLAY)
 if (ioctl(cd->fd,CDROMSTOP,NULL)<0)
  draw_status("error stopping",1);
 return;
}

void cd_pauseresume(struct mcdp *cd) {
 switch (cd->cds.cdsc_audiostatus) {
 case CDROM_AUDIO_PLAY:
  ioctl(cd->fd,CDROMPAUSE,NULL);
 break;
 case CDROM_AUDIO_PAUSED:
  ioctl(cd->fd,CDROMRESUME,NULL);
 break;
 default: draw_status("request ignored",1);
 }
 return;
}

void cd_eject(int *fd) {
 draw_status("opening cdrom",1);
 ioctl(*fd,CDROMSTOP,NULL);
 ioctl(*fd,CDROMEJECT,NULL);
 return;
}

void cd_close(int *fd) {
 draw_status("reading cdrom",1);
 ioctl(*fd,CDROMCLOSETRAY,NULL);
 updates[U_ALL]=1;
 return;
}
