#ifndef  __HDISKSPACE_H__
#define  __HDISKSPACE_H__
#include "TSystem.h"
#include "TString.h"
#include "TObjArray.h"
#include "TGraph.h"
#include "TCanvas.h"
#include <iostream>
#include <iomanip>
#include <sys/stat.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <vector>
#include <map>
#include <cstring>
#include <sstream>
using namespace std;
class  HDiskCatalog ;
R__EXTERN HDiskCatalog *gHDiskCatalog;
typedef struct stat mystat;
class HRedirect
{
    
    
private:
    std::ostream*   streamIn;
    std::streambuf* streamInBuffer;
    std::ostream*   streamOut;
    std::streambuf* streamOutBuffer;
public:
    HRedirect(std::ostream* inOriginal, std::ostream* inRedirect)
    {
	streamIn       = inOriginal;
	streamInBuffer = inOriginal->rdbuf();
	streamOut      = inRedirect;
	streamOutBuffer= inRedirect->rdbuf();
	inOriginal->rdbuf(inRedirect->rdbuf());
    }
    ~HRedirect()
    {
       streamIn ->rdbuf(streamInBuffer);
       streamOut->rdbuf(streamOutBuffer);
    }
};
class HFileSys {
private:
    static ULong64_t unitK ;
    static ULong64_t unitM ;
    static ULong64_t unitG ;
    static ULong64_t unitT ;
    static ULong64_t SmallerLimit ; 
    static map<uid_t,TString> musers;
    static map<gid_t,TString> mgroups;
    static map<uid_t,TString>  initUsers();
    static map<gid_t,TString>  initGroups();
public:
    static ULong64_t           getSmallerLimit() { return SmallerLimit;}
    static map<uid_t,TString>& getUserMap()      { return musers;  }
    static map<gid_t,TString>& getGroupMap()     { return mgroups; }
    static void                setUnit(ULong64_t u) { unitK = u; unitM=u*u; unitG=unitM*u; unitT=unitG*u; }
    static ULong64_t           getUnitK()           { return unitK;}
    static ULong64_t           getUnitT()           { return unitT;}
    static ULong64_t           getUnitG()           { return unitG;}
    static ULong64_t           getUnitM()           { return unitM;}
    static void                getUsers(map<uid_t,TString>& musers);
    static void                getGroups(map<gid_t,TString>& mgroups);
    static void                fillSysInfo();
    static Bool_t              getUser(uid_t uid,TString& name);
    static Bool_t              getGroup(gid_t gid,TString& name);
    static void                getModTime(time_t lastmod,TString& modtime);
    static void                getFileSize(ULong64_t size, TString& out);
    static void                lsFiles(TString directory, vector<TString>& fileList,Bool_t clear=kTRUE,Bool_t fullpath=kFALSE);
    static void                lsFilesRecursive(TString directory, vector<TString>&fullList);
    static void                lsDirectory(TString directory, vector<TString>& fileList);
    static void                lsDirectoryRecursive(TString directory, vector<TString>&fullList);
};
class  HDiskFile : public TNamed {
private:
    ULong64_t size;             
    UInt_t    group;
    UInt_t    owner;
    time_t    lastmod;
    UInt_t    dir;
public:
    HDiskFile(TString name="");
    ~HDiskFile();
    ULong64_t getSize()                { return size; }
    UInt_t    getGroup()               { return group ; }
    UInt_t    getOwner()               { return owner; }
    time_t    getLastMod()             { return lastmod;}
    UInt_t    getDir()                 { return dir; }
    void      setSize   (ULong64_t sz) { size  = sz ; }
    void      setGroup  (UInt_t gid)   { group   = gid ; }
    void      setOwner  (UInt_t uid)   { owner   = uid;  }
    void      setLastMod(time_t mod)   { lastmod = mod; }
    void      setDir(UInt_t ind)       { dir =ind; }
    void      print    (Int_t sp=0);
    Bool_t    isToSmall()            { if(size<HFileSys::getSmallerLimit()) return kTRUE; else return kFALSE;}
    ClassDef(HDiskFile,1)
};
class  HDiskDir : public TNamed {
private:
    ULong64_t size;             
    UInt_t    group;            
    UInt_t    owner;            
    time_t    lastmod;
    UInt_t    nFilesSmallerLimit;
    UInt_t    nFilesSmallerLimitTotal;
    UInt_t    nFilesTotal;
    UInt_t    nEmptyDirs;
    UInt_t    nDirsTotal;
    UInt_t    nEmptyDirsTotal;
    UChar_t   level;
    UInt_t    mother;
    vector<HDiskFile> files;
    vector<Int_t>     dirs;
    void   update         (HDiskDir* mother);
    void   updateRecursive(HDiskDir* daughter,HDiskDir* mother);
public:
    HDiskDir(TString name="");
    ~HDiskDir();
    ULong64_t          getSize()                      { return size; }
    UInt_t             getGroup()                     { return group; }
    UInt_t             getOwner()                     { return owner; }
    time_t             getLastMod()                   { return lastmod;}
    UInt_t             getNFiles()                    { return files.size();}
    UInt_t             getNFilesTotal()               { return nFilesTotal;}
    UInt_t             getNSmallFiles()               { return nFilesSmallerLimit;}
    UInt_t             getNSmallFilesTotal()          { return nFilesSmallerLimitTotal;}
    UInt_t             getNEmptyDirs()                { return nEmptyDirs;}
    UInt_t             getNDirsTotal()                { return nDirsTotal;}
    UInt_t             getNEmptyDirsTotal()           { return nEmptyDirsTotal;}
    UInt_t             getNDirs()                     { return dirs.size();}
    UChar_t            getLevel()                     { return level;}
    UInt_t             getMother()                    { return mother; }
    vector<HDiskFile>& getFiles()                     { return files;}
    vector<Int_t>&     getDirs()                      { return dirs;}
    void               setSize   (ULong64_t sz)       { size = sz; }
    void               setGroup  (UInt_t gid)         { group = gid; }
    void               setOwner  (UInt_t uid)         { owner = uid;  }
    void               setLastMod(time_t mod)         { lastmod = mod; }
    void               setNFilesTotal(UInt_t n)       { nFilesTotal = n;}
    void               setNSmallFiles(UInt_t n)       { nFilesSmallerLimit = n;}
    void               setNSmallFilesTotal(UInt_t n)  { nFilesSmallerLimitTotal = n;}
    void               setNEmptyDirs(UInt_t n)        { nEmptyDirs = n;}
    void               setNDirsTotal(UInt_t n)        { nDirsTotal = n;}
    void               setNEmptyDirsTotal(UInt_t n)   { nEmptyDirsTotal = n;}
    void               setLevel(UChar_t lvl)          { level = lvl;}
    void               setMother(UInt_t index)        { mother = index; }
    void               setDirToFiles(UInt_t index);
    void               addSize( ULong64_t sz)         { size += sz;}
    void               addNFilesTotal(UInt_t n)       { nFilesTotal += n;}
    void               addNSmallFiles(UInt_t n)       { nFilesSmallerLimit += n;}
    void               addNSmallFilesTotal(UInt_t n)  { nFilesSmallerLimitTotal += n;}
    void               addNEmptyDirs(UInt_t n)        { nEmptyDirs += n;}
    void               addNDirsTotal(UInt_t n)        { nDirsTotal += n;}
    void               addNEmptyDirsTotal(UInt_t n)   { nEmptyDirsTotal += n;}
    void               addFile(HDiskFile f)           { files.push_back(f); }
    void               addDir (Int_t index)           { dirs .push_back(index); }
    void               resetDirs()                    { dirs .clear();}
    void               update();
    void               print     (Int_t sp=0, TString base="");
    void               printFiles(Int_t sp=0);
    void               printDirs (Int_t sp=0, TString base="");
    Bool_t             scan(UInt_t indDir);
    Bool_t             scan(mystat& statu, UInt_t index,vector<TString>& dirList);
    Bool_t             isDaughter(HDiskDir* mother);
    Bool_t             isEmpty() { if(files.size()==0&&dirs.size()==0) return kTRUE; else return kFALSE;}
    ClassDef(HDiskDir,1)
};
class  HDiskCatalog : public TNamed {
private:
    TString diskname;
    TObjArray*  list; 
    map<uid_t,TString> musers;
    map<gid_t,TString> mgroups;
    time_t lastScanStart;
    time_t lastScanStop;
    map<TString,Int_t>     mDirToInd;
    Int_t nSplit;
    
    
    map<TString,HDiskDir*> mDirOld; 
    Int_t fnfiles;        
    Int_t fnfilestotal;   
    Int_t fndirs;         
    TObjArray*  flisttmp; 
    struct stat status ;  
    
