// -*- C++ -*-
//
// Package:     Core
// Class  :     FWModelContextMenuHandler
//
// Implementation:
//     <Notes on implementation>
//
// Original Author:  Chris Jones
//         Created:  Tue Sep 22 13:26:04 CDT 2009
//

// system include files
#include <cassert>
#include <iostream>
#include "TGMenu.h"
#include "KeySymbols.h"

#include "FWCore/Reflection/interface/FunctionWithDict.h"
#include "FWCore/Reflection/interface/ObjectWithDict.h"
#include "FWCore/Reflection/interface/TypeWithDict.h"
#include "TClass.h"
#include "TError.h"

// user include files
#include "Fireworks/Core/src/FWModelContextMenuHandler.h"
#include "Fireworks/Core/interface/FWSelectionManager.h"
#include "Fireworks/Core/interface/FWDetailViewManager.h"
#include "Fireworks/Core/interface/FWColorManager.h"
#include "Fireworks/Core/src/FWColorSelect.h"
#include "Fireworks/Core/src/FWPopupMenu.cc"
#include "Fireworks/Core/interface/FWEventItem.h"
#include "Fireworks/Core/interface/FWGUIManager.h"
#include "Fireworks/Core/interface/FWViewContextMenuHandlerBase.h"

//
// constants, enums and typedefs
//
enum MenuOptions {
  kSetVisibleMO,
  kSetColorMO,
  //   kPrint,
  kOpenDetailViewMO,
  kAfterOpenDetailViewMO = 10,
  kOpen3DRegion,
  kOpenObjectControllerMO = 100,
  kOpenCollectionControllerMO,
  kViewOptionsMO = 1000,
  kLastOfMO
};

//
// static data member definitions
//
static const char* const kOpenDetailView = "Open Detailed View ...";

//
// constructors and destructor
//
FWModelContextMenuHandler::FWModelContextMenuHandler(FWSelectionManager* iSM,
                                                     FWDetailViewManager* iDVM,
                                                     FWColorManager* iCM,
                                                     FWGUIManager* iGM)
    : m_modelPopup(nullptr),
      m_colorPopup(nullptr),
      m_selectionManager(iSM),
      m_detailViewManager(iDVM),
      m_colorManager(iCM),
      m_guiManager(iGM),
      m_seperator(nullptr),
      m_viewSeperator(nullptr),
      m_afterViewSeperator(nullptr),
      m_x(0),
      m_y(0),
      m_nDetailViewEntries(0),
      m_nViewEntries(0),
      m_viewHander(nullptr) {}

// FWModelContextMenuHandler::FWModelContextMenuHandler(const FWModelContextMenuHandler& rhs)
// {
//    // do actual copying here;
// }

FWModelContextMenuHandler::~FWModelContextMenuHandler() { delete m_modelPopup; }

//
// assignment operators
//
// const FWModelContextMenuHandler& FWModelContextMenuHandler::operator=(const FWModelContextMenuHandler& rhs)
// {
//   //An exception safe implementation is
//   FWModelContextMenuHandler temp(rhs);
//   swap(rhs);
//
//   return *this;
// }

