

// TnzFarm includes
#include "tfarmcontroller.h"

// TnzLib includes
#include "toonz/txshleveltypes.h"
#include "toonz/tpalettehandle.h"
#include "toonz/tscenehandle.h"
#include "toonz/txshlevelhandle.h"
#include "toonz/sceneproperties.h"
#include "toonz/levelproperties.h"
#include "toonz/levelupdater.h"
#include "toonz/preferences.h"
#include "toonz/toonzfolders.h"
#include "toonz/toonzscene.h"
#include "toonz/txshchildlevel.h"
#include "toonz/tproject.h"
#include "toonz/tcleanupper.h"
#include "toonz/txsheet.h"
#include "toonz/txshcell.h"
#include "toonz/txshcolumn.h"
#include "toonz/tlog.h"
#include "toonz/imagestyles.h"
#include "toonz/filepathproperties.h"

// TnzBase includes
#include "tcli.h"
#include "tenv.h"

// TnzCore includes
#include "tsystem.h"
#include "tstream.h"
#include "tstopwatch.h"
#include "tthread.h"
#include "tthreadmessage.h"
#include "timagecache.h"
#include "tiio_std.h"
#include "tnzimage.h"
#include "tmsgcore.h"
#include "tpluginmanager.h"
#include "tpalette.h"
#include "tsimplecolorstyles.h"

// Qt includes
#include <QApplication>

using namespace TCli;
using namespace std;

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

inline ostream &operator<<(ostream &out, const wstring &w) {
  return out << ::to_string(w);
}

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

inline ostream &operator<<(ostream &out, const TFilePath &fp) {
  return out << fp.getWideString();
}

//------------------------------------------------------------------------
namespace {

const char *rootVarName     = "TOONZROOT";
const char *systemVarPrefix = "TOONZ";

namespace {

CleanupParameters GlobalParameters;
TFilePath CurrentSettingsFile = TFilePath();

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

void loadSettings(const TFilePath &settingsFile, CleanupParameters *cp) {
  if (CurrentSettingsFile ==
      TFilePath())  // the parameters were global ...I store them
    GlobalParameters.assign(cp);

  CurrentSettingsFile = settingsFile;

  TIStream *is = new TIStream(settingsFile);
  int minor, major;
  string tagName;

  // Extract file version if any
  is->matchTag(tagName);
  if (tagName == "version") {
    *is >> major >> minor;
    is->matchEndTag();
    is->setVersion(VersionNumber(major, minor));
  } else {
    delete is;
    is = new TIStream(settingsFile);
  }

  cp->loadData(*is, true);

  delete is;
}

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

void restoreGlobalSettings(CleanupParameters *cp) {
  if (CurrentSettingsFile == TFilePath()) return;  // already global!

  cp->assign(&GlobalParameters);
  CurrentSettingsFile = TFilePath();
}

}  // namespace

//========================================================================
//
// fatalError
//
//------------------------------------------------------------------------

void fatalError(string msg) {
#ifdef _WIN32
  msg = "Application can't start:\n" + msg;
  DVGui::error(QString::fromStdString(msg));
  // MessageBox(0,msg.c_str(),"Fatal error",MB_ICONERROR);
  exit(1);
#else
  // TODO: Come si fa ad aggiungere un messaggio di errore qui?
  std::cout << msg << std::endl;
  abort();
#endif
}

}  // namespace

#ifdef MACOSX

inline bool isBlank(char c) { return c == ' ' || c == '\t' || c == '\n'; }

//========================================================================
// setToonzFolder
//------------------------------------------------------------------------

// Ritorna il path della variabile passata come secondo argomento
// entrambe vengono lette da un file di testo (filename).

