/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         https://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/

#ifndef itkDOMReader_h
#define itkDOMReader_h

#include "itkDOMNodeXMLReader.h"
#include "itkObject.h"
#include "itkLogger.h"

namespace itk
{

/**
 * \class DOMReader
 * \brief Class to read an ITK object from an XML file or a DOM object, using the DOM APIs.
 *
 * End-users need to derive from this class to implement readers for user objects. In subclasses, users need
 * to provide an implementation for the virtual functions GenerateData(-,-).
 *
 * This class performs similar functions as the XMLReader - both provide the base for handling object reading from
 * an XML source. The difference is that, readers derived from this class perform object reading using the
 * easy-to-use DOM APIs, while XMLReader-based readers use the more error-prone SAX (Simple API for XML) APIs.
 *
 * Internally, this class first implicitly creates an intermediate DOM object from the input XML file using the
 * DOMNodeXMLReader, then performs output object generation by pulling information from the DOM object.
 *
 * Note: Though this class behaves similar to ProcessObject, it is not derived from ProcessObject. This is
 *       because many user objects to be read, e.g., registrations, transforms, optimizers, and so on, are not objects
 *       of type DataObject, thus this class cannot be connected to an ITK process pipeline.
 *
 * The following code snippet demonstrates how to use a DOM-based reader that is derived from this class:
 *
   \code
       itk::MyObjectType::Pointer output_object;
       const char* input_xml_file_name = ...
       itk::MyObjectDOMReader::Pointer reader = itk::MyObjectDOMReader::New();
       reader->SetFileName( input_xml_file_name );
       reader->Update();
       output_object = reader->GetOutput();
   \endcode
 *
 * \sa XMLReader
 * \sa DOMNodeXMLReader
 * \sa DOMNode
 *
 * \ingroup ITKIOXML
 */
template <typename TOutput>
class ITK_TEMPLATE_EXPORT DOMReader : public Object
{
public:
  ITK_DISALLOW_COPY_AND_MOVE(DOMReader);

  /** Standard class type aliases. */
  using Self = DOMReader;

  itkOverrideGetNameOfClassMacro(DOMReader);

  using OutputType = TOutput;

  using DOMNodeType = DOMNode;
  using DOMNodePointer = typename DOMNodeType::Pointer;

  using LoggerType = Logger;
  using LoggerPointer = typename LoggerType::Pointer;

  /** Set the input XML filename. */
  itkSetStringMacro(FileName);

  /** Get the input XML filename. */
  itkGetStringMacro(FileName);

  /**
   * The output object will be created automatically, but the user
   * can appoint a user object as the output by calling this function.
   */
  virtual void
  SetOutput(OutputType * output);

  /** Get the output object for full access. */
  OutputType *
  GetOutput();

  /** Get the output object for read-only access. */
  const OutputType *
  GetOutput() const;

  /**
   * Return the internal logger so that users can change the
   * output format or add/remove logging destinations.
   */
  itkGetConstMacro(Logger, LoggerType *);

  /**
   * Function called by Update() or end-users to generate the output object from a DOM object.
   * Some derived readers may accept an incomplete DOM object during the reading process, in those cases
   * the optional argument 'userdata' can be used to provide the missed information.
   */
  void
  Update(const DOMNodeType * inputdom, const void * userdata = nullptr);

  /**
   * Function called by end-users to generate the output object from the input XML file.
   */
  virtual void
  Update();

protected:
  DOMReader();

  /**
   * Function to be implemented in subclasses. It is called automatically
   * when update functions are performed. It should fill the contents of the output object by pulling
   * information from the intermediate DOM object.
   * Some derived readers may accept an incomplete DOM object during the reading process, in those cases
   * the optional argument 'userdata' can be used to provide the missed information.
   */
  virtual void
  GenerateData(const DOMNodeType * inputdom, const void * userdata) = 0;

private:
  /** Get/Set the intermediate DOM object. */
  itkSetObjectMacro(IntermediateDOM, DOMNodeType);
  itkGetModifiableObjectMacro(IntermediateDOM, DOMNodeType);

  /** Variable to hold the input XML file name. */
  std::string m_FileName{};

  /** Variable to hold the output object, created internally or supplied by the user. */
  OutputType * m_Output{};
  /** Variable to hold the output object if it is a smart object. */
  typename LightObject::Pointer m_OutputHolder{};

  /** Variable to hold the intermediate DOM object. */
  DOMNodePointer m_IntermediateDOM{};

  /** Variable to log various messages during the reading process. */
  mutable LoggerPointer m_Logger{};
};

} // namespace itk

#ifndef ITK_MANUAL_INSTANTIATION
#  include "itkDOMReader.hxx"
#endif

#endif // itkDOMReader_h
