

#if (!(defined(x64) || defined(__LP64__) || defined(LINUX) || defined(FREEBSD) || defined(HAIKU)))

// Toonz stuff
#include "tiio.h"
#include "timage_io.h"
#include "tlevel_io.h"
#include "trasterimage.h"
#include "traster.h"
#include "tstream.h"
#include "tproperty.h"
#include "tsound.h"

// tipc includes
#include "tipc.h"
#include "tipcmsg.h"
#include "tipcsrv.h"
#include "t32bitsrv_wrap.h"

#include "t32movmsg.h"

// movesettings deps, must be included before QuickTime includes
#include "texception.h"
#include "tpropertytype.h"
#include "tproperty.h"

// following includes may include QuickTime
#if defined(_WIN32)
#include "../image/mov/tiio_mov.h"
#elif defined(MACOSX)
#include "../image/mov/tiio_movM.h"
#include <ApplicationServices/ApplicationServices.h>
#endif
#include "movsettings.h"

// Qt stuff
#include <QString>
#include <QHash>
#include <QSharedMemory>
#include <QDebug>

#include <QLocalSocket>
#include <QDataStream>

//---------------------------------------------------

//  Diagnostics stuff

//#define TIPC_DEBUG

#ifdef TIPC_DEBUG
#define tipc_debug(expr) expr
#else
#define tipc_debug(expr)
#endif

#ifdef TIPC_DEBUG
#include <QTime>
#endif

//---------------------------------------------------

//  Local namespace stuff

namespace {
QHash<unsigned int, TLevelReaderP> readers;
QHash<unsigned int, TLevelWriterP> writers;
}

//---------------------------------------------------

using namespace tipc;