TFilePath setToonzFolder(const TFilePath &filename, std::string toonzVar) {
  Tifstream is(filename);
  if (!is) return TFilePath();

  char buffer[1024];

  while (is.getline(buffer, sizeof(buffer))) {
    // le righe dentro toonzenv.txt sono del tipo
    // export set TOONZPROJECT="....."

    // devo trovare la linea che contiene toonzVar
    char *s = buffer;
    while (isBlank(*s)) s++;
    // Se la riga  vuota, o inizia per # o ! salto alla prossima
    if (*s == '\0' || *s == '#' || *s == '!') continue;
    if (*s == '=') continue;  // errore: nome variabile non c'
    char *t = s;
    // Mi prendo la sottoStringa fino all'=
    while (*t && *t != '=') t++;
    if (*t != '=') continue;  // errore: manca '='
    char *q = t;
    // Torno indietro fino al primo blank
    while (q > s && !isBlank(*(q - 1))) q--;
    if (q == s)
      continue;  // non dovrebbe mai succedere: prima di '=' tutti blanks

    string toonzVarString(q, t);

    // Confronto la stringa trovata con toonzVar, se   lei vado avanti.
    if (toonzVar != toonzVarString) continue;  // errore: stringhe diverse
    s = t + 1;
    // Salto gli spazi
    while (isBlank(*s)) s++;
    if (*s == '\0') continue;  // errore: dst vuoto
    t = s;
    while (*t) t++;
    while (t > s && isBlank(*(t - 1))) t--;
    if (t == s) continue;
    // ATTENZIONE : tolgo le virgolette !!
    string pathName(s + 1, t - s - 2);
    return TFilePath(pathName);
  }

  return TFilePath();
}

#endif

//==============================================================================================

static void prepareToCleanup(TXshSimpleLevel *xl, TPalette *cleanupPalette) {
  assert(xl->getScene());
  if (xl->getProperties()->getSubsampling() != 1) {
    xl->getProperties()->setSubsampling(1);
    xl->invalidateFrames();
  }

  /* int ltype = xl->getType();
if(ltype != TZP_XSHLEVEL)
xl->makeTlv(xl->getScene()->getDefaultParentDir(TZP_XSHLEVEL));
if(xl->getPalette()==0)
{
//PaletteController* pc = TApp::instance()->getPaletteController();
          xl->setPalette(cleanupPalette);
          //if(xl == TApp::instance()->getCurrentLevel()->getLevel())
          //	pc->getCurrentLevelPalette()->setPalette(xl->getPalette());
}*/
}

//==============================================================================================

namespace {
TStopWatch Sw1;
TStopWatch Sw2;

bool UseRenderFarm = false;
string FarmControllerName;
int FarmControllerPort;

TFarmController *FarmController = 0;

string TaskId;
}  // namespace
//========================================================================
//
// searchLevelsToCleanup
//
// Restituisce l'elenco (ordinato per ordine alfabetico di nome)
// dei livelli TLV(con scannedPath)/TZI/OVL presenti nell'xsheet (o nei
// sottoxsheet)
//
// se selectedOnly = true salta i livelli presenti nelle colonne senza
// "occhietto"
//
//------------------------------------------------------------------------

static void searchLevelsToCleanup(
    std::vector<std::pair<TXshSimpleLevel *, std::set<TFrameId>>> &levels,
    TXsheet *xsh, bool selectedOnly) {
  std::map<wstring, TXshSimpleLevel *> levelTable;
  std::map<wstring, std::set<TFrameId>> framesTable;

  std::set<TXsheet *> visited;
  std::vector<TXsheet *> xsheets;
  xsheets.push_back(xsh);
  visited.insert(xsh);
  while (!xsheets.empty()) {
    TXsheet *xsh = xsheets.back();
    xsheets.pop_back();
    for (int c = 0; c < xsh->getColumnCount(); c++) {
      TXshColumn *column = xsh->getColumn(c);
      if (!column || column->isEmpty()) continue;
      // if(selectedOnly && !column->isPreviewVisible()) continue;
      int r0 = 0, r1 = -1;
      xsh->getCellRange(c, r0, r1);
      for (int r = r0; r <= r1; r++) {
        TXshCell cell = xsh->getCell(r, c);
        if (cell.isEmpty()) continue;
        if (TXshSimpleLevel *sl = cell.m_level->getSimpleLevel()) {
          if (selectedOnly && !column->isPreviewVisible())  // pezza: se questo
                                                            // "if" veniva fatto
                                                            // sopra(riga
                                                            // commentata)
          // quando si fa save della Scene alla fine della cleanuppata,
          // venivano backuppate le tif non processate e cancellati gli
          // originali!
          // Deliri del levelUPdater; evito di mettere le mani in quel pattume.
          // vinz
          {
            sl->setDirtyFlag(false);
            continue;
          }
          int ltype = sl->getType();
          if ((ltype == TZP_XSHLEVEL && sl->getScannedPath() != TFilePath()) ||
              ltype == OVL_XSHLEVEL || ltype == TZI_XSHLEVEL) {
            wstring levelName     = sl->getName();
            levelTable[levelName] = sl;
            framesTable[levelName].insert(cell.m_frameId);
          }
        } else if (TXshChildLevel *cl = cell.m_level->getChildLevel()) {
          TXsheet *subXsh = cl->getXsheet();
          if (visited.count(subXsh) == 0) {
            visited.insert(subXsh);
            xsheets.push_back(subXsh);
          }
        }
      }
    }
  }

  assert(levelTable.size() == framesTable.size());

  for (auto const &level : levelTable) {
    auto const it = framesTable.find(level.first);
    if (it == framesTable.end()) {
      continue;
    }
    levels.push_back(std::make_pair(level.second, (*it).second));
  }
}

