/***********************************************************************
 *
 * I can't get no dlgedit running. So I put this note here:
 *
 *
 *      --- This file has been manually midified ---
 *
 *
 * $Id: CDDialog.cpp,v 1.63.2.2 2003/02/22 09:34:22 aseigo Exp $
 *
 ***********************************************************************/

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

#include <qkeycode.h>
#include <qregexp.h>
#include <qdatetime.h>
#include <qtextstream.h>
#include <qfile.h>
#include <qdir.h>
#include <qfileinfo.h>
#include <qlistview.h>
#include <qlineedit.h>

#include <klocale.h>
#include <kdebug.h>
#include <ktempfile.h>

#include <stdio.h>
#include <math.h>

#include <kapplication.h>
#include <kmessagebox.h>

#include "CDDialog.h"
#include "CDDialog.moc"
#include "CDDialogData.moc"
#include "inexact.h"
#include "version.h"
#include "smtp.h"
#include "smtpconfig.h"
#include "kscd.h"

extern "C" {
#include "libwm/include/workman.h"
}

QTime framestoTime(int frames);
void  mimetranslate(QString& s);
extern void cddb_decode(QString& str);
extern void cddb_encode(QString& str, QStringList &returnlist);
extern void cddb_playlist_encode(QStringList& list,QString& playstr);
extern bool cddb_playlist_decode(QStringList& list, QString& str);

extern SMTP *smtpMailer;

CDDialog::CDDialog
(
	QWidget* parent,
	const char* name
)
	:
	CDDialogData( parent, name )
{
    cdinfo.magicID = 0;	/*cddb magic disk id BERND*/
	cdinfo.ntracks = 0;	/* Number of tracks on the disc */
	cdinfo.length  = 0;	/* Total running time in seconds */
	cdinfo.cddbtoc = 0L;

	connect(tracksList,     SIGNAL(selectionChanged(QListViewItem *)),this,SLOT(titleselected(QListViewItem *)));
	connect(tracksList,     SIGNAL(selectionChanged(QListViewItem *)),this,SLOT(play(QListViewItem *)));
	connect(trackEdit,   SIGNAL(textChanged( const QString & )) ,this,SLOT(trackchanged( const QString & )));
	connect(trackEdit,   SIGNAL( returnPressed ()) ,this,SLOT(nextTrack()));
	connect(ok_button, SIGNAL(clicked())       ,this,SLOT(save()));
	connect(upload_button, SIGNAL(clicked())       ,this,SLOT(upload()));
	connect(cancel_button, SIGNAL(clicked())       ,this,SLOT(cancel()));
	connect(load_button, SIGNAL(clicked())       ,this,SLOT(load_cddb()));
	connect(ext_info_title_button, SIGNAL(clicked()) ,this,SLOT(extITB()));
	connect(ext_info_button, SIGNAL(clicked())       ,this,SLOT(extIB()));
	connect(titleEdit,   SIGNAL(textChanged(const QString &)), this,SLOT(titlechanged()));
	connect(artistEdit,   SIGNAL(textChanged(const QString &)), this,SLOT(titlechanged()));
	ext_info_button->setEnabled(false);

	catlist.append("rock");
	catlist.append("classical");
	catlist.append("jazz");
	catlist.append("soundtrack");
	catlist.append("newage");
	catlist.append("blues");
	catlist.append("folk");
	catlist.append("country");
	catlist.append("reggae");
	catlist.append("misc");
	catlist.append("data");

} // CDDialog


CDDialog::~CDDialog()
{
    if(cdinfo.cddbtoc)
        delete [] cdinfo.cddbtoc;
} // ~CDDialog

void
CDDialog::closeEvent(QCloseEvent*)
{
    emit dialog_done();
} // closeEvent

void
CDDialog::keyPressEvent(QKeyEvent *e)
{
    if(e->state() == 0 && e->key() == Key_Escape)
        emit dialog_done();
} // keyPressEvent

void
CDDialog::cancel()
{
  emit dialog_done();
} // ok

void
CDDialog::play(QListViewItem *item)
{
  emit play_signal(item->text(0).toInt() - 1);
} // play