    static Bool_t cmpName       (HDiskDir*, HDiskDir*);
    static Bool_t cmpSize       (HDiskDir*, HDiskDir*);
    static Bool_t cmpNFiles     (HDiskDir*, HDiskDir*);
    static Bool_t cmpNSmallFiles(HDiskDir*, HDiskDir*);
    static Bool_t cmpNFilesRatio(HDiskDir*, HDiskDir*);
    HDiskDir* getDir(TString name, TObjArray* listtmp,Int_t* ind);
    void      addDir(TString n, Int_t ind)  { if(mDirToInd.find(n) == mDirToInd.end()) mDirToInd[n] = ind;}
    Int_t     getDirMap(TString n)             {
	map<TString,Int_t>::iterator it = mDirToInd.find(n);
	if(it==mDirToInd.end()) return -1;
	else                    return it->second;
    }
    HDiskDir* getDirMapOld(TString n) {
	map<TString,HDiskDir*>::iterator it = mDirOld.find(n);
	if(it==mDirOld.end()) return 0;
	else                  return it->second;
    }
    void loopDirectory         (TString directory, vector<TString>& dirList);
    void loopDirectoryRecursive(TString directory);
public:
    HDiskCatalog(TString name="");
    ~HDiskCatalog ();
    TString              getDiskName()                        { return diskname;}
    TObjArray*           getList()                            { return list;}
    time_t               getLastScanStart()                   { return lastScanStart;}
    time_t               getLastScanStop()                    { return lastScanStop;}
    map<uid_t,TString>&  getUserMap()                         { return musers;}
    map<gid_t,TString>&  getGroupMap()                        { return mgroups;}
    Bool_t               getUser   (uid_t uid,TString& name);
    Bool_t               getGroup  (gid_t gid,TString& name);
    void                 setLastScanStart(time_t t)           { lastScanStart = t;}
    void                 setLastScanStop(time_t t)            { lastScanStop  = t;}
    void                 setUserMap (map<uid_t,TString>& mu)  { musers = mu;}
    void                 setGroupMap(map<gid_t,TString>& mg)  { mgroups = mg;}
    void                 setCurrentCatalog()                  { gHDiskCatalog = this;}
    Bool_t               scan();
    Int_t                addDir(HDiskDir* dir);
    void                 updateDirIndices();
    HDiskDir*            getDir                  (TString name,Int_t* ind=0);
    void                 getDaughterDirs         (HDiskDir* dir, vector<HDiskDir*>& daughters);
    void                 getDaughterDirsRecursive(HDiskDir* dir, vector<HDiskDir*>& daughters);
    void                 sortDirs                (HDiskDir* dir, vector<HDiskDir*>& daughters,TString option);
    void                 sortDirsRecursive       (HDiskDir* dir, vector<HDiskDir*>& daughters,TString option);
    void                 print                   (UChar_t maxlevel=255);
    void                 printDisk               (UChar_t maxlevel=255,TString option="size",Int_t nfill=0,TString filler=" ");
    void                 printExecution();
    UInt_t               filterDirs              (TString regexp,vector<HDiskDir*>& dirs,TString range1S="",TString range2S="",Long64_t size=0);
    UInt_t               filterFiles             (TString regexpdir,TString regexpfile,vector<HDiskFile*>& files,TString range1S="",TString range2S="",Long64_t size=0);
    void                 printDirs               (TString regexp,TString range1S="",TString range2S="",Long64_t size=0);
    void                 printFiles              (TString regexpdir,TString regexpfile,TString range1S="",TString range2S="",Long64_t size=0);
    ClassDef(HDiskCatalog,1)
};
class HDiskStat : public TNamed {
private:
    map<TString,vector<TGraph> > mDirToVal;
    vector<Int_t> vcolors;
    vector<Int_t> vmarkers;
    vector<Int_t> vstyles;
    Int_t evalOpt(TString opt);
    ULong64_t unit;
    TString diskname;
public:
    HDiskStat(TString name="");
    ~HDiskStat();
    void            setDiskName(TString name) { diskname = name;}
    void            setUnit(ULong64_t u)      {unit = u;}
    TString         getDiskName()  {return diskname;}
    TGraph*         getDir         (TString dirname,TString opt="size");
    vector<TGraph>* getDirVec      (TString dirname);
    void            addEntry       (time_t scanstart,HDiskDir* dir);
    Bool_t          findMinMaxGraph(Double_t& xmin,Double_t& xmax,Double_t& ymin,Double_t& ymax,TGraph* g);
    Bool_t          findMinMaxAll  (Double_t& xmin,Double_t& xmax,Double_t& ymin,Double_t& ymax,TString opt);
    Bool_t          findMinMaxAll  (Double_t& xmin,Double_t& xmax,Double_t& ymin,Double_t& ymax,vector<TGraph*>& vg);
    TCanvas*        draw(TString opt,UInt_t lastDays,ULong64_t u,TString select = "daughters");
    ClassDef(HDiskStat,1)
};
#endif /* !__HDISKSPACE_H__ */