//
// member functions
//
#include "TROOT.h"
namespace {
  class change_visibility {
  public:
    change_visibility(bool iIsVisible) : m_isVisible(iIsVisible) {}
    void operator()(const FWModelId& iID) const {
      FWDisplayProperties p = iID.item()->modelInfo(iID.index()).displayProperties();
      p.setIsVisible(m_isVisible);
      iID.item()->setDisplayProperties(iID.index(), p);
    }
    bool m_isVisible;
  };
}  // namespace
void FWModelContextMenuHandler::chosenItem(Int_t iChoice) {
  assert(!m_selectionManager->selected().empty());
  switch (iChoice) {
    case kSetVisibleMO: {
      FWModelId id = *(m_selectionManager->selected().begin());
      for_each(m_selectionManager->selected().begin(),
               m_selectionManager->selected().end(),
               change_visibility(!id.item()->modelInfo(id.index()).displayProperties().isVisible()));
      break;
    }
    case kSetColorMO: {
      FWModelId id = *(m_selectionManager->selected().begin());
      createColorPopup();
      m_colorPopup->SetName("Selected");
      std::vector<Color_t> colors;
      m_colorManager->fillLimitedColors(colors);
      m_colorPopup->ResetColors(colors, m_colorManager->backgroundColorIndex() == FWColorManager::kBlackIndex);
      m_colorPopup->SetSelection(id.item()->modelInfo(id.index()).displayProperties().color());
      m_colorPopup->PlacePopup(m_x, m_y, m_colorPopup->GetDefaultWidth(), m_colorPopup->GetDefaultHeight());
      break;
    } /*
      case kPrint:
      {
         FWModelId id = *(m_selectionManager->selected().begin());
         edm::TypeWithDict rtype(edm::TypeWithDict::byName(id.item()->modelType()->GetName()));
         edm::ObjectWithDict o(rtype, const_cast<void *>(id.item()->modelData(id.index())));

         // void* xx = &std::cout;
         //const std::vector<void*> j(1, xx);
         //edm::TypeMemberQuery inh =  edm::TypeMemberQuery::InheritedAlso;
         //edm::FunctionWithDict m = rtype.functionMemberByName("print",edm::TypeWithDict(edm::TypeWithDict::byName("void (std::ostream&)"), edm::TypeModifiers::Const), edm::TypeModifiers::NoMod , inh))
         //m.Invoke(o, 0, j);

         const char* cmd  = Form("FWGUIManager::OStream() << *(%s*)%p ;",  id.item()->modelType()->GetName(), (void*)id.item()->modelData(id.index()));
         //const char* cmd  = Form("*((std::ostream*)%p) << (%s*)%p ;", (void*)(&std::cout), id.item()->modelType()->GetName(), (void*)id.item()->modelData(id.index()));
         std::cout << cmd << std::endl;
         gROOT->ProcessLine(cmd);


         break;
         }*/
    case kOpenObjectControllerMO: {
      m_guiManager->showModelPopup();
      break;
    }
    case kOpenCollectionControllerMO: {
      m_guiManager->showEDIFrame();
      break;
    }
    case kOpen3DRegion: {
      m_guiManager->open3DRegion();
      break;
    }
    case kOpenDetailViewMO:
    case kViewOptionsMO:
    default: {
      if (iChoice >= kViewOptionsMO) {
        assert(nullptr != m_viewHander);
        m_viewHander->select(iChoice - kViewOptionsMO, *(m_selectionManager->selected().begin()), m_x, m_y);
      } else {
        assert(iChoice < kOpenObjectControllerMO);
        assert(m_selectionManager->selected().size() == 1);
        std::vector<std::string> viewChoices =
            m_detailViewManager->detailViewsFor(*(m_selectionManager->selected().begin()));
        assert(!viewChoices.empty());
        m_detailViewManager->openDetailViewFor(*(m_selectionManager->selected().begin()),
                                               viewChoices[iChoice - kOpenDetailViewMO]);
      }
      break;
    } break;
  }
}

void FWModelContextMenuHandler::colorChangeRequested(Color_t color) {
  for (std::set<FWModelId>::const_iterator it = m_selectionManager->selected().begin(),
                                           itEnd = m_selectionManager->selected().end();
       it != itEnd;
       ++it) {
    FWDisplayProperties changeProperties = it->item()->modelInfo(it->index()).displayProperties();
    changeProperties.setColor(color);
    it->item()->setDisplayProperties(it->index(), changeProperties);
  }
}

