/* 
   ORSA - Orbit Reconstruction, Simulation and Analysis
   Copyright (C) 2002-2004 Pasquale Tricarico
   
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.
   
   As a special exception, Pasquale Tricarico gives permission to
   link this program with Qt commercial edition, and distribute the
   resulting executable, without including the source code for the Qt
   commercial edition in the source distribution.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#ifndef _ORSA_FILE_H_
#define _ORSA_FILE_H_

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "orsa_analysis.h"
#include "orsa_config.h"
#include "orsa_orbit.h"
#ifdef HAVE_GSL
#include "orsa_orbit_gsl.h"
#endif // HAVE_GSL
#include "orsa_universe.h"
#include "orsa_body.h"
#include "orsa_file_jpl.h"

#ifdef HAVE_LIBZ
#include <zlib.h>
#endif

#include <string>
#include <map>
#include <list>
#include <cstdio>

#ifdef HAVE_LIBZ
#define FILE_TYPE gzFile
#define OPEN_FILE gzopen
#define CLOSE_FILE gzclose
#define REWIND_FILE gzrewind
#define GETS_FILE(buffer,length,file) gzgets((file),(buffer),(length))
#define PUTS_FILE(buffer,file) gzputs((file),(buffer))
#define READ_FILE(buffer,size,num,file) gzread((file),(buffer),(size)*(num))
#define WRITE_FILE(buffer,size,num,file) gzwrite((file),(buffer),(size)*(num))
#define SEEK_FILE(file,offset,whence) gzseek((file),(offset),(whence))
#define FLUSH_FILE(file) gzflush((file),Z_FULL_FLUSH)
#define OPEN_READ "rb"
#define OPEN_WRITE "wb"
#else
#define FILE_TYPE FILE*
#define OPEN_FILE fopen
#define CLOSE_FILE fclose
#define REWIND_FILE rewind
#define GETS_FILE(buffer,length,file) fgets((buffer),(length),(file))
#define PUTS_FILE(buffer,file) fputs((buffer),(file))
#define READ_FILE(buffer,size,num,file) fread((buffer),(size),(num),(file))
#define WRITE_FILE(buffer,size,num,file) fwrite((buffer),(size),(num),(file))
#define SEEK_FILE(file,offset,whence) fseek((file),(offset),(whence))
#define FLUSH_FILE(file) fflush((file))
#define OPEN_READ "r"
#define OPEN_WRITE "w"
#endif

namespace orsa {
  
  enum FILE_STATUS {CLOSE,OPEN_R,OPEN_W};
  
  //! File base class
  class File {
  public:
    File() { 
      status = CLOSE; 
      file   = 0;
    }
    
    virtual ~File() { 
      Close(); 
    };
    
  public:
    void Close();
    
  public:
    inline virtual std::string GetFileName () const { return filename; }
    
    inline virtual void SetFileName (std::string name_in) {
      if (status != CLOSE) Close();
      filename = name_in;
    }
    
    inline virtual void SetFileName (char * name_in) {
      std::string n = name_in;
      SetFileName (n);
    }
    
  protected:
    std::string filename;
    FILE_TYPE   file;
    FILE_STATUS status;
  };
  
  // intermediate classes 
  
  //! Read-only files class
  class ReadFile : public File {
  public:
    ReadFile() : File() { }
    void Open();
    virtual void Read() = 0;
  };
  
  //! Write-only files class
  class WriteFile : public File {
  public:
    WriteFile() : File() { }
    void Open();
    virtual void Write() = 0;
  };
  
  //! Read and write files class
  class ReadWriteFile : public File  {
  public:
    ReadWriteFile() : File() { }
    void Open(const FILE_STATUS st = OPEN_R);
    virtual void Read() = 0;
    virtual void Write() = 0;
  };
  
  // user-ready classes
  
  enum M5COLS {C7,C10};
  
  //! Mercury 5 integration input files
  class Mercury5IntegrationFile : public ReadFile { 
  
  public:
    Mercury5IntegrationFile(OrbitStream&);
    Mercury5IntegrationFile(OrbitStream&, M5COLS);

  public:
    void Read();
    
    M5COLS cols;
    
  public:
    // bool GuessCorrectType() { return true; }
   
  private:
    OrbitStream *os;
  };
  
  //! Modified Radau input files
  class RadauModIntegrationFile : public ReadFile { 
    
  public:
    RadauModIntegrationFile(OrbitStream&);
    
  public:
    // bool GuessCorrectType() { return true; }
    
  public:
    void Read();
    
  private:
    OrbitStream *os;
  }; 
  
  //! SWIFT integration file
  class SWIFTFile : public ReadFile {
  public:
    SWIFTFile(OrbitStream&);
    
  public:
    void Read();
    int  AsteroidsInFile();
    
  public:
    // bool GuessCorrectType() { return true; }
    
  public:
    OrbitStream *os;
  };
  
  //! Locations of the observatories
  class LocationFile : public ReadFile {
  public:
    LocationFile();
    
  public:
    void Read();
    
  public:
    Vector ObsPos(const std::string, const Date&);
    
  public:
    std::map<std::string,Location> locations;
    
  public:
    // vector<std::string> codes;
    std::list<std::string> codes;
  };
  
  // world visible LocationFile // defined in orsa_universe.cc
  extern LocationFile * location_file;
  
  //! MPC observation file
  class MPCObsFile : public ReadFile {
  public:
    // MPCObsFile(vector<Observation> *obs_in);
    MPCObsFile();
    
  public:
    void Read();
    
  public:
    bool ReadNominalOrbit(OrbitWithEpoch &);
    
  public:
    std::vector<Observation> obs;
  };
  
  //! AstDyS observation file, usually with a .rwo extension
  class RWOFile : public ReadFile {
  public:
    RWOFile();
    
  public:
    void Read();
    
  public:
    std::vector<Observation> obs;
  };
  
  //! Read-only asteroid files class
  class AsteroidDatabaseFile : public ReadFile {
  public:	
    AsteroidDatabaseFile() : ReadFile() { db = 0; }
  public:
    AsteroidDatabase *db;
  public:     
    inline virtual void read_progress(int, bool&, bool&) { };
    inline virtual void read_finished() { };
  };
  
  
  //! NEODyS and AstDyS .ctc and .ctm files
  class AstDySMatrixFile : public AsteroidDatabaseFile {
  public:	
    AstDySMatrixFile();
    ~AstDySMatrixFile();
  public:
    void Read();
  };
  
  
  //! NEODyS and AstDyS .cat file
  class NEODYSCAT : public AsteroidDatabaseFile {
  public:
    NEODYSCAT();
    ~NEODYSCAT();
  public:
    void Read();
  };
  
  // JPL DASTCOM files
  class JPLDastcomNumFile : public AsteroidDatabaseFile {
  public:
    JPLDastcomNumFile();
    virtual ~JPLDastcomNumFile();
    void Read();
  };  
  
  class JPLDastcomUnnumFile : public AsteroidDatabaseFile {
  public:
    JPLDastcomUnnumFile();
    virtual ~JPLDastcomUnnumFile();
    void Read();
  };  
   
  class JPLDastcomCometFile : public AsteroidDatabaseFile {
  public:
    JPLDastcomCometFile();
    virtual ~JPLDastcomCometFile();
    void Read();
  };  
  
  //! Lowell asteroids database file
  /* 
     class AstorbFile_base {
     public:
     virtual ~AstorbFile_base() { };
     
     public:
     virtual void read_progress(int,bool&,bool&) { };
     virtual void read_finished() { };
     };
  */
  
  //! Lowell asteroids database file
  // class AstorbFile : public AstorbFile_base, public ReadFile {
  // class AstorbFile : public ReadFile {
  class AstorbFile : public AsteroidDatabaseFile {
    
  public:
    AstorbFile();
    virtual ~AstorbFile();
    
  public: 
    void Read();
    //
    // inline virtual void read_progress(int, bool &bool_pause, bool &bool_stop) { };
    // inline virtual void read_finished() { };
    
  public:
    // bool GuessCorrectType();
    
  public: 
    // Date GetEpoch();
    // int GetSize();
    
    /* public:
       AsteroidDatabase *db;
    */
  };
  
  //! MPC asteroids database file
  // class MPCOrbFile : public ReadFile {
  class MPCOrbFile : public AsteroidDatabaseFile {
  public:
    MPCOrbFile();
    ~MPCOrbFile();
    
  public:
    void Read();
    
  public:
    // bool GuessCorrectType();
    
    /* 
       public:
       AsteroidDatabase *db;
    */
  };
  
  //! MPC comets database file
  // class MPCCometFile : public ReadFile {
  class MPCCometFile : public AsteroidDatabaseFile {
  public:
    MPCCometFile();
    ~MPCCometFile();
    
  public:
    void Read();
    
  public:
    // bool GuessCorrectType();
    
    /* 
       public:
       AsteroidDatabase *db;
    */
  };
  
  // NEVER change the numbers
  // values are compatible with the zlib return values
  enum OrsaFileDataType { 
    OFDT_END_OF_FILE=0,
    OFDT_UNIVERSE=1,
    OFDT_EVOLUTION=2,
    OFDT_FRAME=3,
    OFDT_BODY=4
  };
  
  inline void convert(OrsaFileDataType &ofdt, const unsigned int i)  {
    switch(i) {
    case 0: ofdt = OFDT_END_OF_FILE; break;
    case 1: ofdt = OFDT_UNIVERSE;    break;
    case 2: ofdt = OFDT_EVOLUTION;   break;
    case 3: ofdt = OFDT_FRAME;       break;
    case 4: ofdt = OFDT_BODY;        break;
      //
    default:
      ORSA_ERROR("conversion problem: i = %i",i);    
      break;
    }
  }
  
  //! orsa default input-output file  
  class OrsaFile : public ReadWriteFile {
    
  public:
    OrsaFile();
    
  public:
    void Read();
    void Write();
    
  public:
    static bool GoodFile(const std::string&);
    
  private:
    virtual void make_new_universe(Universe**,length_unit,mass_unit,time_unit,UniverseType,ReferenceSystem,TimeScale);
    virtual void make_new_evolution(Evolution**);
    
  protected:
    void Write(Universe**);
    void Read( Universe**);
    void Write(Evolution**);
    void Read( Evolution**);
    void Write(Frame*,bool=false);
    void Read( Frame*,bool=false);
    void Write(Body*);
    void Read( Body*);
    void Write(BodyWithEpoch*);
    void Read( BodyWithEpoch*);
    // void Write(Integrator**);
    void Write(const Integrator *);
    void Read( Integrator**); 
    // void Write(Interaction**);
    void Write(const Interaction *);
    void Read( Interaction**);
    void Write(std::string*);
    void Read( std::string*);
    void Write(orsa::Vector*);
    void Read( orsa::Vector*);
    void Write(bool*);
    void Read( bool*);
    void Write(unsigned int*);
    void Read( unsigned int*);
    void Write(int*);
    void Read( int*);
    void Write(double*);
    void Read( double*);
    void Write(IntegratorType*);
    void Read( IntegratorType*);
    void Write(InteractionType*);
    void Read( InteractionType*);
    void Write(time_unit*);
    void Read( time_unit*);
    void Write(length_unit*);
    void Read( length_unit*);
    void Write(mass_unit*);
    void Read( mass_unit*);
    void Write(Date*);
    void Read( Date*);
    void Write(UniverseTypeAwareTime*);
    void Read( UniverseTypeAwareTime*);
    void Write(UniverseTypeAwareTimeStep*);
    void Read( UniverseTypeAwareTimeStep*);
    void Write(ReferenceSystem*);
    void Read( ReferenceSystem*);
    void Write(UniverseType*);
    void Read( UniverseType*);
    void Write(TimeScale*);
    void Read( TimeScale*);
    void Write(OrsaFileDataType*);
    void Read( OrsaFileDataType*);
    void Write(JPL_planets*);
    void Read( JPL_planets*);
    void Write(TimeStep*);
    void Read( TimeStep*);
    
  private:
    size_t read_swap(void *ptr, unsigned int size);
    
  private:
    unsigned int     byte_order;
    std::string      orsa_version;
    OrsaFileDataType last_ofdt_read;
    bool             swap_bytes;
  };
  
  //! orsa configuration file
  class OrsaConfigFile : public ReadWriteFile {
  public:
    OrsaConfigFile();
    // OrsaConfigFile(Config*);
    
    void Read();
    void Write();
    
  private:
    std::list<ConfigEnum> list_enum; 
    
    // private:
    // Config *conf;
  };
  
  // a std::string util, to be moved somewhere else in future
  inline void remove_leading_trailing_spaces(std::string &s) {
    
    const int first = s.find_first_not_of(" ");
    s.erase(0,first);
    
    const int last  = s.find_last_not_of(" ");
    s.erase(last+1,s.size());
  }
  
  /* 
     class OrsaPaths {
     public:
     OrsaPaths();
     static const char * work_path() { return path; }
     static char path_separator() { return _path_separator; }
     //
     static void set_path(char * new_path) { path = strdup(new_path); }
     private:
     static char * path;
     static char _path_separator;
     };
  */
  
  class OrsaPaths {
  public:
    OrsaPaths();
    OrsaPaths(const std::string &config_path); // set config path
    static const char * work_path() { return path; }
    static char path_separator() { return _path_separator; }
    //
    // static void set_path(char * new_path) { path = strdup(new_path); }
  private:
    void set_path_separator();
    void set_path();
    
  private:
    static char * path;
    static char _path_separator;
  };
  
  extern OrsaPaths *orsa_paths;
  
  //! This class is used to read TLE 
  //! (Two Lines Elements) files describing
  //! Earth's artificial satellites.
  class TLEFile : public ReadFile {
  public:
    TLEFile();
    void Read();
  public:
    inline virtual void read_progress(int) { };
  public:
    std::vector<BodyWithEpoch> sat;
  };
  
} // namespace orsa

#endif // _ORSA_FILE_H_
