// KreateCD - CD recording software for the K desktop environment
//
// 1999 by Alexander Feigl <Alexander.Feigl@gmx.de>
//
// This file is subject to the terms and conditions of the GNU General      
// Public License.  See the file COPYING in the main directory of the       
// KreateCD distribution for more details.                                     

#include <qstring.h>

#include <kapp.h>
#include <kconfig.h>

#include <sys/stat.h>
#include <unistd.h>


#include "CDTrack.h"
#include "AudioFile.h"
#include "DriveAccess.h"
#include "IsoImage.h"


#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
#include <sys/stat.h>

static const char *TypeNames[]={"Audio","Audio PreEmph","Mode 1","Mode 2","XA Mode 1","XA Mode 2","CDI"};


CDTrack::~CDTrack(void)
 {
  if (ThisAudio!=0)
   {
    delete(ThisAudio);
   }

  if (ThisISO!=0)
   {
    delete(ThisISO);
   }

  if (IsTemporary)
   {
    remove(SourceFile);
   }
  clearImage();
 }

CDTrack::CDTrack(KConfig *config)
 {
  QString tqs;

  TrackType=(enum TrackType) config->readNumEntry("TrackType",Track_DataMode1);
  TrackSource=(enum TrackSource) config->readNumEntry("TrackSource",Source_None);
  TrackImage=(enum TrackImage) config->readNumEntry("TrackImage",Image_None);
  //FIXME : for now we mark all loaded tracks as dirty
  if (TrackImage==Image_Clean) TrackImage=Image_Dirty;
  IsTemporary=config->readNumEntry("IsTemporary",0);
  CDTrackNum=config->readNumEntry("CDTrackNum",1);
  CDTocSum=config->readUnsignedLongNumEntry("CDTocSum",0);
#if QT_VERSION >= 200
  tqs=config->readEntry("Description",QString::null);
  strcpy(Description,tqs.data());
  tqs=config->readEntry("SourceFile",QString::null);
  strcpy(SourceFile,tqs.data());
  tqs=config->readEntry("ImageFile",QString::null);
  strcpy(ImageFile,tqs.data());
#else
  tqs=config->readEntry("Description",0);
  strcpy(Description,tqs.data());
  tqs=config->readEntry("SourceFile",0);
  strcpy(SourceFile,tqs.data());
  tqs=config->readEntry("ImageFile",0);
  strcpy(ImageFile,tqs.data());
#endif

  TrackSize=config->readUnsignedLongNumEntry("TrackSize",0);
  TrackDuration=config->readUnsignedLongNumEntry("TrackDuration",0);
  TrackLBA=config->readUnsignedLongNumEntry("TrackLBA",0);
  ThisAudio=0;
  ThisISO=0;

  if (IsTemporary==1)
   {
    ThisAudio=new AudioFile();
    if (!ThisAudio->setupCDTrack(SourceFile))
     {
      delete ThisAudio;
      ThisAudio=0;
      IsTemporary=0;
      remove(SourceFile);
     }
   }

  if ( isAudio() && (TrackSource==Source_File) )
   {
    ThisAudio=new AudioFile;
    if (!ThisAudio->identifyFile(SourceFile))
     {
      delete(ThisAudio);
      ThisAudio=0;
     }
   }
  if ( isAudio() && (ThisAudio!=0) )
   {
    ThisAudio->setStartPos(config->readLongNumEntry("TrackStart",0));
    ThisAudio->setEndPos(config->readLongNumEntry("TrackEnd",
                         (ThisAudio->getAbsoluteDuration()*588)-1));
    ThisAudio->setBoost((float) config->readDoubleNumEntry("TrackBoost",1));
    ThisAudio->setBalance((float) config->readDoubleNumEntry("TrackBalance",0));
   }
  needImage();
 }