//------------------------------------------------------------------------------
/*- CleanupDefaultパレットを追加する -*/
static void addCleanupDefaultPalette(TXshSimpleLevel *sl) {
  /*- 元となるパレットはStudioPaletteフォルダに置く -*/
  TFilePath palettePath =
      ToonzFolder::getStudioPaletteFolder() + "cleanup_default.tpl";
  TFileStatus pfs(palettePath);

  if (!pfs.doesExist() || !pfs.isReadable()) {
    wcout << L"CleanupDefaultPalette file: " << palettePath.getWideString()
          << L" is not found!" << endl;
    return;
  }

  TIStream is(palettePath);
  if (!is) {
    cout << "CleanupDefaultPalette file: failed to get TIStream" << endl;
    return;
  }

  string tagName;
  if (!is.matchTag(tagName) || tagName != "palette") {
    cout << "CleanupDefaultPalette file: This is not palette file" << endl;
    return;
  }

  string gname;
  is.getTagParam("name", gname);
  TPalette *defaultPalette = new TPalette();
  defaultPalette->loadData(is);

  sl->getPalette()->setIsCleanupPalette(false);

  TPalette::Page *dstPage = sl->getPalette()->getPage(0);
  TPalette::Page *srcPage = defaultPalette->getPage(0);

  for (int srcIndexInPage = 0; srcIndexInPage < srcPage->getStyleCount();
       srcIndexInPage++) {
    int id = srcPage->getStyleId(srcIndexInPage);

    bool isUsedInCleanupPalette;
    isUsedInCleanupPalette = false;

    for (int dstIndexInPage = 0; dstIndexInPage < dstPage->getStyleCount();
         dstIndexInPage++) {
      if (dstPage->getStyleId(dstIndexInPage) == id) {
        isUsedInCleanupPalette = true;
        break;
      }
    }

    if (isUsedInCleanupPalette)
      continue;

    else {
      int addedId = sl->getPalette()->addStyle(
          srcPage->getStyle(srcIndexInPage)->clone());
      dstPage->addStyle(addedId);
      /*- StudioPalette由来のDefaultPaletteの場合、GrobalNameを消去する -*/
      sl->getPalette()->getStyle(addedId)->setGlobalName(L"");
      sl->getPalette()->getStyle(addedId)->setOriginalName(L"");
    }
  }

  delete defaultPalette;
}

//========================================================================
//
// cleanupLevel
//
// effettua il cleanup del livello. Se il livello e' un fullcolor
// modifica tipo e parametri e crea la palette
//
// se overwrite == false non fa il cleanup dei frames gia' cleanuppati
//
//------------------------------------------------------------------------