void
CDDialog::setData(
		  struct wm_cdinfo *cd,
		  QStringList& tracktitlelist,
		  QStringList& extlist,
		  QStringList& discidl,
		  QString& _xmcd_data,
		  QString& cat,
		  int& rev,
		  QStringList& _playlist,
		  QStringList& _pathlist,
		  QString& _cddbbasedir,
		  QString& _submitaddress,
		  SMTPConfigData *_smtpConfigData
		  )
{
    int ntr, etr;

    ext_list 	= extlist;
    track_list 	= tracktitlelist;
    xmcd_data   = _xmcd_data.copy();
    category 	= cat.copy();
    discidlist  = discidl;
    revision    = rev;
    playlist	= _playlist;
    pathlist	= _pathlist;
	cddbbasedir = _cddbbasedir.copy();
    submitaddress = _submitaddress.copy();
    smtpConfigData = _smtpConfigData;

    ntr = track_list.count();
    etr = ext_list.count();

    // Let's make a deep copy of the cd struct info so that the data won't
    // change the cd changes while we are playing with the dialog.

    // put one of these into the destructor too..
    if(cdinfo.cddbtoc)
      delete [] cdinfo.cddbtoc;
    if(!cd)
      return;
    cdinfo.cddbtoc =  new struct mytoc [cd->ntracks + 2];
    /*
     * Avoid people who need to edit titles of "no discs" to crash kscd.
     */
    if( cd->ntracks == 0 )
      {
        cdinfo.magicID = 0;
        cdinfo.ntracks = 0;
        cdinfo.length = 0;
        titleEdit->setText(i18n("No Disc"));
        disc_id_label->clear();
        tracksList->clear();
        return;
      }
    cdinfo.magicID = cddb_discid();	/* cddb magic disk id            */
    cdinfo.ntracks = cd->ntracks;	/* Number of tracks on the disc  */
    cdinfo.length  = cd->length;	/* Total running time in seconds */


    for( int i = 0; i < cd->ntracks + 1; i++)
      {
        cdinfo.cddbtoc[i].min = cd->trk[i].start / 4500;
        cdinfo.cddbtoc[i].sec = (cd->trk[i].start % 4500) / 75;
        cdinfo.cddbtoc[i].frame = cd->trk[i].start - ((cd->trk[i].start / 75)*75);
        cdinfo.cddbtoc[i].absframe = cd->trk[i].start;
      }

    // some sanity provisions

    if((int)track_list.count() < cdinfo.ntracks + 1)
      {
	int k = track_list.count();
	for(int i = 0 ; i < (int)( cdinfo.ntracks + 1 - k) ; i ++)
	  {
	    track_list.append("");
	  }
      }

    if((int)ext_list.count() < cdinfo.ntracks + 1)
      {
	int l = ext_list.count();
	for(int i = 0 ; i < (int) ( cdinfo.ntracks + 1 - l) ; i ++)
	  {
	    ext_list.append("");
	  }
      }

    while((int)track_list.count() > cdinfo.ntracks + 1)
      {
	track_list.remove(track_list.at(track_list.count() - 1));
      }

    while((int)ext_list.count() > cdinfo.ntracks + 1)
      {
	ext_list.remove(ext_list.at(ext_list.count() - 1));
      }

    artistEdit->setText((track_list.first().section('/',0,0)).stripWhiteSpace());
    titleEdit->setText((track_list.first().section('/',1,1)).stripWhiteSpace());

    QString idstr;
    idstr.sprintf("%08lx",cddb_discid());
    idstr = category + "  " + idstr;

    disc_id_label->setText(idstr.stripWhiteSpace());

    QTime   dl;
    dl 	=  dl.addSecs(cdinfo.length);

    QString temp2;
    if(dl.hour() > 0)
      temp2 = QString::fromUtf8( QCString().sprintf("%02d:%02d:%02d",dl.hour(),dl.minute(),dl.second()) );
    else
      temp2 = QString::fromUtf8( QCString().sprintf("%02d:%02d",dl.minute(),dl.second()) );
    total_time_label->setText(temp2);

    QString 	fmt;
    QTime 	dml;

    tracksList->clear();

    for(int i = 1; i <= cdinfo.ntracks; i++)
      {
	dml = framestoTime(cdinfo.cddbtoc[i].absframe - cdinfo.cddbtoc[i-1].absframe);

        QListViewItem * item = new QListViewItem( tracksList, 0 );
        item->setText( 0, QString().sprintf("%02d",i) );
        item->setText( 1, QString().sprintf("%02d:%02d",dml.minute(),dml.second()) );
	if((ntr >=  i) && (ntr > 0))
            item->setText( 2,  *(track_list.at(i)));
      }

    QString str;
    cddb_playlist_encode(playlist,str);
    progseq_edit->setText(str);
} // setData