namespace mov_io {

void addParsers(tipc::Server *srv) {
  srv->addParser(new IsQTInstalledParser);
  srv->addParser(new DefaultMovPropsParser);
  srv->addParser(new OpenMovSettingsPopupParser);
  srv->addParser(new InitLWMovParser);
  srv->addParser(new LWSetFrameRateParser);
  srv->addParser(new LWImageWriteParser);
  srv->addParser(new LWSaveSoundTrackParser);
  srv->addParser(new CloseLWMovParser);
  srv->addParser(new InitLRMovParser);
  srv->addParser(new LRLoadInfoParser);
  srv->addParser(new LREnableRandomAccessReadParser);
  srv->addParser(new LRImageReadParser);
  srv->addParser(new LRImageReadSHMParser);
  srv->addParser(new CloseLRMovParser);

#ifdef WIN32
  srv->addParser(new LRSetYMirrorParser);
  srv->addParser(new LRSetLoadTimecodeParser);
  srv->addParser(new LRTimecodeParser);
#endif
}

//************************************************************************
//    IsQTInstalled Parser
//************************************************************************

void IsQTInstalledParser::operator()(Message &msg) {
  bool ret = Tiio::isQuicktimeInstalled();
  msg << clr << QString((ret) ? "yes" : "no");
}

//************************************************************************
//    DefaultMovProps Parser
//************************************************************************

void DefaultMovPropsParser::operator()(Message &msg) {
  // Ensure the file path was passed - and retrieve it
  QString reply;
  msg >> reply >> clr;
  if (reply.isEmpty()) goto err;

#ifdef WIN32
  // Ensure that QuickTime is correctly running
  if (InitializeQTML(0) != noErr) goto err;
#endif

  // Success - retrieve the props
  {
    TPropertyGroup movProps;
    {
      // Low-level QuickTime stuff
      ComponentInstance ci = OpenDefaultComponent(StandardCompressionType,
                                                  StandardCompressionSubType);
      QTAtomContainer settings;

      if (SCGetSettingsAsAtomContainer(ci, &settings) != noErr) assert(false);

      fromAtomsToProperties(settings, movProps);
    }

    // Write the retrieved properties
    TFilePath tfp(reply.toStdWString());
    TOStream os(tfp);

    movProps.saveData(os);

    msg << QString("ok");
  }

  return;

err:

  msg << QString("err");
}

//************************************************************************
//    OpenMovSettingsPopup Parser
//************************************************************************

void OpenMovSettingsPopupParser::operator()(Message &msg) {
  // Open the properties file
  QString fp;
  msg >> fp >> clr;

  // Retrieve the properties
  TPropertyGroup *props = new TPropertyGroup;
  TFilePath tfp(fp.toStdWString());
  {
    TIStream is(tfp);
    props->loadData(is);
  }

#ifdef MACOSX

  ProcessSerialNumber psn = {0, kCurrentProcess};
  TransformProcessType(&psn, kProcessTransformToForegroundApplication);
  SetFrontProcess(&psn);

#endif

  openMovSettingsPopup(props, true);

  {
    TOStream os(tfp);  // Should NOT append
    props->saveData(os);
  }

  delete props;

  msg << QString("ok");
}

//************************************************************************
//    InitLWMov Parser
//************************************************************************

void InitLWMovParser::operator()(Message &msg) {
  unsigned int id;
  QString fp, propsFp;
  msg >> id >> fp >> propsFp >> clr;

  TFilePath tfp(fp.toStdWString()), propsTFp(propsFp.toStdWString());

  try {
    TPropertyGroup *props = 0;
    if (!propsTFp.isEmpty()) {
      props = new TPropertyGroup;

      TIStream is(propsTFp);
      props->loadData(is);
    }

    writers.insert(id, TLevelWriterP(tfp, props));

    msg << QString("ok");
  } catch (...) {
    msg << QString("err");
  }
}

//************************************************************************
//    LWsetFrameRate Parser
//************************************************************************

void LWSetFrameRateParser::operator()(Message &msg) {
  unsigned int id;
  double fps;

  msg >> id >> fps >> clr;

  writers.find(id).value()->setFrameRate(fps);

  msg << QString("ok");
}

//************************************************************************
//    LWImageWrite Parser
//************************************************************************

void LWImageWriteParser::operator()(Message &msg) {
  unsigned int id;
  int frameIdx, lx, ly;

  msg >> id >> frameIdx >> lx >> ly;

  // Read the data through a shared memory segment
  TRaster32P ras(lx, ly);
  t32bitsrv::RasterExchanger<TPixel32> exch(ras);
  tipc::readShMemBuffer(*stream(), msg, &exch);

  // Save the image
  try {
    TImageWriterP iw(writers.find(id).value()->getFrameWriter(frameIdx + 1));
    iw->save(TRasterImageP(ras));

    msg << QString("ok");
  } catch (...) {
    msg << QString("err");
  }
}

//************************************************************************
//    LWSaveSoundTrack Parser
//************************************************************************

void LWSaveSoundTrackParser::operator()(Message &msg) {
  unsigned int id;
  QString shMemId;

  TUINT32 sampleRate;
  TINT32 sCount;
  int bps, chanCount;
  bool signedSample;

  msg >> id >> sampleRate >> bps >> chanCount >> sCount >> signedSample;

  // Retrieve the soundtrack buffer
  TSoundTrackP st =
      TSoundTrack::create(sampleRate, bps, chanCount, sCount, signedSample);
  t32bitsrv::BufferExchanger exch((UCHAR *)st->getRawData());
  tipc::readShMemBuffer(*stream(), msg, &exch);

  // Write the soundtrack
  try {
    writers.find(id).value()->saveSoundTrack(st.getPointer());
    msg << QString("ok");
  } catch (...) {
    msg << QString("err");
  }
}

//************************************************************************
//    CloseLWMov Parser
//************************************************************************

void CloseLWMovParser::operator()(Message &msg) {
  unsigned int id;
  msg >> id >> clr;

  try {
    writers.take(id);
    msg << QString("ok");
  } catch (...) {
    msg << QString("err");
  }
}

//************************************************************************
//    InitLRMov Parser
//************************************************************************

void InitLRMovParser::operator()(Message &msg) {
  unsigned int id;
  QString fp, propsFp;
  msg >> id >> fp >> clr;
  assert(!fp.isEmpty());

  TFilePath tfp(fp.toStdWString());

  try {
    TLevelReaderP lrm(tfp);

    // Extract some info to be returned
    const TImageInfo *info = lrm->getImageInfo();
    if (!info) throw TImageException(tfp, "Couldn't retrieve image properties");

    int lx = info->m_lx, ly = info->m_ly;
    double frameRate = info->m_frameRate;

    tipc_debug(qDebug() << "Inserted image"
                        << QString::fromStdWString(tfp.getWideString()));
    readers.insert(id, lrm);

    msg << QString("ok") << lx << ly << frameRate;
  } catch (...) {
    msg << QString("err");
  }
}

//************************************************************************
//    LRLoadInfo Parser
//************************************************************************

void LRLoadInfoParser::operator()(Message &msg) {
  // Read command data
  unsigned int id;
  QString shMemId;
  msg >> id >> shMemId >> clr;

  QHash<unsigned int, TLevelReaderP>::iterator it = readers.find(id);
  if (it == readers.end()) goto err;

  // Read level infos
  {
    TLevelP level;
    try {
      level = it.value()->loadInfo();
    } catch (...) {
      goto err;
    }

    int frameCount = level->getFrameCount();
    if (!shMemId.isEmpty()) {
      // Create a shared memory segment to transfer the infos to
      tipc::DefaultMessageParser<SHMEM_REQUEST> msgParser;
      Message shMsg;

      shMsg << shMemId << frameCount * (int)sizeof(int) << reset;
      msgParser(shMsg);

      QString str;
      shMsg >> reset >> str;
      if (str != QString("ok")) goto err;

      // Copy level data to the shared memory segment
      {
        QSharedMemory shmem(shMemId);
        shmem.attach();
        shmem.lock();

        TLevel::Table *table = level->getTable();

        TLevel::Table::const_iterator jt;
        int *f = (int *)shmem.data();
        for (jt = table->begin(); jt != table->end(); ++jt, ++f)
          *f = jt->first.getNumber();

        shmem.unlock();
        shmem.detach();
      }
    }

    msg << QString("ok") << frameCount;
  }

  return;

err:

  msg << QString("err");
}

//************************************************************************
//    LREnableRandomAccessRead Parser
//************************************************************************

void LREnableRandomAccessReadParser::operator()(Message &msg) {
  unsigned int id;
  QString str;
  msg >> id >> str >> clr;
  bool enable = (str == "true");

  QHash<unsigned int, TLevelReaderP>::iterator it = readers.find(id);
  if (it == readers.end()) {
    msg << QString("err");
    return;
  }

  it.value()->enableRandomAccessRead(enable);

  msg << QString("ok");
}

//************************************************************************
//    LRSetYMirror Parser
//************************************************************************

#ifdef WIN32

void LRSetYMirrorParser::operator()(Message &msg) {
  unsigned int id;
  QString str;
  msg >> id >> str >> clr;
  bool enable = (str == "true");

  QHash<unsigned int, TLevelReaderP>::iterator it = readers.find(id);
  if (it == readers.end()) {
    msg << QString("err");
    return;
  }

  static_cast<TLevelReaderMov *>(it.value().getPointer())->setYMirror(enable);

  msg << QString("ok");
}

//************************************************************************
//    LRSetLoadTimecode Parser
//************************************************************************

void LRSetLoadTimecodeParser::operator()(Message &msg) {
  unsigned int id;
  QString str;
  msg >> id >> str >> clr;
  bool enable = (str == "true");

  QHash<unsigned int, TLevelReaderP>::iterator it = readers.find(id);
  if (it == readers.end()) {
    msg << QString("err");
    return;
  }

  static_cast<TLevelReaderMov *>(it.value().getPointer())
      ->setLoadTimecode(enable);

  msg << QString("ok");
}

//************************************************************************
//    LRTimecode Parser
//************************************************************************

void LRTimecodeParser::operator()(Message &msg) {
  unsigned int id;
  int frameIdx;
  QString str;
  msg >> id >> frameIdx >> clr;

  QHash<unsigned int, TLevelReaderP>::iterator it = readers.find(id);
  if (it == readers.end()) {
    msg << QString("err");
    return;
  }

  UCHAR hh, mm, ss, ff;
  static_cast<TLevelReaderMov *>(it.value().getPointer())
      ->timecode(frameIdx, hh, mm, ss, ff);

  msg << QString("ok") << hh << mm << ss << ff;
}

#endif

//************************************************************************
//    LRImageRead Parser
//************************************************************************

void LRImageReadParser::operator()(Message &msg) {
  tipc_debug(QTime fTime; QTime irTime; QTime shTime; fTime.start(););

  {
    unsigned int id;
    int lx, ly, pixSize, frameIdx, x, y, shrinkX, shrinkY;

    msg >> id >> lx >> ly >> pixSize >> frameIdx >> x >> y >> shrinkX >>
        shrinkY >> clr;

    if (pixSize != 4) goto err;

    QHash<unsigned int, TLevelReaderP>::iterator it = readers.find(id);
    if (it == readers.end()) goto err;

    tipc_debug(irTime.start());

    // Load the raster
    TRaster32P ras(lx, ly);
    try {
      TImageReaderP ir(it.value()->getFrameReader(frameIdx + 1));
      ir->load(ras, TPoint(x, y), shrinkX, shrinkY);
    } catch (...) {
      goto err;
    }

    tipc_debug(qDebug() << "load time:" << irTime.elapsed());
    tipc_debug(shTime.start());

    t32bitsrv::RasterExchanger<TPixel32> exch(ras);
    if (!tipc::writeShMemBuffer(*stream(), msg << clr,
                                lx * ly * sizeof(TPixel32), &exch))
      goto err;

    tipc_debug(qDebug() << "exchange time:" << shTime.elapsed());
    tipc_debug(qDebug() << "TLevelReaderMov::loadImage time:"
                        << fTime.elapsed());
  }

  return;

err:

  msg << QString("err");
}

//************************************************************************
//    LRImageReadSHM Parser
//************************************************************************

void LRImageReadSHMParser::operator()(Message &msg) {
  tipc_debug(QTime fTime; QTime irTime; fTime.start(););

  unsigned int id;
  int lx, ly, frameIdx;
  QString shMemId;

  msg >> id >> lx >> ly >> frameIdx >> shMemId >> clr;

  tipc_debug(qDebug() << "LoadImageSHM data:" << id << lx << ly << frameIdx
                      << shMemId);

  QHash<unsigned int, TLevelReaderP>::iterator it = readers.find(id);
  if (it == readers.end()) goto err;

  // Attach the shared memory segment the raster
  {
    QSharedMemory shm(shMemId);
    shm.attach();
    if (!shm.isAttached()) goto err;

    // Load the raster
    TRaster32P ras(lx, ly, lx, (TPixel32 *)shm.data());
    try {
      tipc_debug(qDebug() << "loading image...");
      tipc_debug(irTime.start());

      shm.lock();
      TImageReaderP ir(it.value()->getFrameReader(frameIdx + 1));
      ir->load(ras, TPoint(), 1, 1);
      shm.unlock();

      tipc_debug(qDebug() << "load time:" << irTime.elapsed());
    } catch (TImageException e) {
      shm.unlock();
      tipc_debug(qDebug() << "Image Read Error:"
                          << QString::fromStdWString(e.getMessage()));
      goto err;
    } catch (...) {
      shm.unlock();
      tipc_debug(qDebug() << "Unknown Image Read Error");
      goto err;
    }
  }

  msg << QString("ok");

#ifdef WIN32

  UCHAR hh, mm, ss, ff;
  TLevelReaderMov *lrm =
      static_cast<TLevelReaderMov *>(it.value().getPointer());
  lrm->loadedTimecode(hh, mm, ss, ff);

  tipc_debug(qDebug() << "TLevelReaderMov::loadImage time:" << fTime.elapsed());
  msg << hh << mm << ss << ff;

#endif

  return;

err:

  msg << QString("err");
}

//************************************************************************
//    CloseLRMov Parser
//************************************************************************

void CloseLRMovParser::operator()(Message &msg) {
  unsigned int id;
  msg >> id >> clr;

  readers.take(id);
  msg << QString("ok");
}

}  // namespace mov_io

#endif  // !x64 && !__LP64__
