

#include "filmstripselection.h"

// Tnz6 includes
#include "menubarcommandids.h"
#include "filmstripcommand.h"
#include "addfilmstripframespopup.h"
#include "renumberpopup.h"
#include "tapp.h"

// TnzTools includes
#include "tools/rasterselection.h"
#include "tools/strokeselection.h"
#include "tools/toolhandle.h"
#include "tools/tool.h"

// TnzQt includes
#include "toonzqt/strokesdata.h"
#include "toonzqt/gutil.h"

// TnzLib includes
#include "toonz/txshlevelhandle.h"
#include "toonz/tframehandle.h"
#include "toonz/txshlevel.h"
#include "toonz/txshsimplelevel.h"
#include "toonz/txshleveltypes.h"

// TnzCore includes
#include "tundo.h"

// Qt includes
#include <QApplication>
#include <QClipboard>

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

TFilmstripSelection::TFilmstripSelection()
    : m_inbetweenRange(TFrameId(1), TFrameId(0)) {}

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

TFilmstripSelection::~TFilmstripSelection() {}

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

void TFilmstripSelection::enableCommands() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (!sl) return;

  enableCommand(this, MI_SelectAll, &TFilmstripSelection::selectAll);
  enableCommand(this, MI_InvertSelection,
                &TFilmstripSelection::invertSelection);

  int type       = sl->getType();
  TFilePath path = sl->getPath();

  bool doEnable =
      (type == PLI_XSHLEVEL || type == TZP_XSHLEVEL || type == MESH_XSHLEVEL ||
       (type == OVL_XSHLEVEL && path.getType() != "psd"));

  TRasterImageP ri = (TRasterImageP)sl->getSimpleLevel()->getFrame(
      sl->getSimpleLevel()->getFirstFid(), false);

  bool isNotEditableFullColorLevel =
      (type == OVL_XSHLEVEL && path.getFrame() == TFrameId::NO_FRAME ||
       (ri && ri->isScanBW()));

  if (doEnable && !isNotEditableFullColorLevel) {
    enableCommand(this, MI_Cut, &TFilmstripSelection::cutFrames);
    enableCommand(this, MI_Copy, &TFilmstripSelection::copyFrames);
    enableCommand(this, MI_Paste, &TFilmstripSelection::pasteFrames);
    enableCommand(this, MI_PasteInto, &TFilmstripSelection::pasteInto);
    enableCommand(this, MI_Clear, &TFilmstripSelection::deleteFrames);
    enableCommand(this, MI_Reverse, &TFilmstripSelection::reverseFrames);
    enableCommand(this, MI_Swing, &TFilmstripSelection::swingFrames);
    enableCommand(this, MI_Step2, &TFilmstripSelection::stepFrames, 2);
    enableCommand(this, MI_Step3, &TFilmstripSelection::stepFrames, 3);
    enableCommand(this, MI_Step4, &TFilmstripSelection::stepFrames, 4);
    enableCommand(this, MI_Each2, &TFilmstripSelection::eachFrames, 2);
    enableCommand(this, MI_Each3, &TFilmstripSelection::eachFrames, 3);
    enableCommand(this, MI_Each4, &TFilmstripSelection::eachFrames, 4);
    enableCommand(this, MI_Duplicate, &TFilmstripSelection::duplicateFrames);

    if (type != MESH_XSHLEVEL) {
      enableCommand(this, MI_Insert, &TFilmstripSelection::insertEmptyFrames);
      enableCommand(this, MI_MergeFrames, &TFilmstripSelection::mergeFrames);

      enableCommand(this, MI_AddFrames, &TFilmstripSelection::addFrames);
    }
  } else if (isNotEditableFullColorLevel)
    enableCommand(this, MI_Copy, &TFilmstripSelection::copyFrames);

  enableCommand(this, MI_ExposeResource, &TFilmstripSelection::exposeFrames);

  if (doEnable && !isNotEditableFullColorLevel)
    enableCommand(this, MI_Renumber, &TFilmstripSelection::renumberFrames);
}

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

bool TFilmstripSelection::isEmpty() const { return m_selectedFrames.empty(); }

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

void TFilmstripSelection::updateInbetweenRange() {
  // ibrange = (la prima) sequenza di almeno tre frame selezionati consecutivi
  m_inbetweenRange = std::make_pair(TFrameId(1), TFrameId(0));
  if (m_selectedFrames.size() < 3)
    return;  // ci vogliono almeno tre frames selezionati
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (sl) {
    std::vector<TFrameId> fids;
    sl->getFids(fids);
    int m = (int)fids.size();
    int i, j = -1;
    for (i = 0; i < (int)fids.size(); i++)
      if (isSelected(fids[i])) {
        if (j < 0) j = i;
      } else {
        if (j >= 0 && i - j >= 3) break;
        j = -1;
      }
    if (j >= 0 && i - j >= 3) {
      m_inbetweenRange = std::make_pair(fids[j], fids[i - 1]);
    }
  }
}

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

void TFilmstripSelection::select(const TFrameId &fid, bool selected) {
  TApp *app = TApp::instance();

  if (selected)
    m_selectedFrames.insert(fid);
  else
    m_selectedFrames.erase(fid);

  updateInbetweenRange();

  TTool *tool = app->getCurrentTool()->getTool();
  if (tool) tool->setSelectedFrames(m_selectedFrames);

  TXshSimpleLevel *sl = app->getCurrentLevel()->getSimpleLevel();
  bool rasterLevel    = sl->getType() == TZP_XSHLEVEL ||
                     sl->getType() == OVL_XSHLEVEL ||
                     sl->getType() == TZI_XSHLEVEL;

  CommandManager::instance()->enable(MI_CanvasSize, rasterLevel);
}

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

