// -*- C++ -*-
//
// Package:     Core
// Class  :     FWParameterSetterBase
//
// Implementation:
//     <Notes on implementation>
//
// Original Author:  Chris Jones
//         Created:  Fri Mar  7 14:16:20 EST 2008
//

// system include files
#include "FWCore/Reflection/interface/TypeWithDict.h"
#include "FWCore/Reflection/interface/ObjectWithDict.h"

#include <cassert>
#include <iostream>
#include <functional>

// user include files
#include "FWCore/Utilities/interface/TypeID.h"

#include "Fireworks/Core/interface/FWParameterSetterBase.h"
#include "Fireworks/Core/interface/FWParameterBase.h"
#include "Fireworks/Core/interface/FWParameterSetterEditorBase.h"
#include "Fireworks/Core/interface/fwLog.h"

//
// constants, enums and typedefs
//

//
// static data member definitions
//

//
// constructors and destructor
//
FWParameterSetterBase::FWParameterSetterBase() : m_frame(nullptr) {}

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

FWParameterSetterBase::~FWParameterSetterBase() {}

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

//
// member functions
//

void FWParameterSetterBase::attach(FWParameterBase* iBase, FWParameterSetterEditorBase* iFrame) {
  m_frame = iFrame;
  attach(iBase);
}

//
// const member functions
//

void FWParameterSetterBase::update() const {
  if (m_frame != nullptr)
    m_frame->updateEditor();
}

//
// static member functions
//

std::shared_ptr<FWParameterSetterBase> FWParameterSetterBase::makeSetterFor(FWParameterBase* iParam) {
  static std::map<edm::TypeID, edm::TypeWithDict> s_paramToSetterMap;
  edm::TypeID paramType(typeid(*iParam));
  std::map<edm::TypeID, edm::TypeWithDict>::iterator itFind = s_paramToSetterMap.find(paramType);
  if (itFind == s_paramToSetterMap.end()) {
    edm::TypeWithDict paramClass(typeid(*iParam));
    if (paramClass == edm::TypeWithDict()) {
      fwLog(fwlog::kError) << " the type " << typeid(*iParam).name() << " is not known to Root" << std::endl;
    }
    assert(paramClass != edm::TypeWithDict());

    //the corresponding setter has the same name but with 'Setter' at the end
    std::string name = paramClass.name();
    // FIXME: there was a convention between parameter class names and associated
    //        setters. The following works around the problem introduced by
    //        the generic parameter class but it is clear that a better
    //        way of doing the binding is required. Notice that there are only 5
    //        different type of FW*Parameter.
    if (name == "FWGenericParameter<bool>")
      name = "FWBoolParameterSetter";
    else if (name == "FWGenericParameterWithRange<double>")
      name = "FWDoubleParameterSetter";
    else if (name == "FWGenericParameterWithRange<long int>")
      name = "FWLongParameterSetter";
    else if (name == "FWGenericParameterWithRange<long>")
      name = "FWLongParameterSetter";
    else if (paramClass.friendlyClassName() == "StringFWGenericParameter")
      name = "FWStringParameterSetter";
    else {
      name += "Setter";
      fwLog(fwlog::kWarning) << "can't find setter type for " << iParam->name() << ", guessing to " << name << "\n";
    }
    edm::TypeWithDict setterClass(edm::TypeWithDict::byName(name));

    if (setterClass == edm::TypeWithDict()) {
      fwLog(fwlog::kError) << " the type " << name << " has no dictionary" << std::endl;
    }
    assert(setterClass != edm::TypeWithDict());

    s_paramToSetterMap[paramType] = setterClass;
    itFind = s_paramToSetterMap.find(paramType);
  }
  //create the instance we want
  //NOTE: 'construct' will use 'malloc' to allocate the memory if the object is not of class type.
  // This means the object cannot be deleted using 'delete'!  So we must call destruct on the object.
  edm::ObjectWithDict setterObj = itFind->second.construct();

  //make it into the base class
  FWParameterSetterBase* p = static_cast<FWParameterSetterBase*>(setterObj.address());
  //Make a shared pointer to the base class that uses a destructor for the derived class, in order to match the above construct call.
  std::shared_ptr<FWParameterSetterBase> ptr(
      p, std::bind(&edm::TypeWithDict::destruct, itFind->second, setterObj.address(), true));
  return ptr;
}

/* Virtual function which sets widgets enabled state.*/
void FWParameterSetterBase::setEnabled(bool) {}