void CDTrack::saveTrack(KConfig *config)
 {
  config->writeEntry("TrackType",(int)TrackType);
  config->writeEntry("TrackSource",(int)TrackSource);
  config->writeEntry("TrackImage",(int)TrackImage);
  config->writeEntry("IsTemporary",IsTemporary);
  if (TrackSource==Source_CD)
   {
    config->writeEntry("CDTrackNum",CDTrackNum);
    config->writeEntry("CDTocSum",CDTocSum);
    config->writeEntry("TrackLBA",TrackLBA);
   }
  config->writeEntry("Description",Description);

  config->writeEntry("SourceFile",SourceFile);
  if (TrackImage==Image_Clean)
   {
    config->writeEntry("ImageFile",ImageFile);
   }
  config->writeEntry("TrackSize",TrackSize);
  config->writeEntry("TrackDuration",TrackDuration);
  if ( (isAudio()) && (ThisAudio!=0) )
   {
    config->writeEntry("TrackStart",ThisAudio->getStartPos());
    config->writeEntry("TrackEnd",ThisAudio->getEndPos());
    config->writeEntry("TrackBoost",(double) ThisAudio->getBoost());
    config->writeEntry("TrackBalance",(double) ThisAudio->getBalance());
   }
 }

CDTrack::CDTrack(void)
 {
  strcpy(Description,"New track");
  ImageFile[0]='\0';
  TrackType=Track_DataMode1;
  ThisAudio=0;
  ThisISO=0;
  TrackImage=Image_None;
  IsTemporary=0;
  clearSource();
 }

enum CDTrack::TrackType CDTrack::getTrackType(void)
 {
  return(TrackType);
 }

enum CDTrack::TrackSource CDTrack::getTrackSource(void)
 {
  return(TrackSource);
 }

enum CDTrack::TrackImage CDTrack::getTrackImageMode(void)
 {
  return(TrackImage);
 }

void CDTrack::getTrackDescription(char *desc)
 {
  strcpy(desc,Description);
 }

void CDTrack::getTrackDurationHMSB(char *duration)
 {
  long int dura;
  dura=TrackDuration;
  if (ThisAudio)
   {
    dura=ThisAudio->getSelectedDuration();
   }
  getDurationHMSB(duration,dura);
 }

long int CDTrack::getRealDuration(void)
 {
  long int dura;
  dura=TrackDuration;
  if (ThisAudio)
   {
    dura=ThisAudio->getSelectedDuration();
   }
  return(dura);
 }
 


void CDTrack::getTrackTypeText(char *type)
 {
  strcpy(type,TypeNames[TrackType]);
 }

int CDTrack::isAudio(void)
 {
  if ( (TrackType==Track_Audio) || (TrackType==Track_AudioPreEmph) ) return(1);
  return(0);
 }

class AudioFile * CDTrack::getAudioFile(void)
 {
  return(ThisAudio);
 }

class ISOImage * CDTrack::getISOImage(void)
 {
  return(ThisISO);
 }

int CDTrack::getBurnFile(char *burnfile)
 {
  switch (TrackImage)
   {
    case Image_None:
      strcpy (burnfile,SourceFile);
      return(1);
    case Image_Dirty:
      return(0);
    case Image_Clean:
      strcpy(burnfile,ImageFile);
      return(1);
   }
  return(0);
 }

int CDTrack::getUseImage(void)
 {
  if (TrackImage==Image_None) return(0);
  return(1);
 }

void CDTrack::enableImage(void)
 {
  if (TrackImage==Image_None) TrackImage=Image_Dirty;
 }

void CDTrack::disableImage(void)
 {
  if (!NeedImage)
   {
    clearImage();
    TrackImage=Image_None;
   }
 }


void CDTrack::getTypeString(enum CDTrack::TrackType ttype,char *type)
 {
  strcpy(type,TypeNames[ttype]);
 }

void CDTrack::getDurationHMSB(char *durstr,long int duration)
 {
  int hour,min,sec,block;

  block=duration%75;
  sec=duration/75;
  min=sec/60;
  sec%=60;
  hour=min/60;
  min%=60;
  sprintf(durstr,"%01d:%02d:%02d.%02d",hour,min,sec,block);
 }      

