#ifndef __JROOT__JTREEWRITER__
#define __JROOT__JTREEWRITER__

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wall"
#include "TTree.h"
#include "TBranch.h"
#pragma GCC diagnostic pop

#include "JROOT/JTreeParameters.hh"
#include "JIO/JSerialisable.hh"


/**
 * \file
 * TTree writing for template data type.
 * \author mdejong
 */
namespace JROOT {}
namespace JPP { using namespace JROOT; }

namespace JROOT {

  using JIO::JReader;


  /**
   * Auxiliary class for template TTree writing.
   */
  template<class T>
  class JTreeWriter :
    public virtual TTree,
    public JTreeParameters
  {
  public:
    /**
     * Constructor.
     *
     * Note that the default TTree parameters are obtained using method JROOT::getTreeParameters.
     *
     * \param  parameters    parameters of TTree
     */
    JTreeWriter(const JTreeParameters& parameters = JROOT::getTreeParameters<T>()) :
      JTreeParameters(parameters),
      address(NULL)
    {
      SetNameTitle(this->getTreeName(), this->getTreeTitle());

      branch = Branch(this->getBranchName(),
		      T::Class_Name(), 
		      &address, 
		      this->getBasketSize(), 
		      this->getSplitLevel());

      branch->SetCompressionLevel(this->getCompressionLevel());
    }


    /**
     * Get the pointer to the unique TBranch belonging this TTree.
     *
     * \return               pointer to TBranch
     */
    const TBranch* GetBranch() const
    { 
      return branch; 
    }


    /**
     * Data object output equivalent of TTree::Fill().
     *
     * \param  object        data object
     * \return               as TTree::Fill
     */
    Int_t Write(const T& object)
    {
      address = &object;
    
      return this->Fill();
    }

  protected:
    using TTree::GetBranch;
    using TTree::Write;

  private:
    TBranch*  branch;    //!< Pointer to unique branch belonging to this TTree.
    const T*  address;   //!< Pointer to unique object belonging to this TTree.
  };


  /**
   * Interface for template TTree writing and copying.
   */
  struct JTreeCopyWriterInterface :
    public virtual TTree
  {
    /**
     * Copy data.
     *
     * \param  in            binary reader
     */
    virtual Int_t copy(JReader& in) = 0;
  };


  /**
   * Implementation for template TTree writing and copying.
   * This class implements the JTreeCopyWriter interface.
   */
  template<class T>
  class JTreeCopyWriter : 
    public JTreeWriter<T>,
    public JTreeCopyWriterInterface
  {
  protected:
    /**
     * Constructor.
     *
     * \param  tree          parameters of TTree
     */
    JTreeCopyWriter(const JTreeParameters& tree) :
      JTreeWriter<T>(tree)
    {}


    /**
     * Hide copy constructor.
     *
     * \param  writer        TTree writer object
     */
    JTreeCopyWriter(const JTreeCopyWriter<T>& writer);


  public: 
    /**
     * Get reference to unique instance of this class object.
     *
     * \return               reference to this class object
     */
    static JTreeCopyWriter<T>& getInstance()
    {
      static JTreeCopyWriter<T> writer(getTreeParameters<T>());

      return writer;
    }


    /**
     * Copy data.
     *
     * \param  in            binary reader
     */
    virtual Int_t copy(JReader& in) override 
    {
      in >> object;

      return static_cast<JTreeWriter<T>&>(*this).Write(object);
    }


  protected:
    T object;
  };


  /**
   * Get the TTree writer and copy for this type of object.
   *
   * \return             TTree writer and copy for this type of object
   */
  template <class T>
  inline JTreeCopyWriter<T>& getTreeCopyWriter()
  {
    return JTreeCopyWriter<T>::getInstance();
  }
}

#endif