void
CDDialog::extIB()
{

  int iNr = tracksList->currentItem()->text(0).toInt();

  InexactDialog *dialog;
  dialog = new InexactDialog(0,"dialog",false);
  dialog->setTitle(i18n("Use this editor to annotate this track."));

  dialog->insertText(*ext_list.at(iNr));

  if(dialog->exec() != QDialog::Accepted)
    {
      delete dialog;
      return;
    }

  QString text;
  dialog->getSelection(text);

  *ext_list.at(iNr) = text;

  delete dialog;
} // extIB

void
CDDialog::extITB()
{
  InexactDialog *dialog;
  dialog = new InexactDialog(0,"dialog",false);
  dialog->insertText(ext_list.first());
  dialog->setTitle(i18n("Use this editor to annotate the title."));

  if(dialog->exec() != QDialog::Accepted)
    {
      delete dialog;
      return;
    }

  QString text;
  dialog->getSelection(text);

  *ext_list.at(0) = text;
  //ext_list.insert( 0 , text );
  //ext_list.remove( 1 );

  delete dialog;
} // extITB

void CDDialog::titleselected(QListViewItem *item)
{
    ext_info_button->setEnabled(true);
    trackEdit->setEnabled(true);
    if(item->text(0).toInt() <  (int)track_list.count()) {
        trackEdit->setText(item->text(2));
        trackEdit->setFocus();
    }

} // titleselected

void CDDialog::trackchanged( const QString &text ) {
    int i = tracksList->currentItem()->text(0).toInt();
    track_list.insert(track_list.at(i),text);
    track_list.remove(track_list.at(i+1));

    tracksList->currentItem()->setText( 2,  text);
} // trackchanged

void CDDialog::nextTrack() {
    int i = tracksList->currentItem()->text(0).toInt();
    QListViewItem  *item = tracksList->findItem ( QString().sprintf("%02d",i+1), 0,ExactMatch);
    tracksList->setSelected(item, true);
    tracksList->ensureItemVisible(item);
}

QTime
framestoTime(int _frames)
{
  QTime 	dml;
  double 	frames;
  double 	dsecs;
  int 		secs;
  double 	ip;

  frames = (double) _frames;
  dml.setHMS(0,0,0);

  dsecs = frames/ (75.0);

  if(modf(dsecs,&ip) >= 0.5)
    {
      ip = ip + 1.0;
    }
  secs = (int) ip;

  dml = dml.addSecs(secs);
  return dml;
} // framestotime

void CDDialog::titlechanged() {
    QString title = titleEdit->text().stripWhiteSpace();
    QString artist = artistEdit->text().stripWhiteSpace();
    if(title.isEmpty() || artist.isEmpty()) return;
    title = QString("%1 / %2").arg(artist).arg(title);
    *track_list.begin() = title;
} // titlechanged

QString submitcat;