enum CDTrack::TrackType CDTrack::getTOCType(int control,int mode)
 {
  enum TrackType ttype;
  ttype=Track_CDI;
  switch (control)
     {
      case 0:
        ttype=CDTrack::Track_Audio;
        break;
      case 1:
        ttype=CDTrack::Track_AudioPreEmph;
      case 4:
        if (mode==1) ttype=CDTrack::Track_DataMode1;
        if (mode==2) ttype=CDTrack::Track_DataMode2;
        break;
     }
  return(ttype);
 }


void CDTrack::setDescription(const char *descr)
 {
  strncpy(Description,descr,39);
 }

void CDTrack::setTrackType(enum TrackType tracktype)
 {
  int oldaud,aud;
  oldaud=isAudio();
  TrackType=tracktype;
  aud=isAudio();
  if (oldaud!=aud) clearSource();
  updateTrack(); 
 }

void CDTrack::clearSource(void)
 {
  TrackSource=Source_None;
  if (ThisAudio!=0)
   {
    delete(ThisAudio);
    ThisAudio=0;
   }

  if (ThisISO!=0)
   {
    delete(ThisISO);
    ThisISO=0;
   }

  TrackDuration=0;
  TrackSize=0;
  if (IsTemporary)
   {
    remove(SourceFile);
    IsTemporary=0;
   }

  SourceFile[0]='\0';
  clearImage();
  TrackImage=Image_None;
  needImage();
 }

void CDTrack::setSourceFile(const char *filename)
 {
  clearSource();
  TrackSource=Source_File;
  strcpy(SourceFile,filename);
  ImageFile[0]='\0';
  TrackSize=0;
  TrackDuration=0;
  if (isAudio())
   {
    ThisAudio=new AudioFile;
    if (!ThisAudio->identifyFile(filename))
     {
      delete(ThisAudio);
      ThisAudio=0;
     }
    needImage();
   }
  TrackImage=NeedImage?Image_Dirty:Image_None;
  updateTrack();
 }

void CDTrack::setSourceFileSystem(ISOImage *isoimg)
 {
  if (isoimg==ThisISO) ThisISO=0;
  clearSource();
  if (isAudio()) return;
  TrackSource=Source_Filesystem;
  SourceFile[0]='\0';
  ImageFile[0]='\0';
  TrackDuration=0;
  needImage();
  TrackImage=NeedImage?Image_Dirty:Image_None;
  if (isoimg==0)
   {
    ThisISO=new ISOImage();
   }
   else
   {
    ThisISO=isoimg;
   }
  updateTrack();

 }

void CDTrack::setSourceTrack(int track,long int tocsum,unsigned long duration,unsigned long lba)
 {
  clearSource();
  TrackSource=Source_CD;
  CDTrackNum=track;
  CDTocSum=tocsum;
 
  SourceFile[0]='\0';
  TrackDuration=duration;
  if (isAudio())
   {
    TrackSize=duration*2352;
   }
   else
   {
    TrackSize=duration*2048;
   }
  TrackLBA=lba;
  needImage();
  TrackImage=NeedImage?Image_Dirty:Image_None;
  updateTrack();
 }

void CDTrack::updateTrack()
 {
  clearImage();
  if (IsTemporary==1)
   {
    remove(SourceFile);
    IsTemporary=0;
   }

  switch (TrackSource)
   {
    case Source_None:
     {
      break;
     }
    
    case Source_CD:
      break;
    case Source_File:
     {
      struct stat statbuf;
      if ((isAudio()) && (ThisAudio!=0) )
       {
        
        TrackDuration=ThisAudio->getSelectedDuration();
       }
       else
       { 
        if (stat(SourceFile,&statbuf))
         {
          clearSource();
          return;
         }      
        TrackSize=statbuf.st_size;
        if (isAudio())
         {
          TrackDuration=(TrackSize+2351)/2352;
         }
         else
         {
          TrackDuration=(TrackSize+2047)/2048;
         }     
        break;
       }
     }
    case Source_Filesystem:
      break;
    
     
   }

 }

