#ifndef __JROOTFILE__
#define __JROOTFILE__

#include <string>
#include <stdlib.h>
#include <string.h>

#include "TFile.h"
#include "TError.h"

#include "JLang/JAccessible.hh"
#include "JLang/JException.hh"
#include "JLang/JThrow.hh"
#include "JLang/JStorage.hh"


/**
 * \author mdejong
 */

namespace JROOT {}
namespace JPP { using namespace JROOT; }

namespace JROOT {

  using JLANG::JAccessible;
  using JLANG::JStorage;
  using JLANG::JFileOpenException;
  using JLANG::JFileRecoveryException;
  using JLANG::JThrow;

  /**
   * Environment variable to disable ROOT file recovery.
   *
   * If not set or set to 0, allow recovery of ROOT file, else do not.
   */
  static const char* const ROOT_FILE_RECOVERY_DISABLE = "ROOT_FILE_RECOVERY_DISABLE";
 

  /**
   * ROOT file.
   *
   * This class implements the method is_open() of the JLANG::JAccessible interface.
   */
  class JRootFile :
    public virtual JAccessible,
    public JStorage<TFile>
  {
  protected:
    /**
     * Default constructor.
     * The printing of ROOT errors is suppressed. 
     */
    JRootFile()
    {
      gErrorIgnoreLevel = kError;
    }


  public:
    /**
     * Get file.
     *
     * \return                 pointer to file
     */
    TFile* getFile() const 
    {
      return get();
    }


    /**
     * Check is file is open.
     *
     * \return                 true if open; else false
     */
    virtual bool is_open() const override 
    {
      return (getFile() != NULL && getFile()->IsOpen());
    }

  private:
    JRootFile(const JRootFile&);
    JRootFile(JRootFile&&);
    JRootFile& operator=(const JRootFile&);
    JRootFile& operator=(JRootFile&&);
  };


  /**
   * ROOT input file.
   *
   * This class implements the methods open() and close() of the JLANG::JAccessible interface.
   */
  class JRootInputFile :
    public JRootFile
  {
  public:
    /**
     * Default constructor.
     */
    JRootInputFile() :
      JRootFile()
    {}


    /**
     * Constructor.
     *
     * \param  file_name      file name
     */
    JRootInputFile(const char* file_name) :
      JRootFile()
    {
      do_open(file_name);
    }


    /**
     * Destructor.
     *
     * The destructor closes the file if it is still open.
     */
    ~JRootInputFile()
    {
      do_close();
    }


    /**
     * Open file.
     * The file is not opened when no file exists with the given name.
     *
     * \param  file_name      file name
     */
    virtual void open(const char* file_name) override 
    {
      do_open(file_name);
    }


    /**
     * Close file.
     */
    virtual void close() override 
    {
      do_close();
    }


  private:
    /**
     * Open file.
     * The file is not opened when no file exists with the given name.
     *
     * \param  file_name      file name
     */
    void do_open(const char* file_name)
    {
      set(TFile::Open(file_name, "READ"));

      if (!is_open()) {
	Throw(MAKE_EXCEPTION(JFileOpenException, "Error opening file " << file_name));
      }
      
      if (getFile()->TestBit(TFile::kRecovered)) {

	const char* const value = getenv(ROOT_FILE_RECOVERY_DISABLE);

	if (value != NULL && strcmp(value,"0") != 0) {
	  Throw(MAKE_EXCEPTION(JFileRecoveryException, "Error recovery file " << file_name << " disabled"));
	}
      }
    }


    /**
     * Close file.
     */
    void do_close()
    {
      if (getFile() != NULL && getFile()->IsOpen()) {
	getFile()->Close();
      }

      reset();
    }
  };


  /**
   * ROOT output file.
   *
   * This class implements the methods open() and close() of the JLANG::JAccessible interface.
   */
  class JRootOutputFile :
    public JRootFile
  {
  public:
    /**
     * Default constructor.
     */
    JRootOutputFile() :
      JRootFile()
    {}


    /**
     * Constructor.
     *
     * \param  file_name      file name
     */
    JRootOutputFile(const char* file_name) :
      JRootFile()
    {
      do_open(file_name);
    }


    /**
     * Destructor.
     *
     * The destructor writes and closes the file if it is still open.
     */
    ~JRootOutputFile()
    {
      do_close();
    }


    /**
     * Open file.
     * The file is not opened when a file exists with the given name.
     *
     * \param  file_name      file name
     */
    virtual void open(const char* file_name) override 
    {
      do_open(file_name);
    }


    /**
     * Close file.
     * This method calls the TFile::Write method before closing the file.
     */
    virtual void close() override 
    {
      do_close();
    }


  private:
    /**
     * Open file.
     * The file is not opened when a file exists with the given name.
     *
     * \param  file_name      file name
     */
    void do_open(const char* file_name)
    {
      set(TFile::Open(file_name, "CREATE"));

      if (!is_open()) {
	Throw(MAKE_EXCEPTION(JFileOpenException, "Error opening file " << file_name));
      }
    }


    /**
     * Close file.
     * This method calls the TFile::Write method before closing the file.
     */
    void do_close()
    {
      if (getFile() != NULL && getFile()->IsOpen()) {
	getFile()->Write();
	getFile()->Close();
      }

      reset();
    }
  };
}

#endif