// C++ code Copyright (C) David R. Evans G4AMJ/NQ0I

#ifndef EDRH
#define EDRH

#include <data.h>
#include <t_array.h>

// edr classes

#define HIGHEST_HIGH_BAND_CHANNEL	130
#define	LONGEST_RECORD_LENGTH		3860

const int N_PRA_MODES = 7;

class edr_file;

// Miscellaneous routines which do not require an edr_record object

inline boolean high_band(int ch)
  { return (ch <= HIGHEST_HIGH_BAND_CHANNEL); }
inline boolean low_band(int ch)
  { return !high_band(ch); }

// ****************************  edr_record  ***************************

extern const int edr_duration(const int);

class bdr_record;

class edr_record : public data_record
{ friend class science_edr_record;

  friend class edr_file;
  friend class const_edr_file;
  
protected:
// certain functions are always available, not all of which are
// accessible to the outside world. (NB "reveal" is misleading here)

  reveal(int, _hour, ((_record[24] << 8) + _record[25]));
  reveal(int, _second, ((_record[26] << 8) + _record[27]));
  
public:

// default constructor
  edr_record(void);

// copy constructor
  edr_record(const edr_record&);

// A note; I have experimented with several ways to try to use both
// edr_record and bdr_record objects consistently within a program.
// I can find no way (even deriving from a common base) that is not
// an rather gross kludge. The leanest mechanism is simply to provide
// for casting from bdr_record to edr_record. This is also a kludge,
// if only because the polarisation information is lost in the process,
// but at least all the kludges are pretty much confined to the casting
// process, rather than being spread throughout the application program.
  edr_record(const bdr_record&);

  virtual ~edr_record(void) { }
  void operator=(const bdr_record&);
  void operator=(const edr_record &);
      
  inline byte record(const int n) const 
    { return datum(n); }

// input
  void operator=(edr_file&);

// output
  int write(edr_file&);

// several functions work for all (non-unknown) types of edr record
// because there is a common "standard" header.

// year
  reveal(int, year, _record[30]);

// day (Jan 1 == 1); Page C-2 of 618-306 demonstrates that their "hour
// of the year" is actually "hour of year + 24". That is, _hour() can
// never return a value less than 24. (And if we believe that....)
  reveal(int, day, _hour() / 24);
  reveal(int, hour, _hour() % 24); 
  reveal(int, minute, _second() / 60);
  reveal(int, second, _second() % 60);
  reveal(int, sc_mode, _record[6]);
  reveal(byte, sc, 2 - (_record[3] & 1));
  
// type of the record -- this can be handy
  virtual edr_record_type record_type(void) const;

// name of record type
  char* record_type_name(void) const;
  
// return an individual byte
  inline byte datum(const int n) const
    { return _record[n]; }

  reveal(const char *, sc_mode_name, _sc_mode_name[sc_mode()]);
  
// some useful virtual functions
  virtual inline int n_sweeps(void) const 
    { return 1; }

  inline const int duration(void) const
    { return edr_duration(sc_mode()); }
 
};

// derived classes

// ***************************  science_edr_record  ********************

extern n_statuses_per_edr(const int);

class science_edr_record : public edr_record
{
public:
  science_edr_record(const edr_record&);
  science_edr_record(const science_edr_record&);
  
  inline science_edr_record(void) : edr_record() { }
  ~science_edr_record(void) { }
  void operator=(const edr_record&);
  inline edr_record_type record_type(void) const
    { return science; }

// there are lots of special science-only functions. For almost all of
// these, things go awry once we enter VIM, as there is no longer any
// real correspondence between status words and sweeps. Therefore, if
// we are in VIM, then reasonable defaults are returned without regard
// to status words.