int CDTrack::validateTrack(void)
 {
  if (TrackSource==Source_None) return(0);
  if ((TrackSource==Source_CD) && (isAudio()) && (IsTemporary!=1))
   {
    DriveAccess da;
    clearImage();
    if (IsTemporary==1)
     {
      remove(SourceFile);
      IsTemporary=0;
     }
    createName(SourceFile,0,TrackDuration);
    if (da.paranoiaTrack(CDTrackNum,SourceFile)==0)
     {
      return(0);
     }
    IsTemporary=1; 
    ThisAudio=new AudioFile;
    if (!ThisAudio->setupCDTrack(SourceFile))
     {
      delete ThisAudio;
      ThisAudio=0;
      IsTemporary=0;
      remove(SourceFile);
      return(0);
     }
    updateDuration(SourceFile);
    return(1);
   }

  if (TrackImage==Image_None) return(1);
  if (TrackImage==Image_Clean) return(1);
  
  switch (TrackSource)
   {
    case Source_CD:
      if (isAudio())
       {
        // create audio image file
        if (ThisAudio==NULL) return(0);
        createName(ImageFile,1,TrackDuration);
        if (!ThisAudio->buildImage(ImageFile))
         {
          return(0);
         }
        TrackImage=Image_Clean;
        updateDuration(ImageFile);
        return(1);
       }
       else
       {
        // rip a datatrack from CD
        DriveAccess da;
        createName(ImageFile,1,TrackDuration);
        if (!da.readDataTrack(CDTrackNum,ImageFile,TrackLBA,TrackDuration))
         {
          return(0);
         }
        updateDuration(ImageFile);
        TrackImage=Image_Clean;
        return(1);
       } 
    case Source_File:
      if (isAudio())
       {
        // create audio image file
        if (ThisAudio==NULL) return(0);
        createName(ImageFile,1,TrackDuration);
        if (!ThisAudio->buildImage(ImageFile))
         {
          return(0);
         }
        updateDuration(ImageFile);
        TrackImage=Image_Clean;
        return(1);
       }
       else
       {
        // copy a datatrack
        createName(ImageFile,1,TrackDuration);
        return(0);
        //updateDuration(ImageFile);
        //TrackImage=Image_Clean;
        //return(1);
       }
    case Source_Filesystem:
     {
      if (ThisISO==NULL) return(0);
      createName(ImageFile,1,rand()%1000);
      if (!ThisISO->makeImage(ImageFile))
       {
        return(0);
       }
      updateDuration(ImageFile);
      TrackImage=Image_Clean;
      return(1);
     }

    default:
      return(0);
   }

  return(0);
 }


void CDTrack::clearImage(void)
 {
  if (TrackImage!=Image_Clean) return;
  remove(ImageFile);
  TrackImage=Image_Dirty;
  ImageFile[0]='\0';
 }

void CDTrack::createName(char *fn,int type,long int duration)
 {
  KConfig *config;
  QString tempdir;
  const char *directory;
  int random;

  config=kapp->getConfig();
  config->setGroup("Path");
  tempdir=config->readEntry("PathTemporary","/tmp");
  directory=tempdir.data();
   
  while (1)
   {
    struct stat sbuf;
    random=rand()%1000;
    sprintf(fn,"%s/kreatecd_%ld_%d.%s",directory,duration,random,type?"img":"cdt");
    if (stat(fn,&sbuf)!=0) break;
   }
 }

void CDTrack::updateDuration(const char *fn)
 {
  struct stat sb;
  if (stat(fn,&sb)==-1) return;
  TrackSize=sb.st_size;
  if (TrackSource==Source_CD)
   {
    if (isAudio())
     {
      TrackDuration=TrackSize/2352;
     }
     else
     {
      TrackDuration=TrackSize/2048;
     }
    }
  if (TrackSource==Source_Filesystem)
   {
    TrackDuration=TrackSize/2048;
   }
 }


int CDTrack::needImage(void)
 {
  switch (TrackSource)
   {
    case Source_None:
      NeedImage=0;
      break;
    case Source_File:
      if (ThisAudio==0)
       {
        NeedImage=0;
       }
       else
       {
        NeedImage=ThisAudio->needImage()?1:0;
       }
    case Source_Filesystem:
      NeedImage=1;
      break;
    case Source_CD:
      if (isAudio())
       {
        NeedImage=0;
       }
       else
       {
        NeedImage=1;
       }
   }
  return(NeedImage);

 }