void FWModelContextMenuHandler::addViewEntry(const char* iEntryName, int iEntryIndex, bool enabled) {
  if (!m_viewSeperator) {
    m_modelPopup->AddSeparator(m_afterViewSeperator);
    m_viewSeperator = dynamic_cast<TGMenuEntry*>(m_modelPopup->GetListOfEntries()->Before(m_afterViewSeperator));
    assert(nullptr != m_viewSeperator);
  }

  if (static_cast<int>(m_nViewEntries) > iEntryIndex) {
    m_modelPopup->GetEntry(iEntryIndex + kViewOptionsMO)->GetLabel()->SetString(iEntryName);
    if (enabled)
      m_modelPopup->EnableEntry(iEntryIndex + kViewOptionsMO);
    else
      m_modelPopup->DisableEntry(iEntryIndex + kViewOptionsMO);

  } else {
    assert(static_cast<int>(m_nViewEntries) == iEntryIndex);
    m_modelPopup->AddEntry(iEntryName, kViewOptionsMO + iEntryIndex, nullptr, nullptr, m_viewSeperator);

    if (enabled)
      m_modelPopup->EnableEntry(kViewOptionsMO + iEntryIndex);
    else
      m_modelPopup->DisableEntry(kViewOptionsMO + iEntryIndex);

    ++m_nViewEntries;
  }
}
//
// const member functions
//
void FWModelContextMenuHandler::showSelectedModelContext(Int_t iX,
                                                         Int_t iY,
                                                         FWViewContextMenuHandlerBase* iHandler) const {
  m_viewHander = iHandler;
  assert(!m_selectionManager->selected().empty());
  createModelContext();

  //setup the menu based on this object
  FWModelId id = *(m_selectionManager->selected().begin());
  if (id.item()->modelInfo(id.index()).displayProperties().isVisible()) {
    m_modelPopup->CheckEntry(kSetVisibleMO);
  } else {
    m_modelPopup->UnCheckEntry(kSetVisibleMO);
  }

  if (m_selectionManager->selected().size() == 1) {
    {
      //edm::TypeWithDict rtype(edm::TypeWithDict::byName(id.item()->modelType()->GetName()));
      //edm::ObjectWithDict o(rtype, const_cast<void *>(id.item()->modelData(id.index())));
      //edm::TypeMemberQuery inh =  edm::TypeMemberQuery::InheritedAlso;
      //if ( rtype.functionMemberByName("print",edm::TypeWithDict(edm::TypeWithDict::byName("void (std::ostream&)"), Long_t(kIsConstant)), 0, inh))
      //{
      //m_modelPopup->EnableEntry(kPrint);
      // std::cout <<  "Enable " <<std::endl;
      //}
      //else
      //{
      m_modelPopup->DisableEntry(kPrint);
      // printf("Disable print \n");
      //}
    }
    //add the detail view entries
    std::vector<std::string> viewChoices =
        m_detailViewManager->detailViewsFor(*(m_selectionManager->selected().begin()));
    if (!viewChoices.empty()) {
      if (m_nDetailViewEntries < viewChoices.size()) {
        for (unsigned int index = m_nDetailViewEntries; index != viewChoices.size(); ++index) {
          m_modelPopup->AddEntry(kOpenDetailView, kOpenDetailViewMO + index, nullptr, nullptr, m_seperator);
        }
        m_nDetailViewEntries = viewChoices.size();
      }
      const std::string kStart("Open ");
      const std::string kEnd(" Detail View ...");
      for (unsigned int index = 0; index != viewChoices.size(); ++index) {
        m_modelPopup->EnableEntry(index + kOpenDetailViewMO);  // need to call this to make it visible
        if (viewChoices[index][0] != '!') {
          m_modelPopup->GetEntry(index + kOpenDetailViewMO)
              ->GetLabel()
              ->SetString((kStart + viewChoices[index] + kEnd).c_str());
        } else {
          m_modelPopup->GetEntry(index + kOpenDetailViewMO)
              ->GetLabel()
              ->SetString((kStart + &viewChoices[index][1] + kEnd).c_str());
          m_modelPopup->DisableEntry(index + kOpenDetailViewMO);
        }
      }
      for (unsigned int i = viewChoices.size(); i < m_nDetailViewEntries; ++i) {
        m_modelPopup->HideEntry(kOpenDetailViewMO + i);
      }

    } else {
      for (unsigned int i = 0; i < m_nDetailViewEntries; ++i) {
        m_modelPopup->HideEntry(kOpenDetailViewMO + i);
      }
    }
  } else {
    for (unsigned int i = 0; i < m_nDetailViewEntries; ++i) {
      m_modelPopup->HideEntry(kOpenDetailViewMO + i);
    }
  }
  //add necessary entries from the view
  m_modelPopup->DeleteEntry(m_viewSeperator);
  m_viewSeperator = nullptr;

  for (unsigned int i = 0; i < m_nViewEntries; ++i) {
    m_modelPopup->HideEntry(kViewOptionsMO + i);
  }
  if (m_viewHander) {
    m_viewHander->addTo(const_cast<FWModelContextMenuHandler&>(*this), *(m_selectionManager->selected().begin()));
  }

  m_x = iX;
  m_y = iY;
  m_modelPopup->PlaceMenu(iX, iY, false, true);
}

void FWModelContextMenuHandler::createModelContext() const {
  if (nullptr == m_modelPopup) {
    m_modelPopup = new FWPopupMenu();

    m_modelPopup->AddEntry("Set Visible", kSetVisibleMO);
    m_modelPopup->AddEntry("Set Color ...", kSetColorMO);
    //      m_modelPopup->AddEntry("Print ...",kPrint);
    m_modelPopup->AddEntry(kOpenDetailView, kOpenDetailViewMO);
    m_nDetailViewEntries = 1;
    m_seperator = dynamic_cast<TGMenuEntry*>(m_modelPopup->GetListOfEntries()->Last());
    assert(nullptr != m_seperator);
    m_modelPopup->AddEntry("Open 3D Region ...", kOpen3DRegion);
    m_modelPopup->AddSeparator();
    m_modelPopup->AddEntry("Open Object Controller ...", kOpenObjectControllerMO);
    m_afterViewSeperator = dynamic_cast<TGMenuEntry*>(m_modelPopup->GetListOfEntries()->Last());
    m_modelPopup->AddEntry("Open Collection Controller ...", kOpenCollectionControllerMO);

    m_modelPopup->Connect("Activated(Int_t)",
                          "FWModelContextMenuHandler",
                          const_cast<FWModelContextMenuHandler*>(this),
                          "chosenItem(Int_t)");
  }
}

void FWModelContextMenuHandler::createColorPopup() const {
  if (nullptr == m_colorPopup) {
    std::vector<Color_t> colors;
    m_colorManager->fillLimitedColors(colors);

    m_colorPopup = new FWColorPopup(gClient->GetDefaultRoot(), colors.front());
    m_colorPopup->InitContent("", colors);
    m_colorPopup->Connect("ColorSelected(Color_t)",
                          "FWModelContextMenuHandler",
                          const_cast<FWModelContextMenuHandler*>(this),
                          "colorChangeRequested(Color_t)");
  }
}

//
// static member functions
//

ClassImp(FWModelContextMenuHandler);
