/*
 *
 *    soniK digital audio editor
 *    Copyright (C) 2003-2006  Robert Walker <rob@tenfoot.org.uk>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */
#include "afplugin.h"
#include "sonik_util.h"

#include <klocale.h>
#include <kgenericfactory.h>
#include <kdialog.h>
#include <kdebug.h>

#include <qfileinfo.h>

#include <qlayout.h>
#include <qlabel.h>
#include <qcombobox.h>

#include <kdebug.h>

using Sonik::FileIO;
using Sonik::AFPlugin;
using Sonik::AFWriteOptionsPage;

AFPlugin::Reader::Reader(const QString& fileName, const QString& mimeType)
  : FileIO::Reader::Reader(fileName, mimeType),
    mSampleFormat(AF_SAMPFMT_TWOSCOMP), mPos(0),
    mCompressionMode(kNone)
{
}

AFPlugin::Reader::~Reader()
{
}

Sonik::IOResult AFPlugin::Reader::open()
{
  kdDebug(60606) << "AFPlugin::open: " << mFileName << "\n";

  mHandle = afOpenFile(mFileName, "r", NULL);
  kdDebug(60606) << "AFPlugin::open: " << mHandle << "\n";
  if (!mHandle)
    return kFileNotFound;

  int bits;
  mLength = afGetFrameCount(mHandle, AF_DEFAULT_TRACK);
  mChannels = afGetChannels(mHandle, AF_DEFAULT_TRACK);
  afGetSampleFormat(mHandle, AF_DEFAULT_TRACK,
                    &mSampleFormat, &bits);
  mBits = bits;
  mSampleRate = (int)afGetRate(mHandle, AF_DEFAULT_TRACK);
  kdDebug(60606) << "AFPlugin::open: "
                 << "length = " << mLength
                 << ", channels = " << mChannels
                 << ", format = " << mSampleFormat
                 << ", bits = " << mBits
                 << ", rate = " << mSampleRate << "\n";

  afSetVirtualSampleFormat(mHandle, AF_DEFAULT_TRACK,
                           AF_SAMPFMT_TWOSCOMP, mBits);
  afSetVirtualByteOrder(mHandle, AF_DEFAULT_TRACK,
                        AF_BYTEORDER_LITTLEENDIAN);

  switch (afGetCompression(mHandle, AF_DEFAULT_TRACK))
  {
    case AF_COMPRESSION_G711_ALAW:
      mCompressionMode = kALaw;
      break;
    case AF_COMPRESSION_G711_ULAW:
      mCompressionMode = kULaw;
      break;
    case AF_COMPRESSION_NONE:
    default:
      mCompressionMode = kNone;
      break;
  }
  kdDebug(60606) << "AFPlugin::open: "
                 << "compression = " << mCompressionMode << "\n";

  mFrameSize = (int)afGetFrameSize(mHandle, AF_DEFAULT_TRACK, true);
  kdDebug(60606) << "AFPlugin::open: "
                 << " frameSize = " << mFrameSize << "\n";

  return kSuccess;
}

void AFPlugin::Reader::close()
{
  afCloseFile(mHandle);
  mPos = 0;
}

uint32_t AFPlugin::Reader::read(SampleBuffer& data)
{
  if (mHandle == 0)
    return 0;

  uint32_t sampleCount = data.size();
  uint32_t frameCount = sampleCount / mChannels;
  uint32_t readCount = 0;

  if (mBits == 8)
  {
    Sample8* buf = new Sample8[sampleCount];
    readCount = afReadFrames(mHandle, AF_DEFAULT_TRACK, buf, frameCount);
    for (uint i = 0; i < readCount * mChannels; ++i)
      data[i] = Sonik::from<Sample8>(buf[i]);
    delete[] buf;
  }
  else if (mBits == 16)
  {
    Sample16* buf = new Sample16[sampleCount];
    readCount = afReadFrames(mHandle, AF_DEFAULT_TRACK, buf, frameCount);
    for (uint i = 0; i < readCount * mChannels; ++i)
      data[i] = Sonik::from<Sample16>(buf[i]);
    delete[] buf;
  }
  else if (mBits == 24)
  {
    Sample24* buf = new Sample24[sampleCount];
    readCount = afReadFrames(mHandle, AF_DEFAULT_TRACK, buf, frameCount);
    for (uint i = 0; i < readCount * mChannels; ++i)
      data[i] = Sonik::from<Sample24>(buf[i]);
    delete[] buf;
  }

  return readCount;
}

