/* BEGIN software license
 *
 * MsXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2026 Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This file is part of the MsXpertSuite project.
 *
 * The MsXpertSuite project is the successor of the massXpert project. This
 * project now includes various independent modules:
 *
 * - MassXpert, model polymer chemistries and simulate mass spectrometric data;
 * - MineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * END software license
 */

/////////////////////// stdlib includes
#include <memory>


/////////////////////// Qt includes
#include <QDebug>
#include <QFile>
#include <QSvgRenderer>


/////////////////////// pappsomspp includes


/////////////////////// libXpertMass includes
#include <MsXpS/libXpertMassCore/PolChemDef.hpp>
#include <MsXpS/libXpertMassCore/Monomer.hpp>
#include <MsXpS/libXpertMassCore/Polymer.hpp>


/////////////////////// libXpertMassGui includes


/////////////////////// Local includes
#include "PolChemDefRendering.hpp"
#include "Modification_default_vignette.hpp"
#include "Cross_link_default_vignette.hpp"


namespace MsXpS
{
namespace MassXpert
{


/*!
\class MsXpS::libXpertMassCore::PolChemDefRendering
\inmodule MassXpert
\ingroup MassXpertGraphics
\inheaderfile PolChemDefRendering.hpp

\brief The PolChemDefRendering class ensures that chemical entities have a
proper graphical rendering using shared renderers.
*/

/*!
\variable OligomerPair::mcsp_polChemDef

\brief The polymer chemistry definition for which the chemical entities are
being rendered graphically.
*/

/*!
\variable OligomerPair::m_chemEntVignetteRenderers

\brief The container of QSvgRenderer instances. The items are shared pointers.

The container is actually a map relating a string (a string identifying the
chemical entity, like a Monomer code, a Modif or CrossLink name, for example)
and the shared pointer to a const QSvgRenderer.
*/

/*!
\brief Constructs a polymer chemistry definition rendering instance.
*/
PolChemDefRendering::PolChemDefRendering()
{
}

/*!
\brief Constructs a PolChemDefRendering polymer chemistry definition rendering
instance initializing the member PolChemDef with \a pol_chem_def_csp.

\a pol_chem_def_csp cannot be nullptr.
*/
PolChemDefRendering::PolChemDefRendering(
  libXpertMassCore::PolChemDefCstSPtr pol_chem_def_csp)
  : mcsp_polChemDef(pol_chem_def_csp)
{
  if(mcsp_polChemDef == nullptr)
    qFatal() << "Programming error. Pointer cannot be nullptr.";
}

/*!
\brief Destructs this PolChemDefRendering instance.
*/
PolChemDefRendering::~PolChemDefRendering()
{
}

/*!
\brief Sets the polymer chemistry definition to \a pol_chem_def_csp.

\a pol_chem_def_csp cannot be nullptr.
*/
void
PolChemDefRendering::setPolChemDefCstSPtr(
  libXpertMassCore::PolChemDefCstSPtr pol_chem_def_csp)
{
  if(mcsp_polChemDef == nullptr)
    qFatal() << "Programming error. Pointer cannot be nullptr.";
  mcsp_polChemDef = pol_chem_def_csp;
}


/*!
\brief Returns the polymer chemistry definition.
*/
const libXpertMassCore::PolChemDefCstSPtr
PolChemDefRendering::getPolChemDefCstSPtr() const
{
  return mcsp_polChemDef;
}


/*!
\brief Returns a const reference to the container of QSvgRenderer instances.
*/
const std::map<QString, QSvgRendererSPtr> &
PolChemDefRendering::getChemEntVignettesRenderersCstRef() const
{
  return m_chemEntVignetteRenderers;
}

/*!
\brief Returns a reference to the container of QSvgRenderer instances.
*/
std::map<QString, QSvgRendererSPtr> &
PolChemDefRendering::getChemEntVignettesRenderersRef()
{
  return m_chemEntVignetteRenderers;
}

const std::vector<MonomerSpec> &
PolChemDefRendering::getMonomerSpecsCstRef() const
{
  return m_monomerSpecs;
}

std::vector<MonomerSpec> &
PolChemDefRendering::getMonomerSpecsRef()
{
  return m_monomerSpecs;
}

MonomerSpec
PolChemDefRendering::getMonomerSpec(const QString &code)
{
  std::vector<MonomerSpec>::const_iterator the_iterator_cst =
    std::find_if(m_monomerSpecs.cbegin(),
                 m_monomerSpecs.cend(),
                 [code](const MonomerSpec &monomer_spec) {
                   return monomer_spec.getCode() == code;
                 });

  if(the_iterator_cst == m_monomerSpecs.cend())
    return MonomerSpec();

  return *the_iterator_cst;
}


const std::vector<ModifSpec> &
PolChemDefRendering::getModifSpecsCstRef() const
{
  return m_modifSpecs;
}

std::vector<ModifSpec> &
PolChemDefRendering::getModifSpecsRef()
{
  return m_modifSpecs;
}

ModifSpec
PolChemDefRendering::getModifSpec(const QString &name)
{
  std::vector<ModifSpec>::const_iterator the_iterator_cst =
    std::find_if(m_modifSpecs.cbegin(),
                 m_modifSpecs.cend(),
                 [name](const ModifSpec &modif_spec) {
                   return modif_spec.getName() == name;
                 });

  if(the_iterator_cst == m_modifSpecs.cend())
    return ModifSpec();

  return *the_iterator_cst;
}


const std::vector<CrossLinkerSpec> &
PolChemDefRendering::getCrossLinkerSpecsCstRef() const
{
  return m_crossLinkerSpecs;
}

std::vector<CrossLinkerSpec> &
PolChemDefRendering::getCrossLinkerSpecsRef()
{
  return m_crossLinkerSpecs;
}

CrossLinkerSpec
PolChemDefRendering::getCrossLinkerSpec(const QString &name)
{
  std::vector<CrossLinkerSpec>::const_iterator the_iterator_cst =
    std::find_if(m_crossLinkerSpecs.cbegin(),
                 m_crossLinkerSpecs.cend(),
                 [name](const CrossLinkerSpec &cross_linker_spec) {
                   return cross_linker_spec.getName() == name;
                 });

  if(the_iterator_cst == m_crossLinkerSpecs.cend())
    return CrossLinkerSpec();

  return *the_iterator_cst;
}


/*!
\brief Returns the QSvgRenderer instance associated to \a key.

For a Monomer, the key is the Monomer instance's code,  for a Modif or a
CrossLinker,  the key is these instances' name.

If no QSvgRenderer instance is found, nullptr is returned.
*/
QSvgRendererSPtr
PolChemDefRendering::chemEntVignetteRenderer(const QString &key) const
{
  std::map<QString, QSvgRendererSPtr>::const_iterator the_iterator_cst =
    std::find_if(m_chemEntVignetteRenderers.cbegin(),
                 m_chemEntVignetteRenderers.cend(),
                 [key](const std::pair<QString, QSvgRendererSPtr> &pair) {
                   return key == pair.first;
                 });

  if(the_iterator_cst != m_chemEntVignetteRenderers.cend())
    return (*the_iterator_cst).second;

  return nullptr;
}


/*!
\brief Returns a newly allocated QSvgRenderer for an SVG file located
at \a file_path, and with parent QSvgRenderer \a parent.

If the renderer has no parent, \a parent can be nullptr.
*/
QSvgRendererSPtr
PolChemDefRendering::newChemEntVignetteRendererFromSvgFile(
  const QString &file_path, QObject *parent) const
{
  return std::make_shared<QSvgRenderer>(file_path, parent);
}

/*!
\brief Allocates a QSvgRenderer for a Monomer of code \a code with parent
QSvgRenderer \a parent.

The allocated QSvgRenderer object is added to the member map container using \a
key.

Returns the allocated QSvgRenderer instance or nullptr if the Monomer code \a
key could not be found in the member polymer chemistry definition.
*/
QSvgRendererSPtr
PolChemDefRendering::newMonomerVignetteRendererFromMonomerCode(
  const QString &code, QObject *parent)
{
  MonomerSpec monomer_spec = getMonomerSpec(code);

  if(monomer_spec.getCode().isEmpty())
    return nullptr;

  QString file_path =
    mcsp_polChemDef->getXmlDataDirPath() + "/" + monomer_spec.getVector();

  QSvgRendererSPtr svg_renderer_sp =
    std::make_shared<QSvgRenderer>(file_path, parent);

  if(!svg_renderer_sp->isValid())
    {
      svg_renderer_sp.reset();
      return nullptr;
    }

  m_chemEntVignetteRenderers[code] = svg_renderer_sp;

  return svg_renderer_sp;
}


/*!
\brief Allocates a QSvgRenderer for a Modif of name \a key with parent
QSvgRenderer \a parent.

The allocated QSvgRenderer object is added to the member map container using \a
key.

Returns the allocated QSvgRenderer instance or nullptr if the Modif name \a
key could not be found in the member polymer chemistry definition.
*/
QSvgRendererSPtr
PolChemDefRendering::newModifVignetteRendererFromModifName(
  const QString &modif_name, QObject *parent)
{
  ModifSpec modif_spec = getModifSpec(modif_name);

  // If the modification name modifName is for a modification that has no
  // vignette associated to it in modification_dictionary, then use
  // the default one (if there is one in the directory corresponding
  // to current polymer chemistry definition). If there is not any,
  // then use the one that is defined in the
  // Modification_default_vignette.hpp file included in this source
  // file.

  QString file_path;

  if(modif_spec.getName().isEmpty())
    file_path =
      mcsp_polChemDef->getXmlDataDirPath() + "/default-modif-vignette.svg";
  else
    file_path =
      mcsp_polChemDef->getXmlDataDirPath() + "/" + modif_spec.getVector();

  std::shared_ptr<QSvgRenderer> svg_renderer_sp;

  if(!QFile(file_path).exists())
    {
      // None of the two files exists. Try to construct the renderer
      // using the svg vignette declared in
      // Modification_default_vignette.hpp in the form of QByteArray
      // defaultModifVignette.
      svg_renderer_sp =
        std::make_shared<QSvgRenderer>(defaultModifVignette, parent);
    }
  else
    {
      svg_renderer_sp = std::make_shared<QSvgRenderer>(file_path, parent);
    }

  if(!svg_renderer_sp->isValid())
    {
      svg_renderer_sp.reset();
      return nullptr;
    }

  m_chemEntVignetteRenderers[modif_name] = svg_renderer_sp;

  return svg_renderer_sp;
}

QSvgRendererSPtr
PolChemDefRendering::newCrossLinkerVignetteRenderer(
  const QString &cross_linker_name, QObject *parent)
{
  CrossLinkerSpec cross_linker_spec = getCrossLinkerSpec(cross_linker_name);

  // If the crossLinker name is for a crossLink that has no
  // vignette associated to it in cross_linker_dictionary, then use
  // the default one(if there is one in the directory corresponding
  // to current polymer chemistry definition). If there is not any,
  // then use the one that is defined in the
  // Modification_default_vignette.hpp file included in this source
  // file.

  QString file_path;

  if(cross_linker_spec.getName().isEmpty())
    file_path =
      mcsp_polChemDef->getXmlDataDirPath() + "/default-cross-link-vignette.svg";
  else
    file_path = mcsp_polChemDef->getXmlDataDirPath() + "/" +
                cross_linker_spec.getVector();

  QSvgRendererSPtr svg_renderer_sp;

  if(!QFile(file_path).exists())
    {
      // None of the two files exists. Try to construct the renderer
      // using the svg vignette declared in
      // Cross_link_default_vignette.hpp in the form of QByteArray
      // defaultCrossLinkerVignette.
      svg_renderer_sp =
        std::make_shared<QSvgRenderer>(defaultCrossLinkerVignette, parent);
    }
  else
    {
      svg_renderer_sp = std::make_shared<QSvgRenderer>(file_path, parent);
    }

  if(!svg_renderer_sp->isValid())
    {
      svg_renderer_sp.reset();
      return nullptr;
    }

  m_chemEntVignetteRenderers[cross_linker_name] = svg_renderer_sp;

  return svg_renderer_sp;
}


} // namespace MassXpert
} // namespace MsXpS