void
CDDialog::upload()
{
  if(!checkit())
    return;

  InexactDialog *dialog;
  dialog = new InexactDialog(0,"Dialog",true);

  if( category.length() < 1 )
	{
	  dialog->insertList(catlist);
	  dialog->setErrorString(i18n("Please select a category or press Cancel"));
	  dialog->setTitle(i18n("To Which Category does the CD Belong?"));
	  if(dialog->exec() != QDialog::Accepted)
		{
		  delete dialog;
		  return;
		}

	  dialog->getSelection(submitcat);
	  delete dialog;
	} else {
	  submitcat = category.copy();
	}

  KTempFile tmpFile;
  tmpFile.setAutoDelete(true); // delete file when we are done.
  QString tempfile = tmpFile.name();

  save_cddb_entry(tempfile,true);

  kapp->processEvents();
  kapp->flushX();


  if(smtpConfigData->enabled)
    {
      kdDebug() << "Submitting freedb entry via SMTP...\n" << endl;
      QFile file(tempfile);

      file.open(IO_ReadOnly);
      QTextStream ti(&file);

      QString s;
      QString subject;

      while (!ti.eof())
	{
          s += ti.readLine() + "\r\n";
	}

      smtpMailer->setServerHost(smtpConfigData->serverHost);
      smtpMailer->setPort(smtpConfigData->serverPort.toUInt());

      smtpMailer->setSenderAddress(smtpConfigData->senderAddress);
      smtpMailer->setSenderReplyTo(smtpConfigData->senderReplyTo);
      smtpMailer->setRecipientAddress(submitaddress);

      subject.sprintf("cddb %s %08lx", submitcat.utf8().data(), cdinfo.magicID);
      smtpMailer->setMessageSubject(subject);
      smtpMailer->setMessageBody(s);

      smtpMailer->sendMessage();

      return;
    }


  QString cmd;

  cmd = "/usr/sbin/sendmail -t";

  kdDebug() << "Submitting freedb entry: " << cmd << "\n" << endl;

  FILE* mailpipe;
  mailpipe = popen(QFile::encodeName(cmd),"w");

  if(mailpipe == NULL){
    QString str;
    str = i18n("Could not pipe contents into:\n %1").arg(cmd);

    KMessageBox::error(this, str);
    pclose(mailpipe);
    return;

  }

  QFile file(tempfile);

  file.open(IO_ReadOnly);

  QTextStream ti(&file);


  QTextStream to(mailpipe,IO_WriteOnly );

  QString s;

  //to << "Content-Transfer-Encoding: quoted-printable\n";

  while ( !ti.eof() )
    {
      s = ti.readLine();
      if(!ti.eof())
	{
          //  mimetranslate(s);
          to << s << '\n';
	}
    }

  pclose(mailpipe);

  file.close();
  //  file2.close();   // *****

  kdDebug() << "DONE SENDING\n" << endl;
} // upload

void
CDDialog::getCategoryFromPathName(char* pathname, QString& _category)
{
  QString path = pathname;
  path = path.stripWhiteSpace();

  while(path.right(1) == QString("/"))
    {
      path = path.left(path.length() - 1);
    }

  int pos = 0;
  pos  = path.findRev("/",-1,true);
  if(pos == -1)
    _category = path.copy();
  else
    _category = path.mid(pos+1,path.length());
} // getCategoryFromPathName

void
CDDialog::save()
{
  if(!checkit())
  {
    emit dialog_done();
    return;
  }

  QString savecat;

  if( category.length() < 1 )
  {
    InexactDialog *dialog;
    dialog = new InexactDialog(0,"Dialog",true);
    dialog->insertList(catlist);
    dialog->setErrorString(i18n("Please select a category or press Cancel"));
    dialog->setTitle(i18n("Under Which Category Would you like to Store this Disc's Information?"));

    if(dialog->exec() != QDialog::Accepted)
    {
      delete dialog;
      emit dialog_done();
      return;
    }

    dialog->getSelection(savecat);
    delete dialog;
  }
  else
  {
    savecat = category.copy();
  }

  QString mag;
  mag.sprintf("%s/%s/%08lx",cddbbasedir.utf8().data(),savecat.utf8().data(),cdinfo.magicID);

  save_cddb_entry(mag,false);
  load_cddb();
  emit dialog_done();
} // save