bool AFPlugin::Reader::optionsAvailable()
{
  return false;
}

QWidget* AFPlugin::Reader::makeOptionsPage(QWidget*, const char*)
{
  return 0;
}

AFPlugin::Writer::Writer(const QString& fileName, const QString& mimeType,
                         size_t length, uint8_t channels,
                         uint32_t sampleRate, uint8_t bits)
  : FileIO::Writer::Writer(fileName, mimeType,
                           length, channels, sampleRate, bits),
    mSampleFormat(AF_SAMPFMT_TWOSCOMP), mPos(0),
    mCompressionMode(kNone)
{
}

AFPlugin::Writer::~Writer()
{
}

Sonik::IOResult AFPlugin::Writer::open()
{
  AFfilesetup filesetup = afNewFileSetup();

  int format;
  // choose format from mimeType
  if (mMimeType == "audio/x-wav")
    format = AF_FILE_WAVE;
  else if (mMimeType == "audio/x-aiff")
  {
    if (mCompressionMode != kNone)
      format = AF_FILE_AIFFC;
    else
      format = AF_FILE_AIFF;
  }
  else if (mMimeType == "audio/basic" ||
           mMimeType == "audio/x-ulaw")
    format = AF_FILE_NEXTSND;
  else
  {
    afFreeFileSetup(filesetup);
    return kFileFormat;
  }

  afInitFileFormat(filesetup, format);

  int comp;
  switch (mCompressionMode)
  {
    case kALaw:
      comp = AF_COMPRESSION_G711_ALAW;
      break;
    case kULaw:
      comp = AF_COMPRESSION_G711_ULAW;
      break;
    case kNone:
    default:
      comp = AF_COMPRESSION_NONE;
      break;
  }
  afInitCompression(filesetup, AF_DEFAULT_TRACK, comp);

  afInitSampleFormat(filesetup, AF_DEFAULT_TRACK, mSampleFormat, mBits);
  afInitChannels(filesetup, AF_DEFAULT_TRACK, mChannels);
  afInitRate(filesetup, AF_DEFAULT_TRACK, mSampleRate);

  mHandle = afOpenFile(mFileName, "w", filesetup);
  if (!mHandle)
  {
    afFreeFileSetup(filesetup);
    return kFileNotFound;
  }

  afSetVirtualSampleFormat(mHandle, AF_DEFAULT_TRACK,
                           AF_SAMPFMT_TWOSCOMP, mBits);
  afSetVirtualByteOrder(mHandle, AF_DEFAULT_TRACK,
                        AF_BYTEORDER_LITTLEENDIAN);

  afFreeFileSetup(filesetup);

  return kSuccess;
}

void AFPlugin::Writer::close()
{
  afCloseFile(mHandle);
  mPos = 0;
}

uint32_t AFPlugin::Writer::write(const SampleBuffer& data)
{
  if (mHandle == 0)
    return 0;

  uint32_t sampleCount = data.size();
  uint32_t frameCount = sampleCount / mChannels;
  uint32_t writeCount = 0;

  if (mBits == 8)
  {
    Sample8* buf = new Sample8[sampleCount];
    for (uint i = 0; i < sampleCount; ++i)
      buf[i] = Sonik::to<Sample8>(data[i]);
    writeCount = afWriteFrames(mHandle, AF_DEFAULT_TRACK, buf, frameCount);
    delete[] buf;
  }
  else if (mBits == 16)
  {
    Sample16* buf = new Sample16[sampleCount];
    for (uint i = 0; i < sampleCount; ++i)
      buf[i] = Sonik::to<Sample16>(data[i]);
    writeCount = afWriteFrames(mHandle, AF_DEFAULT_TRACK, buf, frameCount);
    delete[] buf;
  }
  if (mBits == 24)
  {
    Sample24* buf = new Sample24[sampleCount];
    for (uint i = 0; i < sampleCount; ++i)
      buf[i] = Sonik::to<Sample24>(data[i]);
    writeCount = afWriteFrames(mHandle, AF_DEFAULT_TRACK, buf, frameCount);
    delete[] buf;
  }

  return writeCount;
}