bool TFilmstripSelection::isSelected(const TFrameId &fid) const {
  return m_selectedFrames.count(fid) > 0;
}

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

void TFilmstripSelection::selectNone() {
  m_selectedFrames.clear();
  updateInbetweenRange();
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  CommandManager::instance()->enable(MI_CanvasSize, false);
}

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

void TFilmstripSelection::selectAll() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (!sl) return;
  std::vector<TFrameId> fids;
  sl->getFids(fids);
  m_selectedFrames.clear();
  m_selectedFrames.insert(fids.begin(), fids.end());
  updateInbetweenRange();
  TTool *tool = TApp::instance()->getCurrentTool()->getTool();
  tool->setSelectedFrames(m_selectedFrames);
  bool rasterLevel = sl->getType() == TZP_XSHLEVEL ||
                     sl->getType() == OVL_XSHLEVEL ||
                     sl->getType() == TZI_XSHLEVEL;
  CommandManager::instance()->enable(MI_CanvasSize, rasterLevel);
  notifyView();
}

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

void TFilmstripSelection::invertSelection() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (!sl) return;
  std::vector<TFrameId> fids;
  sl->getFids(fids);
  std::set<TFrameId> oldSelectedFrames = m_selectedFrames;
  m_selectedFrames.clear();
  std::vector<TFrameId>::iterator it;
  for (it = fids.begin(); it != fids.end(); it++) {
    if (oldSelectedFrames.find(*it) != oldSelectedFrames.end()) continue;
    m_selectedFrames.insert(*it);
  }
  updateInbetweenRange();
  TTool *tool = TApp::instance()->getCurrentTool()->getTool();
  tool->setSelectedFrames(m_selectedFrames);
  if (sl->getType() == TZP_XSHLEVEL || sl->getType() == OVL_XSHLEVEL ||
      sl->getType() == TZI_XSHLEVEL)
    CommandManager::instance()->enable(MI_CanvasSize, true);
  notifyView();
}

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

void TFilmstripSelection::addFrames() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (!sl) return;
  AddFilmstripFramesPopup popup;
  int ret = popup.exec();
  if (ret == 0) return;

  int startFrame = 1;
  int endFrame   = 1;
  int stepFrame  = 1;

  popup.getParameters(startFrame, endFrame, stepFrame);
  FilmstripCmd::addFrames(sl, startFrame, endFrame, stepFrame);
}

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

void TFilmstripSelection::copyFrames() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (sl) FilmstripCmd::copy(sl, m_selectedFrames);
}

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

void TFilmstripSelection::cutFrames() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (sl) {
    FilmstripCmd::cut(sl, m_selectedFrames);
    selectNone();
    TApp::instance()->getCurrentFrame()->setFid(sl->getFirstFid());
    select(sl->getFirstFid());
  }
}

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

void TFilmstripSelection::pasteFrames() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (!sl) return;

  std::set<TFrameId> fids;
  if (m_selectedFrames.empty()) {
    if (sl->isSubsequence()) return;
    fids.insert(TApp::instance()->getCurrentFrame()->getFid());
  } else
    fids = m_selectedFrames;

  FilmstripCmd::paste(sl, fids);
}

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

void TFilmstripSelection::mergeFrames() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (sl) FilmstripCmd::merge(sl, m_selectedFrames);
}

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

void TFilmstripSelection::pasteInto() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (sl) FilmstripCmd::pasteInto(sl, m_selectedFrames);
}

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

void TFilmstripSelection::deleteFrames() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (sl) FilmstripCmd::clear(sl, m_selectedFrames);
  updateInbetweenRange();
}

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

void TFilmstripSelection::insertEmptyFrames() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (sl) FilmstripCmd::insert(sl, m_selectedFrames, true);
}

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

void TFilmstripSelection::reverseFrames() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (sl) FilmstripCmd::reverse(sl, m_selectedFrames);
}

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

void TFilmstripSelection::swingFrames() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (sl) FilmstripCmd::swing(sl, m_selectedFrames);
}

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

void TFilmstripSelection::stepFrames(int step) {
  if (step < 2) return;
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (sl) FilmstripCmd::step(sl, m_selectedFrames, step);
  updateInbetweenRange();
}

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

void TFilmstripSelection::eachFrames(int each) {
  if (each < 2) return;
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (sl) FilmstripCmd::each(sl, m_selectedFrames, each);
  updateInbetweenRange();
}

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

void TFilmstripSelection::duplicateFrames() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (sl) FilmstripCmd::duplicate(sl, m_selectedFrames, true);
}

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

void TFilmstripSelection::exposeFrames() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (sl) FilmstripCmd::moveToScene(sl, m_selectedFrames);
}

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

void TFilmstripSelection::renumberFrames() {
  TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
  if (!sl) return;
  if (m_selectedFrames.empty()) return;
  TFrameId fid = *m_selectedFrames.begin();
  RenumberPopup popup;
  popup.setValues(fid.getNumber(), 1);
  int ret = popup.exec();
  if (ret != QDialog::Accepted) return;
  int startFrame = 0, stepFrame = 0;
  popup.getValues(startFrame, stepFrame);
  if (startFrame < 1 || stepFrame < 1) {
    DVGui::error(("Bad renumber values"));
    return;
  }
  FilmstripCmd::renumber(sl, m_selectedFrames, startFrame, stepFrame);
  updateInbetweenRange();
}