void
CDDialog::save_cddb_entry(QString& path,bool upload)
{
  QString magic;
  magic.sprintf("%08lx",cdinfo.magicID);
  bool have_magic_already = false;

  kdDebug() << "::save_cddb_entry(): path: " << path << " upload = " << upload << "\n" << endl;

  if( !upload )
    {
      for ( QStringList::Iterator it = discidlist.begin();
            it != discidlist.end();
            ++it )
	{
	  if (magic == *it)
	    {
	      have_magic_already = true;
	      break;
	    }
	}

      if(!have_magic_already)
	discidlist.insert(discidlist.begin(), magic);
    } else { // uploading
      discidlist.clear();
      discidlist.insert(discidlist.begin(), magic);
    }

  QFile file(path);


  if( !file.open( IO_WriteOnly  ))
    {
      QString str = i18n("Unable to write to file:\n%1\nPlease check "
			 "your permissions and ensure your category directories exist.")
	.arg(path);

      KMessageBox::error(this, str);
      return;
    }

  QString tmp;
  QTextStream t(&file);


  if(upload && !smtpConfigData->enabled)
    {
      QString subject;
      subject.sprintf("cddb %s %08lx", submitcat.utf8().data(), cdinfo.magicID);

      t << "To: " + submitaddress + "\n";
      tmp = QString("Subject: %1\n").arg(subject);
      t << tmp;
    }

  t << "# xmcd CD database file\n";

  QString datestr;
  datestr = QDateTime::currentDateTime().toString();
  tmp = QString("# Generated: %1 by KSCD\n").arg(datestr);
  t << tmp;

  // Waste some disk space
  if(!upload) {
    t << "# Copyright (C) 1997-1999 Bernd Johannes Wuebben.\n";
    t << "# Copyright (C) 1999-2001 Dirk Foersterling.\n";
  }


  t << "# \n";
  t << "# Track frame offsets:\n";

  for(int i = 0 ; i < cdinfo.ntracks;i ++)
    {
      tmp = QString("#       %1\n").arg(cdinfo.cddbtoc[i].absframe);
      t << tmp;
    }

  t << "#\n";
  tmp = QString("# Disc length: %1 seconds\n").arg(cdinfo.length);
  t << tmp;
  t << "#\n";
  // FIXME: Only increase if the entry was received from freedb
  if(upload)
    tmp = QString("# Revision: %1\n").arg(++revision);
  else
    tmp = QString("# Revision: %1\n").arg(revision);

  t << tmp;
  t << "# Submitted via: Kscd "KSCDVERSION"\n";
  t << "#\n";


  tmp = "DISCID=";
  int counter = 0;

  int num = 0;
  for ( QStringList::Iterator it = discidlist.begin();
        it != discidlist.end();
        ++it, ++num )
    {

      tmp += *it;

      if( num < (int) discidlist.count() - 1)
	{
	  if( counter++ == 3 )
	    {
	      tmp += "\nDISCID=";
	      counter = 0;
	    } else {
	      tmp += ",";
	    }
	}
    }

  tmp += "\n";
  t << tmp;

  QStringList returnlist;
  QString tmp2;

  tmp2 = *track_list.at(0);
  cddb_encode(tmp2,returnlist);

  if(returnlist.count() == 0)
    {
      // sanity provision
      tmp = QString("DTITLE=%1\n").arg("");
      t << tmp;
    } else {
      for ( QStringList::Iterator it = returnlist.begin();
            it != returnlist.end();
            ++it )
	{
	  tmp = QString("DTITLE=%1\n").arg(*it);
	  t << tmp;
	}
    }

  num = 1;
  for ( QStringList::Iterator it = track_list.begin();
        it != track_list.end();
        ++it)
    {
      tmp2 = *it;
      cddb_encode(tmp2,returnlist);

      // no perfect solution, but it's working so far.
      if( it != track_list.begin() ) {
        if(returnlist.isEmpty())
          {
            // sanity provision
            tmp = QString("TTITLE%1=%2\n").arg(num-1).arg("");
            t << tmp;
          } else {
            tmp = QString("TTITLE%1=%2\n").arg(num-1).arg(*it);
            t << tmp;
          }
        num++;
      }
    }

  tmp2 = ext_list.first();
  cddb_encode(tmp2,returnlist);

  if(returnlist.isEmpty())
    {
      // sanity provision
      tmp = tmp.sprintf("EXTD=%s\n","");
      t << tmp;
    } else {
      for ( QStringList::Iterator it = returnlist.begin();
            it != returnlist.end();
            ++it )
	{
	  tmp = QString("EXTD=%1\n").arg(*it);
	  t << tmp;
	}
    }

  int i = 1;
  for ( QStringList::Iterator it = ext_list.at(1);
        it != ext_list.end();
        ++it, i++ )
    {
      tmp2 = *it;
      cddb_encode(tmp2,returnlist);

      if(returnlist.count() == 0)
	{
	  // sanity provision
	  tmp = tmp.sprintf("EXTT%d=%s\n",i-1,"");
	  t << tmp;
	} else {
	  for(int j = 0; j < (int) returnlist.count();j++)
	    {
	      tmp = tmp.sprintf("EXTT%d=%s\n",i-1,(*returnlist.at(j)).utf8().data());
	      t << tmp;
	    }
	}
    }

  if(!upload)
    {
      cddb_encode(playorder,returnlist);

      for(int i = 0; i < (int) returnlist.count();i++)
	{
	  tmp = tmp.sprintf("PLAYORDER=%s\n", (*returnlist.at(i)).utf8().data());
	  t << tmp;
	}
    } else {
      tmp = tmp.sprintf("PLAYORDER=\n");
      t << tmp;
    }

  t << "\n";

  file.close();
  chmod(QFile::encodeName(file.name()), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
  return;
} // save_cddb_entry


bool
CDDialog::checkit()
{
  QString title = titleEdit->text().stripWhiteSpace();
  if(title.isEmpty())
    {
      KMessageBox::sorry(this,
			 i18n("The title of the disc has to be entered.\n"
			      "Please correct the entry and try again."),
			 i18n("Invalid Database Entry"));
      return false;
    }

  QString artist = artistEdit->text().stripWhiteSpace();
  if(artist.isEmpty())
    {
      KMessageBox::sorry(this,
			 i18n("The artist name of the disc has to be entered.\n"
			      "Please correct the entry and try again."),
			 i18n("Invalid Database Entry"));
      return false;
    }

  if(track_list.count() < 2)
    {
      KMessageBox::sorry(this,
			 i18n("At least one track title must be entered.\n"\
			      "Please correct the entry and try again."),
			 i18n("Invalid Database Entry"));
      return false;
    }

  bool have_nonempty_title = false;
  for ( QStringList::Iterator it = track_list.at(1);
        it != track_list.end();
        ++it )
    {
      title = *it;
      title = title.stripWhiteSpace();
      if(!title.isEmpty()){
	have_nonempty_title = true;
	break;
      }
    }

  if(!have_nonempty_title)
    {

      KMessageBox::sorry(this,
			 i18n("At least one track title must be entered.\n"\
			      "Please correct the entry and try again."),
			 i18n("Invalid Database Entry"));
      return false;
    }

  if(cdinfo.ntracks +1 != (int)track_list.count() )
    {
      KMessageBox::error(this,
			 i18n("cdinfo.ntracks != title_list->count() + 1\n"
			      "Please email the author."),
			 i18n("Internal Error"));
      return false;
    }

  QString str;
  QStringList strlist;
  str = progseq_edit->text();

  bool ret;
  ret = cddb_playlist_decode(strlist, str);

  QString teststr;
  bool ok;
  int  num;

  for ( QStringList::Iterator it = strlist.begin();
        it != strlist.end();
        ++it )
    {
      teststr = *it;
      num = teststr.toInt(&ok);

      if( num > cdinfo.ntracks || !ok)
	ret = false;
    }

  if(!ret)
    {
      KMessageBox::sorry(this,
			 i18n("Invalid Playlist\nPlease use track numbers only, separated by commas."));
      return false;
    }

  cddb_playlist_encode(strlist,playorder);
  return true;
} // checkit

void
CDDialog::load_cddb()
{
  emit cddb_query_signal(true);
} // load

// simplyfied quoted printable mime encoding that should be good enough
// for our purposed. The encoding differs from the 'real' encoding in
// that we don't need to worry about trailing \n, \t or lines exeeding the
// spec length.

void
mimetranslate(QString& s)
{
  QString q;
  QString hex;

  s = s.stripWhiteSpace(); // there is no harm in doing this and it
  // will simplify the quoted printable mime encoding.

  for(uint i = 0 ; i < s.length(); i++)
    {
      if (((s[i] >= 32) && (s[i] <= 60)) ||
	  ((s[i] >= 62) && (s[i] <= 126)))
	{

	  q += s.at(i);
	} else {

	  hex = hex.sprintf("=%02X", (unsigned char)s[i].latin1());
	  q += hex;
	}
    }

  //  printf("%s\n",q.data());
  s = q.copy();

} // mimetranslate