bool AFPlugin::Writer::optionsAvailable()
{
  // TODO: check requested format supports compression
  return true;
}

QWidget* AFPlugin::Writer::makeOptionsPage(QWidget* parent, const char *name)
{
  return new AFWriteOptionsPage(*this, parent, name);
}

AFPlugin::AFPlugin(QObject* parent, const char* name, const QStringList& args)
  : Sonik::FileIO("libaudiofile", i18n("Audiofile"), parent, name, args)
{
  mReadFilter  = i18n("*.wav *.WAV|WAVE files (*.wav *.WAV)\n");
  mReadFilter += i18n("*.aiff *.AIFF|AIFF files (*.aiff *.AIFF)\n");
  mReadFilter += i18n("*.snd *.au|ULAW files (*.snd *.au)\n");
  mWriteFilter = mReadFilter;

  mReadMimeTypes.clear();
  mReadMimeTypes.append("audio/x-wav");
  mReadMimeTypes.append("audio/x-aiff");
  mReadMimeTypes.append("audio/basic");
  mReadMimeTypes.append("audio/x-ulaw");
  mWriteMimeTypes = mReadMimeTypes;
}

AFPlugin::~AFPlugin()
{
}

QWidget* AFPlugin::makeConfigPage(QWidget* parent)
{
  return 0;
}

void AFPlugin::applyConfigPage()
{
}

FileIO::Reader* AFPlugin::makeReader(const QString& fileName,
                                     const QString& mimeType) const
{
  return new AFPlugin::Reader(fileName, mimeType);
}

FileIO::Writer* AFPlugin::makeWriter(const QString& fileName,
                                     const QString& mimeType,
                                     size_t length, uint8_t channels,
                                     uint32_t sampleRate, uint8_t bits) const
{
  return new AFPlugin::Writer(fileName, mimeType,
                              length, channels, sampleRate, bits);
}

AFWriteOptionsPage::AFWriteOptionsPage(AFPlugin::Writer& w,
                                       QWidget* parent, const char *name)
  : QWidget(parent, name),
    mWriter(w)
{
  QHBoxLayout *layout = new QHBoxLayout(this, 0, KDialog::spacingHint());
  QLabel* label1 = new QLabel("Compression Mode", this);
  label1->setAlignment(AlignRight|AlignVCenter);
  layout->addWidget(label1);
  mCompressionMode = new QComboBox(this, "compression");
  mCompressionMode->insertItem("None",  0);
  mCompressionMode->insertItem("a Law", 1);
  mCompressionMode->insertItem("u Law", 2);
  switch (mWriter.compressionMode())
  {
  case AFPlugin::kNone:
    mCompressionMode->setCurrentItem(0);
    break;
  case AFPlugin::kALaw:
    mCompressionMode->setCurrentItem(1);
    break;
  case AFPlugin::kULaw:
    mCompressionMode->setCurrentItem(2);
    break;
  }

  layout->addWidget(mCompressionMode);
}

AFWriteOptionsPage::~AFWriteOptionsPage()
{
}

void AFWriteOptionsPage::okClicked()
{
  if (mCompressionMode->currentText() == "None")
    mWriter.setCompressionMode(AFPlugin::kNone);
  else if (mCompressionMode->currentText() == "a Law")
    mWriter.setCompressionMode(AFPlugin::kALaw);
  else if (mCompressionMode->currentText() == "u Law")
    mWriter.setCompressionMode(AFPlugin::kULaw);
}

//
// Factory definition
//
K_EXPORT_COMPONENT_FACTORY(libsonik_audiofile,
                           KGenericFactory<AFPlugin>(
                             "sonik-file-audiofile")
                           );