static void cleanupLevel(TXshSimpleLevel *xl, std::set<TFrameId> fidsInXsheet,
                         ToonzScene *scene, bool overwrite,
                         TUserLogAppend &m_userLog) {
  prepareToCleanup(xl, scene->getProperties()
                           ->getCleanupParameters()
                           ->m_cleanupPalette.getPointer());

  TCleanupper *cl = TCleanupper::instance();

  TFilePath fp = scene->decodeFilePath(xl->getPath());
  TSystem::touchParentDir(fp);
  cout << "cleanupping " << xl->getName() << " path=" << fp << endl;
  string info = "cleanupping " + ::to_string(xl->getPath());
  LevelUpdater updater(xl);
  m_userLog.info(info);
  DVGui::info(QString::fromStdString(info));
  bool firstImage = true;
  for (auto const &fid : fidsInXsheet) {
    cout << "  " << fid << endl;
    info = "  " + fid.expand();
    m_userLog.info(info);
    int status = xl->getFrameStatus(fid);

    if (0 != (status & TXshSimpleLevel::Cleanupped) && !overwrite) {
      cout << "  skipped" << endl;
      m_userLog.info("  skipped");
      DVGui::info(QString("--skipped frame ") +
                  QString::fromStdString(fid.expand()));
      continue;
    }
    CleanupParameters *params = scene->getProperties()->getCleanupParameters();
    // if lines are not processed, obtain the original sampled image
    bool toBeLineProcessed = params->m_lineProcessingMode != lpNone;
    TRasterImageP original = xl->getFrameToCleanup(fid, toBeLineProcessed);
    if (!original) {
      string err = "    *error* missed frame";
      m_userLog.error(err);
      cout << err << endl;
      continue;
    }

    if (params->m_lineProcessingMode == lpNone) {
      TRasterImageP ri(original);
      /*if (params->m_autocenterType == CleanupTypes::AUTOCENTER_NONE)
        ri = original;
      else {
        bool autocentered;
        ri = cl->autocenterOnly(original, false, autocentered);
        if (!autocentered) {
          m_userLog.error("The autocentering failed on the current drawing.");
          cout << "The autocentering failed on the current drawing." << endl;
        }
      }*/
      cl->process(original, false, ri, false, true, true, nullptr,
                  ri->getRaster());
      updater.update(fid, ri);
      continue;
    }
    // Obtain the source dpi. Changed it to be done once at the first frame of
    // each level in order to avoid the following problem:
    // If the original raster level has no dpi (such as TGA images), obtaining
    // dpi in every frame causes dpi mismatch between the first frame and the
    // following frames, since the value
    // TXshSimpleLevel::m_properties->getDpi() will be changed to the
    // dpi of cleanup camera (= TLV's dpi) after finishing the first frame.
    if (firstImage) {
      TPointD dpi;
      original->getDpi(dpi.x, dpi.y);
      if (dpi.x == 0 && dpi.y == 0) dpi = xl->getProperties()->getDpi();
      cl->setSourceDpi(dpi);
    }

    CleanupPreprocessedImage *cpi;
    {
      TRasterImageP resampledImage;
      cpi = cl->process(original, firstImage, resampledImage);
    }

    TToonzImageP timage = cl->finalize(cpi, true);
    TPointD dpi(0, 0);
    timage->getDpi(dpi.x, dpi.y);
    if (dpi.x != 0 && dpi.y != 0) xl->getProperties()->setDpi(dpi);

    if (firstImage) addCleanupDefaultPalette(xl);
    firstImage = false;

    timage->setPalette(xl->getPalette());
    xl->setFrameStatus(fid, status | TXshSimpleLevel::Cleanupped);
    xl->setFrame(fid, timage);

    updater.update(fid, timage);

    /*- 1フレーム終わったら、そのフレームのキャッシュは消す -*/
    xl->invalidateFrame(fid);

    delete cpi;
  }
}

//========================================================================
//
// main
//
// usage: tcleanup filename.tnz [-selected][-overwrite]
//
//------------------------------------------------------------------------

