// C++ code Copyright (C) David R. Evans G4AMJ/NQ0I

#include <edr.h>
#include <gmdr.h>
#include <pdsdr.h>

#include <strstream.h>

const int MAX_PDSDR_REC_LEN = 3000;  // an arbitrary, big number

//  ************************  pdsdr_record  ****************************

// constructor
pdsdr_record::pdsdr_record(const science_edr_record& sedr, const int n)
 :
                           _format(1),
                           _record_duration(sedr.sweep_duration()),
                           _fixlo_frequency(0)
{ _time = timeclass(sedr.year(), sedr.day(), sedr.hour(), sedr.minute(),
                    sedr.second());
  _time += timeclass(0, 0, 0, 0, _record_duration * (n - 1));
  _sc = sedr.sc();
  _sc_mode = sedr.sc_mode();
  _pra_submode = sedr.pra_submode(n);
  _pra_mode = sedr.pra_mode(n);
  _toggle = !(sedr.configuration_channel_toggle_disable());
  _pol3 = 'L';
  if ((sedr.rhcupper(n)) && !(sedr.ad_from_lower(n))) then
    _pol3 = 'R';
  if (!(sedr.rhcupper(n)) && (sedr.ad_from_lower(n))) then
    _pol3 = 'R';
  _attenuation = sedr.attenuation();
  _por_counter = sedr.por_counter(n);
  _sc_mode_command = sedr.mode_command();
  _sc_configuration_command = sedr.configuration_command();
  _frequency = sedr.frequency(n);
  if (_sc_mode == gs8) then
    _fixlo_frequency = sedr.fixlo_frequency(n);  

// in this version, we assume that we are dealing with ordinary, old
// style data

  _n_bytes = 198;

// fill the data array
  destroy_array(_record);
  destroy_array(_int16_record);

  heap_check(_int16_record = new int16 [198]);
  for (int datum_nr = 0; datum_nr < 198; datum_nr++)
    _int16_record[datum_nr] = sedr.datum(n, datum_nr + 3);
  _record_length = 198; 
}

// do a binary read
void pdsdr_record::read(FILE* infile)
{ byte b1;
  int16 b2;
 
  read_binary(infile, b1);
  _time = timeclass(b1);
  read_binary(infile, b2);
  _time += timeclass(0, b2);
  read_binary(infile, b1);
  _time += timeclass(0, 0, b1);
  read_binary(infile, b1);
  _time += timeclass(0, 0, 0, b1);
  read_binary(infile, b1);
  _time += timeclass(0, 0, 0, 0, b1);
  read_binary(infile, _format);
  read_binary(infile, _sc);
  read_binary(infile, _sc_mode);
  read_binary(infile, _pra_mode);
  read_binary(infile, _pra_submode);
  read_binary(infile, _pol3);
  read_binary(infile, _toggle);
  read_binary(infile, _por_counter);
  read_binary(infile, _attenuation);
  read_binary(infile, _sc_mode_command);
  read_binary(infile, _sc_configuration_command);
  read_binary(infile, _frequency);
  read_binary(infile, _fixlo_frequency);
  read_binary(infile, _record_duration);
  read_binary(infile, _n_bytes);
 
// the data, externally bytes, are kept as int16s internally
  destroy_array(_record);
  destroy_array(_int16_record);

  heap_check(_int16_record = new int16 [_n_bytes]);
  for (int n = 0; n < _n_bytes; n++)
  { read_binary(infile, b1);
    _int16_record[n] = b1;
  }
}
  
// do a binary write
void pdsdr_record::write(FILE* outfile)
{ write_binary(outfile, (byte)year());
  write_binary(outfile, (int16)day());
  write_binary(outfile, (byte)hour());
  write_binary(outfile, (byte)minute());
  write_binary(outfile, (byte)second());
  write_binary(outfile, _format);
  write_binary(outfile, _sc);
  write_binary(outfile, _sc_mode);
  write_binary(outfile, _pra_mode);
  write_binary(outfile, _pra_submode);
  write_binary(outfile, _pol3);
  write_binary(outfile, _toggle);
  write_binary(outfile, _por_counter);
  write_binary(outfile, _attenuation);
  write_binary(outfile, _sc_mode_command);
  write_binary(outfile, _sc_configuration_command);
  write_binary(outfile, _frequency);
  write_binary(outfile, _fixlo_frequency);
  write_binary(outfile, _record_duration);
  write_binary(outfile, _n_bytes);
  
  for (int n = 0; n < 198; n++)
    write_binary(outfile, (byte)_int16_record[n]);
}

