// C++ code Copyright (C) David R. Evans G4AMJ/NQ0I

#include <bdr.h>
#include <edr.h>
#include <gmdr.h>

#include <strstream.h>

const int MAX_BROWSE_REC_LEN = 3000;

//  ************************  bdr_record  *****************************
// constructor
bdr_record::bdr_record(void)
{ _start_channel = _end_channel = 0;
}

// ASCII read
void bdr_record::operator=(char* buffer)
{ int old_n_channels = n_channels();

// Argh! Borland messes up if we pass the length parameter
  istrstream input(buffer);
  int year, day, hour, minute, second, _spacecraft, _spacecraft_mode;
  input >> year >> day >> hour >> minute >> second
        >> _spacecraft >> _spacecraft_mode >> _start_channel
        >> _end_channel;
  _time = timeclass(year, day, hour, minute, second);
  _sc = (byte)_spacecraft;
  _sc_mode = (byte)_spacecraft_mode;
  if (old_n_channels != n_channels()) then
  { destroy_array((int16*)_int16_record);
    heap_check(_int16_record = new int16 [2 * n_channels()]);
  }
  for (int n = 0; n < n_channels(); n++)
    input >> _int16_record[2 * n] >> _int16_record[2 * n + 1];
}

// binary read
void bdr_record::operator=(FILE* fp)
{ const int old_n_channels = n_channels();
  int16 year, day, hour, minute, second;

  read_binary(fp, year);
  read_binary(fp, day);
  read_binary(fp, hour);
  read_binary(fp, minute);
  read_binary(fp, second);

  _time = timeclass(year, day, hour, minute, second);
  
// Originally, _sc was an octet (as it should be). However, in 
// producing the CD-ROM, PDS converted it to an int16.
  int16 temp_sc;
  read_binary(fp, temp_sc);
  _sc = (byte)temp_sc;
  
  read_binary(fp, _sc_mode);
  read_binary(fp, _start_channel);
  read_binary(fp, _end_channel);
  if (old_n_channels != n_channels()) then
  { destroy_array((int16*)_int16_record);
    heap_check(_int16_record = new int16 [2 * n_channels()]);  
  }

// read each datum individually to permit easy byte inversion when
// necessary
  for (int n = 0; n < 2; n++)
    for (int channel = 0; channel < n_channels(); channel++)
    { uint16 temp_datum;
      read_binary(fp, temp_datum);
      _int16_record[n * n_channels() + channel] = temp_datum;
//     fread (&(_int16_record[n * n_channels()]), sizeof(_int16_record[0]),
//           n_channels(), fp);
    }
}

int16 bdr_record::datum(const int s, const int n) const
{ boolean error = false;
  error = (s < 0 || s > 2);
  error = error || (n < min_valid_channel_nr());
  error = error || (n > max_valid_channel_nr());
  if (error) then
    fatal_error("Invalid parameter in bdr_record::datum(const int, const int)");

  const int nr = (s - 1) * (max_valid_channel_nr() - min_valid_channel_nr() + 1) + (n - min_valid_channel_nr());
  
  return _int16_record[nr];
}

// ************************  bdr_file  ****************************
// constructor
bdr_file::bdr_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 int32* [n_thousands]);
    for (int n = 0; n < n_thousands; n++)
      heap_check(start_of_record_index[n] = new int32 [1000]);

    START(fp);    // go to beginning of file
    char buffer [MAX_BROWSE_REC_LEN];
    for (n = 0; n < file_size; n++)
    { start_of_record_index[n / 1000][n % 1000] = ftell(fp);
      fgets(buffer, MAX_BROWSE_REC_LEN, fp);
    }
  }
  else
  { warning("Unable to open file in bdr_file constructor");
  }
}

// destructor
bdr_file::~bdr_file(void)
{ if (file_size) then
  { for (int n = 0; n < file_size / 1000 + 1; n++)
      destroy_array(start_of_record_index[n]);
    destroy_array(start_of_record_index);
  }
}

int32 bdr_file::_get_file_size(void)
{ // we can be very dirty with this; just count the newlines!
  START(fp);    // go to beginning of file
  int records_so_far = 0;
  char buffer[MAX_BROWSE_REC_LEN];
  while (fgets(buffer, MAX_BROWSE_REC_LEN, fp))
    records_so_far++;
  return records_so_far;
}

void bdr_file::read(bdr_record& bdr, 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);
  char buffer[MAX_BROWSE_REC_LEN];
  bdr = fgets(buffer, MAX_BROWSE_REC_LEN, fp);
}

void bdr_file::read(edr_record& edr, const int rec_nr)
{ 
  static bdr_record bdr;
  read(bdr, rec_nr);
  edr = bdr;
}

// read a GMDR
void bdr_file::read(GMDR& gmdr, const int rec_nr)
{ static bdr_record bdr;
  read(bdr, rec_nr);
  gmdr = bdr;
}

uint32 bdr_file::size(void)
{ if (recent_write) then 
    file_size = _get_file_size();
  return file_size;
}

// ************************  bbdr_file  ***************************
// constructor
bbdr_file::bbdr_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
    heap_check(start_of_record_index = new int32 [file_size]);
    START(fp);    // go to beginning of file
    for (int n = 0; n < file_size; n++)
    { start_of_record_index[n] = ftell(fp);
      fseek(fp, 14, 1);
      int16 start_channel, end_channel;
  
      read_binary(fp, start_channel);
      read_binary(fp, end_channel);
// skip ahead to start of next record
      fseek(fp,
            sizeof(int16) * 2 * (end_channel - start_channel + 1),
            1);
    }
  }
}

int32 bbdr_file::_get_file_size(void)
{ int records_so_far = 0;
  int32 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, 14, 1);
    int16 start_channel, end_channel;
  
    read_binary(fp, start_channel);
    read_binary(fp, end_channel);


// skip ahead to start of next record
    fseek(fp, sizeof(int16) * 2 * (end_channel - start_channel + 1), 1);
    records_so_far++;
  }
  return records_so_far;
}

void bbdr_file::read(edr_record& edr, const int rec_nr)
{ static bdr_record bdr;
  read(bdr, rec_nr);
  edr = bdr;
}

// read a GMDR
void bbdr_file::read(GMDR& gmdr, const int rec_nr)
{ static bdr_record bdr;
  read(bdr, rec_nr);
  gmdr = bdr;
}

void bbdr_file::read(bdr_record& bdr, const int rec_nr)
{ // go to the right place (if necessary)
  if (rec_nr != -1) then
    fseek(fp, start_of_record_index[rec_nr], 0);
  bdr = fp;
}

uint32 bbdr_file::size(void)
{ if (recent_write) then 
    file_size = _get_file_size();
  return file_size;
}