int main(int argc, char *argv[]) {
  QApplication app(argc, argv);

  // questo definisce la registry root e inizializza TEnv
  TEnv::setRootVarName(rootVarName);
  TEnv::setSystemVarPrefix(systemVarPrefix);
  TEnv::setApplicationFileName(argv[0]);

  QCoreApplication::setOrganizationName("OpenToonz");
  QCoreApplication::setOrganizationDomain("");
  QCoreApplication::setApplicationName(
      QString::fromStdString(TEnv::getApplicationName()));

  TSystem::hasMainLoop(false);
  int i;
  for (i = 0; i < argc; i++)  // tmsg must be set as soon as it's possible
  {
    QString str = argv[i];
    if (str == "-tmsg")

    {
      TMsgCore::instance()->connectTo(argv[i + 1]);
      break;
    }
  }
  if (i == argc) TMsgCore::instance()->connectTo("");

  TFilePath fproot = TEnv::getStuffDir();
  if (fproot == TFilePath())
    fatalError(string("Undefined: \"") + ::to_string(TEnv::getRootVarPath()) +
               "\"");
  if (!TFileStatus(fproot).isDirectory())
    fatalError(string("Directory \"") + ::to_string(fproot) +
               "\" not found or not readable");

  TFilePath lRootDir    = TEnv::getStuffDir() + "toonzfarm";
  TFilePath logFilePath = lRootDir + "tcleanup.log";
  TUserLogAppend m_userLog(logFilePath);

  TFilePathSet fps = ToonzFolder::getProjectsFolders();
  TFilePathSet::iterator fpIt;
  for (fpIt = fps.begin(); fpIt != fps.end(); ++fpIt)
    TProjectManager::instance()->addProjectsRoot(*fpIt);

  TFilePath libraryFolder = ToonzFolder::getLibraryFolder();
  TRasterImagePatternStrokeStyle::setRootDir(libraryFolder);
  TVectorImagePatternStrokeStyle::setRootDir(libraryFolder);
  TPalette::setRootDir(libraryFolder);
  TImageStyle::setLibraryDir(libraryFolder);
  TFilePath cacheRoot = ToonzFolder::getCacheRootFolder();
  if (cacheRoot.isEmpty()) cacheRoot = TEnv::getStuffDir() + "cache";
  TImageCache::instance()->setRootDir(cacheRoot);

  FilePathArgument srcName("tnzFile", "Scene file");
  SimpleQualifier selectedOnlyOption("-onlyvisible", "Selected column only");

  SimpleQualifier overwriteAllOption("-overwriteAll",
                                     "Overwrite all already cleanupped frames");
  SimpleQualifier overwriteNoPaintOption(
      "-overwriteNoPaint",
      "Overwrite only no-paint levels of already cleanupped frames");

  StringQualifier farmData("-farm data", "TFarm Controller");
  StringQualifier idq("-id n", "id");
  StringQualifier tmsg("-tmsg n", "Internal use only");
  Usage usage(argv[0]);
  usage.add(srcName + selectedOnlyOption + overwriteAllOption +
            overwriteNoPaintOption + farmData + idq + tmsg);
  if (!usage.parse(argc, argv)) exit(1);

  TaskId       = idq.getValue();
  string fdata = farmData.getValue();
  if (fdata.empty())
    UseRenderFarm = false;
  else {
    UseRenderFarm         = true;
    string::size_type pos = fdata.find('@');
    if (pos == string::npos)
      UseRenderFarm = false;
    else {
      FarmControllerPort = std::stoi(fdata.substr(0, pos));
      FarmControllerName = fdata.substr(pos + 1);
    }
  }

  if (UseRenderFarm) {
    TFarmControllerFactory factory;
    factory.create(QString::fromStdString(FarmControllerName),
                   FarmControllerPort, &FarmController);
  }

  /*- 画像Read/Writeの関数を登録 -*/
  initImageIo();
  Tiio::defineStd();

  /*- プロジェクトのロード -*/
  auto project = TProjectManager::instance()->loadSceneProject(srcName);

  if (!project) {
    string err = "Couldn't find the project" + project->getName().getName();
    m_userLog.error(err);
    cerr << err << endl;
    return -2;
  }

  cout << "project:" << project->getName() << endl;

  // update TFilePath condition on loading the current project
  FilePathProperties *fpProp = project->getFilePathProperties();
  TFilePath::setFilePathProperties(fpProp->useStandard(),
                                   fpProp->acceptNonAlphabetSuffix(),
                                   fpProp->letterCountForSuffix());

  TFilePath fp = srcName;

  /*- CLNファイルを直接指定した場合 -*/
  bool sourceFileIsCleanupSetting = (fp.getType() == "cln");

  bool selectedOnly = selectedOnlyOption;

  ToonzScene *scene = new ToonzScene();

  TImageStyle::setCurrentScene(scene);

  /*- シーンファイルパスを入力した場合 -*/
  if (!sourceFileIsCleanupSetting) {
    try {
      scene->loadNoResources(fp);
    } catch (...) {
      string err = "can't read " + fp.getName();
      m_userLog.error(err);
      cerr << "can't read " << fp << endl;
      TImageCache::instance()->clear(true);
      return -3;
    }
  }
  /*-- CleanupSettingsファイルパスを直接入力した場合 --*/
  else {
    try {
      TProjectManager *pm = TProjectManager::instance();
      auto sceneProject = pm->loadSceneProject(fp);
      if (!sceneProject) {
        cerr << "can't open project." << endl;
        return -3;
      }
      scene->setProject(sceneProject);

      /*- CleanupSettingsファイルに対応するTIFファイルが有るかチェック -*/
      TFilePath tifImagePath = fp.withType("tif");
      std::wcout << L"tifImagePath : " << tifImagePath.getWideString()
                 << std::endl;

      /*-
       * 無ければ、連番画像がソースである可能性がある。tifImagePathを連番に差し替える
       * -*/
      if (!TFileStatus(tifImagePath).doesExist()) {
        tifImagePath =
            tifImagePath.getParentDir() + (tifImagePath.getName() + "..tif");
        std::wcout << "change the source path to : "
                   << tifImagePath.getWideString() << std::endl;
      } else
        std::cout << "tif single image found" << std::endl;

      /*- シーンファイルに対象のTIFファイルをロード -*/
      TXshLevel *xl = scene->loadLevel(tifImagePath);
      if (!xl) {
        std::cout << "failed to load level" << std::endl;
        throw TException("Failed to load level.");
      }
      std::vector<TFrameId> dummy;
      int frameLength = scene->getXsheet()->exposeLevel(0, 0, xl, dummy);
      std::cout << "expose done. frameLength : " << frameLength << std::endl;
    } catch (...) {
      cerr << "can't read Cleanup Settings file " << fp << endl;
      TImageCache::instance()->clear(true);
      return -3;
    }
  }

  // Levels contiene i livelli e i rispettivi frames prsenti nell'xsheet.
  std::vector<std::pair<TXshSimpleLevel *, std::set<TFrameId>>> levels;

  /*- XsheetからCleanupするLevelのリストを得る -*/
  searchLevelsToCleanup(levels, scene->getXsheet(), selectedOnly);
  TSceneProperties *sprop   = scene->getProperties();
  CleanupParameters *params = scene->getProperties()->getCleanupParameters();
  for (int i = 0; i < (int)levels.size(); i++) {
    bool overwrite = overwriteAllOption;

    TXshSimpleLevel *xl = levels[i].first->getSimpleLevel();
    if (!xl) continue;

    /*- CLNファイルパスを取得 -*/
    TFilePath settingsFile =
        xl->getScannedPath().isEmpty() ? xl->getPath() : xl->getScannedPath();
    settingsFile =
        scene->decodeFilePath(settingsFile).withNoFrame().withType("cln");

    /*- CLNファイルがあればそれを用いる。無ければGlobal設定を読み込む -*/
    // TFilePath settingsFile =
    // scene->decodeFilePath(xl->getPath()).withNoFrame().withType("cln");
    if (TFileStatus(settingsFile).doesExist())
      loadSettings(settingsFile, params);
    else
      restoreGlobalSettings(params);

    TCleanupper::instance()->setParameters(params);

    bool lineProcessing = (params->m_lineProcessingMode != lpNone);
    TFilePath tmp       = params->getPath(scene);
    int ltype           = xl->getType();
    assert(ltype == TZP_XSHLEVEL || ltype == TZI_XSHLEVEL ||
           ltype == OVL_XSHLEVEL);

    // target path
    TFilePath targetPath = xl->getPath();
    if (ltype != TZP_XSHLEVEL) {
      if (lineProcessing)
        targetPath =
            targetPath
                .withParentDir(params->getPath(
                    scene))  // scene->getDefaultParentDir(TZP_XSHLEVEL))
                .withNoFrame()
                .withType("tlv");
      else
        targetPath = targetPath.withParentDir(params->getPath(
            scene));  // scene->getDefaultParentDir(TZP_XSHLEVEL));
    } else {
      targetPath = targetPath.withParentDir(params->getPath(
          scene));  // scene->getDefaultParentDir(TZP_XSHLEVEL));
    }

    /*- NoPaintを作る設定のとき、NoPaintの方だけにOverwriteするようにする -*/
    bool isReCleanup;
    isReCleanup = false;

    /*- 再Cleanupのときにパスを元に戻すために用いる -*/
    TFilePath originalLevelPath = TFilePath();

    TFilePath actualTargetPath = scene->decodeFilePath(targetPath);
    /*- すでにLevelがある場合 -*/
    if (TSystem::doesExistFileOrLevel(actualTargetPath) &&
        Preferences::instance()->isSaveUnpaintedInCleanupEnable() &&
        overwriteNoPaintOption) {
      overwrite         = true;
      originalLevelPath = scene->codeFilePath(targetPath);
      /*- パスを書き換え、再Cleanupのフラグを立てる -*/
      isReCleanup = true;
      /*- nopaintフォルダの作成 -*/
      TFilePath nopaintDir = targetPath.getParentDir() + "nopaint";
      if (!TFileStatus(nopaintDir).doesExist()) {
        try {
          TSystem::mkDir(nopaintDir);
        } catch (...) {
          return 0;
        }
      }
      /*- 保存先のパスをnopaintの方にする -*/
      targetPath =
          targetPath.getParentDir() + "nopaint\\" +
          TFilePath(targetPath.getName() + "_np." + targetPath.getType());
    }
    std::wcout << L"targetPath = " << targetPath.getWideString() << std::endl;

    if (lineProcessing) {
      if (ltype != TZP_XSHLEVEL) {
        xl->makeTlv(targetPath);
      } else if (targetPath != xl->getPath()) {
        xl->setPath(targetPath, false);
      }

      xl->setPalette(
          TCleanupper::instance()->createToonzPaletteFromCleanupPalette());
    } else if (!params->getPath(scene).isEmpty()) {
      xl->setScannedPath(xl->getPath());
      xl->setPath(scene->codeFilePath(targetPath), false);
    }

    std::set<TFrameId> fidsInXsheet = levels[i].second;
    assert(fidsInXsheet.size() > 0);

    xl->load();
    cleanupLevel(xl, fidsInXsheet, scene, overwrite, m_userLog);

    /*- Cleanup完了後、Nopaintをnopaintフォルダに保存する -*/
    if (Preferences::instance()->isSaveUnpaintedInCleanupEnable() &&
        !isReCleanup) /*-- 再Cleanupのときはnopaintを作らない --*/
    {
      /*- nopaintフォルダの作成 -*/
      TFilePath nopaintDir = actualTargetPath.getParentDir() + "nopaint";
      if (!TFileStatus(nopaintDir).doesExist()) {
        try {
          TSystem::mkDir(nopaintDir);
        } catch (...) {
          string err = "Can't make directory  " + nopaintDir.getName();
          m_userLog.error(err);
          cerr << "Can't make directory" << endl;
        }
      }

      // Se sono nell'ultimo frame del livello salvo il livello cleanuppato
      // in un file chiamato nome-unpainted (file di backup del cleanup).

      /*- 保存先 -*/
      TFilePath unpaintedLevelPath =
          actualTargetPath.getParentDir() + "nopaint\\" +
          TFilePath(targetPath.getName() + "_np." + targetPath.getType());

      if (TFileStatus(actualTargetPath).doesExist() &&
          (!TFileStatus(unpaintedLevelPath).doesExist() ||
           overwriteAllOption) /*- 全て上書きなら既存のNoPaintに上書きする -*/
          && xl) {
        /*- GUI上でCleanupするときと同様に、Cleanup結果をコピーして作る -*/
        TSystem::copyFile(unpaintedLevelPath, actualTargetPath);
        /*- パレットのコピー -*/
        TFilePath levelPalettePath =
            actualTargetPath.getParentDir() +
            TFilePath(actualTargetPath.getName() + ".tpl");
        TFilePath unpaintedLevelPalettePath =
            levelPalettePath.getParentDir() + "nopaint\\" +
            TFilePath(levelPalettePath.getName() + "_np." +
                      levelPalettePath.getType());
        TSystem::copyFile(unpaintedLevelPalettePath, levelPalettePath);
      }
    }
    /*--
       再Cleanupの場合はXsheet上のパスをオリジナルのもの(NoPaintでないもの)に戻す
       --*/
    else if (isReCleanup) {
      xl->setPath(originalLevelPath);
    }
  }

  /*- CleanupParamをGrobalに戻す -*/
  restoreGlobalSettings(params);

  /*- CleanupSettingsからCleanupを行った場合はシーンの保存は行わない -*/
  if (sourceFileIsCleanupSetting) {
    std::cout << "Cleanup from Setting file completed" << std::endl;
    return 0;
  }

  wstring wInfo  = L"updating scene " + scene->getSceneName();
  QString qInfo_ = QString::fromStdWString(wInfo);
  string info    = qInfo_.toStdString();
  m_userLog.info(info);
  cout << "updating scene " << scene->getSceneName() << endl;
  /*- シーンを上書き保存 -*/
  try {
    scene->save(fp);
    TImageCache::instance()->clear(true);
  } catch (...) {
    wstring wErr = L"Can't save scene  " + fp.getWideName();
    QString qErr = QString::fromStdWString(wErr);
    string err   = qErr.toStdString();
    m_userLog.error(err);
    cerr << "Can't save scene" << endl;
    TImageCache::instance()->clear(true);
  }

  DVGui::info("Cleanup Done.");
  return 0;
}
//------------------------------------------------------------------------