// ASCII read
void pdsdr_record::operator=(char* buffer)
{ 
// Argh! Borland messes up if we pass the length parameter
  istrstream input(buffer);
  byte b;
  int year, day, hour, minute, second, spacecraft;
  input >> year >> day >> hour >> minute >> second
        >> _format >> spacecraft >> _sc_mode 
        >> _pra_mode >> _pra_submode  >> b
        >> _toggle >> _por_counter >> _attenuation
        >> _sc_mode_command >> _sc_configuration_command
        >> _frequency  >> _fixlo_frequency >> _n_bytes;
  _sc = spacecraft;
  if (_int16_record)
    delete [] _int16_record;
  heap_check(_int16_record = new int16 [_n_bytes]);
  for (int n = 0; n < _n_bytes; n++)
             input >> _int16_record[n];
  _time = timeclass(year, day, hour, minute, second);
}

// write to a stream
ostream& operator<<(ostream& ost, const pdsdr_record& pdr)
{ ost << right_fill(DREstring10(pdr.year()), 2)
  << left_fill(DREstring10(pdr.day()), 4)
  << left_fill(DREstring10(pdr.hour()), 3)
  << left_fill(DREstring10(pdr.minute()), 3)
  << left_fill(DREstring10(pdr.second()), 3)
  << left_fill(DREstring10(pdr.format()), 3)
  << left_fill(DREstring10(pdr.sc()), 2)
  << left_fill(DREstring10(pdr.sc_mode()), 3)
  << left_fill(DREstring10(pdr.pra_mode()), 3)
  << left_fill(DREstring10(pdr.pra_submode()), 3)
  << ' ' << pdr.pol3()
  << left_fill(DREstring10(pdr.toggle()), 2)
  << left_fill(DREstring10(pdr.por_counter()), 2)
  << left_fill(DREstring10(pdr.attenuation()), 3)
  << left_fill(DREstring10(pdr.sc_mode_command()), 6)
  << left_fill(DREstring10(pdr.sc_configuration_command()), 6)
  << left_fill(DREstring10(pdr.frequency()), 4)
  << left_fill(DREstring10(pdr.fixlo_frequency()), 7)
  << left_fill(DREstring10(pdr.record_duration()), 5)
  << left_fill(DREstring10(pdr.n_bytes()), 5);
  
  for (int n = 0; n < 198; n++)
    ost << left_fill(DREstring10(pdr.datum(n)), 4);
  
  ost << endl;
  
  return ost;
}

// ************************  pdsdr_file  ****************************
// constructor
pdsdr_file::pdsdr_file(DREstring& file_name, const char* mode) : start_of_record_index(0)
{ if (fp = fopen(file_name, mode)) then
  { recent_write = false;
    file_size = _get_file_size();

// file the cache of record pointers
    const int n_thousands = file_size / 1000 + 1;
    heap_check(start_of_record_index = new uint32* [n_thousands]);
    for (int n = 0; n < n_thousands; n++)
      heap_check(start_of_record_index[n] = new uint32 [1000]);

    START(fp);    // go to beginning of file
    for (n = 0; n < file_size; n++)
    { start_of_record_index[n / 1000][n % 1000] = ftell(fp);
      fseek(fp, 26, 1);
      int16 n_bytes;
  
      read_binary(fp, n_bytes);

// skip ahead to start of next record
      fseek(fp, n_bytes, 1);
    }
  }
  else
  { warning("Unable to open file in pdsdr_file constructor");
  }
}

// get the number of records in a file
uint32 pdsdr_file::_get_file_size(void)
{ uint32 records_so_far = 0,
         file_size_in_bytes = (END(fp), ftell(fp));

  START(fp);    // go to beginning of file
    
// move ahead to where the record size can be calculated
  while (ftell(fp) < file_size_in_bytes)    // feof(fp) is unreliable
  { fseek(fp, 26, 1);
    int16 n_bytes;
  
    read_binary(fp, n_bytes);

// skip ahead to start of next record
    fseek(fp, n_bytes, 1);
    records_so_far++;
  }
  file_size = records_so_far;
  return records_so_far;
}

uint32 pdsdr_file::size(void)
{ if (file_size) then
    return file_size;
  else
    return (_get_file_size());
}

// read a GMDR
void pdsdr_file::read(GMDR& gmdr, const int rec_nr)
{ pdsdr_record pdsdr;
  read(pdsdr, rec_nr);
  gmdr = pdsdr;
}

// read a pdsdr
void pdsdr_file::read(pdsdr_record& pdsdr, const int rec_nr)
{ // go to the right place (if necessary)
  if (rec_nr != -1) then
    fseek(fp, start_of_record_index[rec_nr / 1000][rec_nr % 1000], 0);
  pdsdr.read(fp);
}