  int n_sweeps(void) const;

// return rhcupper of a sweep [1--8]
  int rhcupper(const int s) const;

// return ad from lower of a sweep [1--8]
  int ad_from_lower(const int s) const;

// return pra_submode of a sweep [1--8]
  int pra_submode(const int s) const;

// return name of pra submode in a sweep
  const char* pra_submode_name(const int s) const;

// return pra_mode of a sweep [1--8]
  int pra_mode(const int s) const;
  
// datum(sweep [1--8], channel [1--200])
  int datum(const int s, const int c) const;

// return status of a sweep [1--8]
  int status(const int s) const;

// return por counter for a sweep [1--8]
  int por_counter(const int s = 1) const;

// return total attenuation [1--8] (in dB)
  int attenuation(const int s = 1) const;

// return the eight bits of subheader frequency information [1--8]
  int frequency(const int s) const;

// return a complete scan -- passed address must be able to hold it!
  void scan(int* d, const int s = 1) const;
  
// how long is a scan (in octets)
  int scan_octets(void) const;
  
// return mode command (from sub header)
  inline int mode_command(void) const
    { return ((_record[240] << 8) + _record[241]); }

// return configuration_command (from sub header)
  inline int configuration_command(void) const
    { return ((_record[242] << 8) + _record[243]); }

// which channel number is lowest with real data?
  int min_valid_channel_nr(void) const;

// which channel number is highest with real data?
  int max_valid_channel_nr(void) const;

// through which physical channel did a datum signal pass?
  int channel(const int sweep, const int channel) const;

// was the command to toggle channels? Can't use status word because
// that is overwritten by the POR counter
  inline int configuration_channel_toggle_disable(void) const
    { return ((configuration_command() & 0x0800) >> 11); }

// what is the polarisation of a datum?
  int polarisation(const int s, const int c) const;

// duration of a scan, in seconds
  int sweep_duration(void) const;
  
// is this a VIM mode edr?
  inline boolean vim(void) const
    { return ((sc_mode() == cr5a) || (sc_mode() == uv5a)); }

// frequency of a given GS-8 scan (times 10)
  uint32 fixlo_frequency(const int n) const;
  
// number of statuses
  inline int n_statuses(void) const
    { return n_statuses_per_edr(sc_mode()); }

// is a particular sweep fixlo?
  inline boolean fixlo(const int s) const
    { return ((pra_submode(s) == fixlol) || (pra_submode(s) == fixloh));
    }
};

// ********************  engineering_edr_record  *************

class engineering_edr_record : public edr_record
{
public:

  inline edr_record_type record_type(void) const 
    { return engineering; }
};


// ********************  decom_edr_record  *************

class decom_edr_record : public edr_record
{
public:

  inline edr_record_type record_type(void) const 
    { return decom; }
};


// ********************  unknown_edr_record  *************

class unknown_edr_record : public edr_record
{
public:

  inline edr_record_type record_type(void) const 
    { return unknown; }
};

// ************************  edr_file  ****************************
class edr_file : public data_file
{ friend class edr_record;
  enum { decom_map_length = 716 } ;

protected:
  boolean previously_open;

//  big_array<uint32> record_posn;
  BIG_ARRAY(uint32) record_posn;
  
  static int record_size[N_SC_MODES];

  int32 _get_file_size(void);
  virtual int _read(byte* record_buffer);
  int _write(const byte* record_buffer);
  void _initialise_static(void);

public:
  edr_file(FILE*);
  edr_file(DREstring filename, const char* mode = "rb" );
  virtual ~edr_file(void);

  boolean magic_number_OK(void);
  virtual void read(GMDR&, const int rec_nr = -1);
  virtual void read(edr_record&, const int rec_nr = -1);
  virtual int32 seek(const int rec_nr);		// wrt 0
  virtual uint32 size(void);
};

// ****************************  const_edr_file  ********************
// edr class with constant-length records
class const_edr_file : public edr_file
{ int record_length;
  int _read(byte* record_buffer);

public:
  const_edr_file(FILE*, const int rec_size = 1872);
  const_edr_file(DREstring& filename, const char* mode = "rb",
                 const int rec_size = 1872);
  int32 seek(const int rec_nr);			// wrt 0
  uint32 size(void);
  void read(GMDR&, const int rec_nr = -1);
  void read(edr_record&, const int rec_nr = -1);
};

#endif




