#ifndef MARS_MWreitFitsFile
#define MARS_MWreitFitsFile

#include <list>
#include <vector>
#include <map>

#include <TClass.h>

#ifndef MARS_MWriteFile
#include "MWriteFile.h"
#endif

#ifndef MARS_MTopFitsGroup
#include "MTopFitsGroup.h"
#endif

#ifndef MARS_MFitsArray
#include "MFitsArray.h"
#endif

///////////////////////////////////////////////////////////////////////////////
// Information of one MParContainer, which data should be written into all or  
// just a few columns of one FITS table                                        
class MFitsSubTable
{
   // the container, which data should be written to the FITS sub-table.   
   MParContainer  *fContainer; 

   // used only if the Container is defined by its name.                   
   // if kTRUE  must exist in the MParList of the program                  
   // if kFALSE the container is ignored. if it is not in the              
   //           MParList of the program                                    
   Bool_t         fMust;

public:
   MFitsSubTable( Bool_t must)
      : fContainer(NULL), fMust(must)
      {}

   MFitsSubTable(MParContainer * container,  Bool_t must)
      : fContainer(container),  fMust(must)
      {}

  
   MFitsSubTable(const MFitsSubTable & subTable)
      : fContainer(subTable.fContainer), fMust(subTable.fMust)
      {}

   Bool_t            MustHave()           {return fMust;}
   MParContainer *   GetContainer()       {return fContainer;}
   void              SetContainer(MParContainer * cont)
                                          {fContainer = cont;}

};



///////////////////////////////////////////////////////////////////////////////
// A class to write data of MParContainer into FITS files.                     
class MWriteFitsFile : public MWriteFile
{
private:
   // all (global) currently open top FITS groups.                      
   // Note: several instances of MWriteFitsFile can write to the same   
   //       top level FITS group (a top level FITS group corresponds to 
   //       one ROOT file)                                              
   static std::map<MUniqueFileId, MTopFitsGroup>    fTopFitsGroups;

   // pointer to the top level FITS group of this instance              
   std::map<MUniqueFileId, MTopFitsGroup>::iterator iTopFitsGroup;

   // the key (TString) of following maps is the FITS table name, which 
   // corresponds to one TTree                                          

   // all main FITS table. Children tables to store data of             
   // TClonesArray are not in this map.
   //ETIENNE ofits objects cannot be copied. So store pointers instead
   std::map<TString, ofits*>                       fFitsTables;

   // all information needed to write data of TClonesArray to their      
   // own FITS table. Note: one parent FTIS table may have several       
   // children tables, each for one TClonesArray.                        
   std::map<TString, std::list<MArrayHelperBase *> >    fClHelper;

   // all information about the MParContainer, which data are stored     
   // in the same FITS table. The key of map<TString, MFitsSubTable>     
   // is the class name of the MParContainer                             
   std::map<TString, std::map<TString, MFitsSubTable> > fSubTables;

   std::map<TString, std::vector<void*> >    fDataPointers;
   std::map<TString, std::vector<char> >     fTypeChars;
   std::map<TString, std::vector<uint32_t> > fColSizes;
   std::map<TString, std::vector<uint32_t> > fColWidth;
   std::map<TString, bool> fTableObjectCreated;
   std::map<TString, bool> fTableHeaderWritten;

   void DeleteArrayHelper();

   // rule to construct an output file-name from the input-filename      
   TString fRule;  

   TString fOpenOption; // RECREATE or NEW
   TString fGroupName;  // name of top level group
   TString fTitle;      // title of top level group

   void OpenTopLevelGroup(const char * fname);
   void CloseTopLevelGroup();

   Bool_t InitColumns(const TString & tableName,
                      const TString & parentVarName,
                      ofits* fitsTable,
                      void * baseAdr,
                      TClass * classDef);
   void InitSingleColumn(const TString& tableName,
                         uint32_t count,
                         const std::string& typeName,
                         void* dataPointer,
                         const std::string& columnName,
                         const std::string& unit,
                         const std::string& comment);
   void writeOneRow(const TString& tableName);
   void InitAttr(const char* attrName,
                 const char* dataType,
                 void* var,
                 const char* unit=NULL,
                 const char* comment=NULL,
                 ofits* outFile=NULL);

   // MWrite
   Bool_t      IsFileOpen() const  
                              {return iTopFitsGroup != fTopFitsGroups.end();}
   Bool_t      CheckAndWrite();
   Bool_t      GetContainer(MParList *pList);
   const char *GetFileName() const;

   Int_t       PreProcess(MParList *pList);
   Int_t       PostProcess();

   std::string Trim(const std::string &str);

   // MTask
   Bool_t ReInit(MParList *pList);

   std::vector<std::string> fVetoedColumns;
   std::map<std::string, uint32_t> fBytesPerSamples;

public:

   //Veto a type-mapping functions and variables
   Bool_t VetoColumn(const std::string& colName);
   Bool_t SetBytesPerSample(const std::string& colName, uint32_t numBytes);

   enum FILE_MODE {
      kMultiFiles,
      kSingleFile
      } ;

    MWriteFitsFile(const char *fname,
                   FILE_MODE fileMode = kMultiFiles,
                   const Option_t *opt="RECREATE",
                   const char *name=NULL,
                   const char *title=NULL);
    //Similar contructor to what MWriteRootFiles takes.
    //Some of the args are not used: comp as they does not make sense in FITS
    MWriteFitsFile(const Int_t comp,
                   const char* rule,
                   const Option_t* option="RECREATE",
                   const char* fTitle="Untitled",
                   const char* name=NULL,
                   const char* title=NULL);

   ~MWriteFitsFile();


   void AddContainer(const char *cName,   const char *tName=NULL, Bool_t must=kTRUE);
   void AddContainer(MParContainer *cont, const char *tName=NULL, Bool_t must=kTRUE);


   void AddTree(const char *name, Bool_t force=kTRUE)
   {
       AddContainer(Form("MReport%s", name), name, force);
       AddContainer(Form("MTime%s", name),   name, force);
   }

   ClassDef(MWriteFitsFile, 0)   
};


#endif
