// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* 
// ** Copyright UCAR (c) 1990 - 2016                                         
// ** University Corporation for Atmospheric Research (UCAR)                 
// ** National Center for Atmospheric Research (NCAR)                        
// ** Boulder, Colorado, USA                                                 
// ** BSD licence applies - redistribution and use in source and binary      
// ** forms, with or without modification, are permitted provided that       
// ** the following conditions are met:                                      
// ** 1) If the software is modified to produce derivative works,            
// ** such modified software should be clearly marked, so as not             
// ** to confuse it with the version available from UCAR.                    
// ** 2) Redistributions of source code must retain the above copyright      
// ** notice, this list of conditions and the following disclaimer.          
// ** 3) Redistributions in binary form must reproduce the above copyright   
// ** notice, this list of conditions and the following disclaimer in the    
// ** documentation and/or other materials provided with the distribution.   
// ** 4) Neither the name of UCAR nor the names of its contributors,         
// ** if any, may be used to endorse or promote products derived from        
// ** this software without specific prior written permission.               
// ** DISCLAIMER: THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS  
// ** OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED      
// ** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.    
// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* 

#include <rapformats/UfRadar.hh>
#include <dataport/bigend.h>
#include <toolsa/DateTime.hh>
#include <toolsa/TaArray.hh>
using namespace std;

/////////////////////////////////////////////////////////////////
// printing

// print mandatory header

void UfRadar::print_mandatory_header(ostream &out,
				     const UF_mandatory_header_t &hdr)

{

  out << "===========================================================" << endl;
  out << "UF mandatory header:" << endl;
  out << "    record_length, bytes: " << hdr.record_length
      << ", " << (hdr.record_length) * sizeof(si16) << endl;
  out << "    optional_header_pos, byte: " << hdr.optional_header_pos
      << ", " << (hdr.optional_header_pos - 1) * sizeof(si16) << endl;
  out << "    local_use_header_pos, byte: " << hdr.local_use_header_pos
      << ", " << (hdr.local_use_header_pos - 1) * sizeof(si16) << endl;
  out << "    data_header_pos, byte: " << hdr.data_header_pos
      << ", " << (hdr.data_header_pos - 1) * sizeof(si16) << endl;
  out << "    record_num: " << hdr.record_num << endl;
  out << "    volume_scan_num: " << hdr.volume_scan_num << endl;
  out << "    ray_num: " << hdr.ray_num << endl;
  out << "    ray_record_num: " << hdr.ray_record_num << endl;
  out << "    sweep_num: " << hdr.sweep_num << endl;
  out << "    radar_name: "<< label(hdr.radar_name, 8) << endl;
  out << "    site_name: " << label(hdr.site_name, 8) << endl;
  out << "    lat_degrees: " << hdr.lat_degrees << endl;
  out << "    lat_minutes: " << hdr.lat_minutes << endl;
  out << "    lat_seconds: " << hdr.lat_seconds / 64.0 << endl;
  double lat = hdr.lat_degrees + hdr.lat_minutes / 60.0 + hdr.lat_seconds / (3600.0 * 64.0);
  out << "    lat: " << lat << endl;
  out << "    lon_degrees: " << hdr.lon_degrees << endl;
  out << "    lon_minutes: " << hdr.lon_minutes << endl;
  out << "    lon_seconds: " << hdr.lon_seconds / 64.0 << endl;
  double lon = hdr.lon_degrees + hdr.lon_minutes / 60.0 + hdr.lon_seconds / (3600.0 * 64.0);
  out << "    lon: " << lon << endl;
  out << "    antenna_height: " << hdr.antenna_height << endl;
  out << "    year: " << hdr.year << endl;
  out << "    month: " << hdr.month << endl;
  out << "    day: " << hdr.day << endl;
  out << "    hour: " << hdr.hour << endl;
  out << "    minute: " << hdr.minute << endl;
  out << "    second: " << hdr.second << endl;
  out << "    time_zone: " << label(hdr.time_zone, 2) << endl;
  out << "    azimuth: " << hdr.azimuth / 64.0 << endl;
  out << "    elevation: " << hdr.elevation / 64.0 << endl;
  out << "    sweep_mode: " << hdr.sweep_mode << endl;
  out << "    fixed_angle: " << hdr.fixed_angle / 64.0 << endl;
  out << "    sweep_rate: " << hdr.sweep_rate / 64.0 << endl;
  out << "    gen_year: " << hdr.gen_year << endl;
  out << "    gen_month: " << hdr.gen_month << endl;
  out << "    gen_day: " << hdr.gen_day << endl;
  out << "    gen_facility: " << label(hdr.gen_facility, 8) << endl;
  out << "    missing_data_val: " << hdr.missing_data_val << endl;
  out << "===========================================================" << endl;

}

// print data header

void UfRadar::print_data_header(ostream &out,
				const UF_data_header_t &hdr)

{

  out << "-----------------------------------------------------------" << endl;
  out << "UF data header:" << endl;
  out << "    num_ray_fields: " << hdr.num_ray_fields << endl;
  out << "    num_ray_records: " << hdr.num_ray_records << endl;
  out << "    num_record_fields: " << hdr.num_record_fields << endl;
  out << "-----------------------------------------------------------" << endl;

}

// print field info

void UfRadar::print_field_info(ostream &out,
			  int field_num,
			  const UF_field_info_t &info)

{

  out << "-----------------------------------------------------------" << endl;
  out << "UF field info: - field num " << field_num << endl;
  out << "    field_name: "<< label(info.field_name, 2) << endl;
  out << "    field_pos, byte: " << info.field_pos
      << ", " << (info.field_pos - 1) * sizeof(si16) << endl;
  out << "-----------------------------------------------------------" << endl;

}

// print field header

void UfRadar::print_field_header(ostream &out,
				 const string &field_name,
				 int field_num,
				 const UF_field_header_t &hdr)
  
{

  out << "-----------------------------------------------------------" << endl;
  out << "UF field header" << endl;
  out << "    field name " << field_name << endl;
  out << "    field num " << field_num << endl;
  out << "    data_pos, bytes: " << hdr.data_pos
      << ", " << (hdr.data_pos - 1) * sizeof(si16) << endl;
  out << "    scale_factor: " << hdr.scale_factor << endl;
  out << "    start_range: " << hdr.start_range << endl;
  out << "    start_center: " << hdr.start_center << endl;
  out << "    volume_spacing: " << hdr.volume_spacing << endl;
  out << "    num_volumes, bytes: " << hdr.num_volumes
      << ", " << hdr.num_volumes * sizeof(si16) << endl;
  out << "    volume_depth: " << hdr.volume_depth << endl;
  out << "    horiz_beam_width: " << hdr.horiz_beam_width / 64.0 << endl;
  out << "    vert_beam_width: " << hdr.vert_beam_width / 64.0 << endl;
  out << "    receiver_bandwidth: " << hdr.receiver_bandwidth << endl;
  out << "    polarization: " << hdr.polarization << endl;
  out << "    wavelength: " << hdr.wavelength / 64.0 << endl;
  out << "    num_samples: " << hdr.num_samples << endl;
  out << "    threshold_field: " << label(hdr.threshold_field, 2) << endl;
  out << "    threshold_val: " << hdr.threshold_val << endl;
  out << "    scale for power and noise: " << hdr.scale << endl;
  out << "    edit_code: " << label(hdr.edit_code, 2) << endl;
  out << "    pulse_rep_time: " << hdr.pulse_rep_time << endl;
  out << "    volume_bits: " << hdr.volume_bits << endl;
  if (field_name.c_str()[0] == 'V') {
    out << "    nyquist_vel: "
	<< hdr.word20.nyquist_vel / (double) hdr.scale_factor << endl;
    out << "    fl_string: " << label(hdr.word21.fl_string, 2) << endl;
  } else {
    out << "    radar_const: " << hdr.word20.radar_const << endl;
    out << "    noise_power: " << hdr.word21.noise_power / (double) hdr.scale << endl;
    out << "    receiver_gain: " << hdr.receiver_gain / (double) hdr.scale << endl;
    out << "    peak_power: " << hdr.peak_power / (double) hdr.scale << endl;
    out << "    antenna_gain: " << hdr.antenna_gain / (double) hdr.scale << endl;
    out << "    pulse_duration: " << hdr.pulse_duration / 64.0 << endl;
  }
  out << "-----------------------------------------------------------" << endl;

}

// print field data

void UfRadar::print_field_data(ostream &out,
			       const string &field_name,
			       int field_num,
			       int ngates,
			       double scale_factor,
			       int missing_val,
			       const si16 *data)

{
  
  out << "###########################################################" << endl;
  out << "UF field data:" << endl;
  out << "  field name " << field_name << endl;
  out << "  field num " << field_num << endl;
  out << "  scale factor " << scale_factor << endl;
  int nMiss = 0;
  for (int i = 0; i < ngates; i++) {
    if (data[i] == missing_val) {
      nMiss++;
    } else {
      if (nMiss > 0) {
	out << " " << nMiss << "*miss";
	nMiss = 0;
      }
      out << " " << data[i] / scale_factor;
    }
  }
  if (nMiss > 0) {
    out << " " << nMiss << "*miss";
  }
  out << endl;
  out << "###########################################################" << endl;

}

////////////////////////////////////////////////
// get a label - takes care of null termination

string UfRadar::label(const char *str, int maxLen)

{

  // null terminate

  TaArray<char> copy_;
  char *copy = copy_.alloc(maxLen + 1);
  memset(copy, 0, maxLen + 1);
  memcpy(copy, str, maxLen);
  
  // remove blanks

  int startPos = 0;
  for (int ii = 0; ii <= maxLen; ii++) {
    if (copy[ii] == ' ') {
      startPos++;
    } else {
      break;
    }
  }

  for (int ii = maxLen-1; ii >= 0; ii--) {
    if (copy[ii] == ' ') {
      copy[ii] = '\0';
    } else {
      break;
    }
  }

  return copy + startPos;

}

////////////////////////////////////////////////////////////////
// Byte swapping

// mandatory header to BigEndian

void UfRadar::BE_from_mandatory_header(UF_mandatory_header_t &hdr)
{

  BE_from_array_16(&hdr.record_length, sizeof(si16));
  BE_from_array_16(&hdr.optional_header_pos, sizeof(si16));
  BE_from_array_16(&hdr.local_use_header_pos, sizeof(si16));
  BE_from_array_16(&hdr.data_header_pos, sizeof(si16));
  BE_from_array_16(&hdr.record_num, sizeof(si16));
  BE_from_array_16(&hdr.volume_scan_num, sizeof(si16));
  BE_from_array_16(&hdr.ray_num, sizeof(si16));
  BE_from_array_16(&hdr.ray_record_num, sizeof(si16));
  BE_from_array_16(&hdr.sweep_num, sizeof(si16));
  BE_from_array_16(&hdr.lat_degrees, sizeof(si16));
  BE_from_array_16(&hdr.lat_minutes, sizeof(si16));
  BE_from_array_16(&hdr.lat_seconds, sizeof(si16));
  BE_from_array_16(&hdr.lon_degrees, sizeof(si16));
  BE_from_array_16(&hdr.lon_minutes, sizeof(si16));
  BE_from_array_16(&hdr.lon_seconds, sizeof(si16));
  BE_from_array_16(&hdr.antenna_height, sizeof(si16));
  BE_from_array_16(&hdr.year, sizeof(si16));
  BE_from_array_16(&hdr.month, sizeof(si16));
  BE_from_array_16(&hdr.day, sizeof(si16));
  BE_from_array_16(&hdr.hour, sizeof(si16));
  BE_from_array_16(&hdr.minute, sizeof(si16));
  BE_from_array_16(&hdr.second, sizeof(si16));
  BE_from_array_16(&hdr.azimuth, sizeof(si16));
  BE_from_array_16(&hdr.elevation, sizeof(si16));
  BE_from_array_16(&hdr.sweep_mode, sizeof(si16));
  BE_from_array_16(&hdr.fixed_angle, sizeof(si16));
  BE_from_array_16(&hdr.sweep_rate, sizeof(si16));
  BE_from_array_16(&hdr.gen_year, sizeof(si16));
  BE_from_array_16(&hdr.gen_month, sizeof(si16));
  BE_from_array_16(&hdr.gen_day, sizeof(si16));
  BE_from_array_16(&hdr.missing_data_val, sizeof(si16));

}

// BigEndian to mandatory header

void UfRadar::BE_to_mandatory_header(UF_mandatory_header_t &hdr)
{

  BE_to_array_16(&hdr.record_length, sizeof(si16));
  BE_to_array_16(&hdr.optional_header_pos, sizeof(si16));
  BE_to_array_16(&hdr.local_use_header_pos, sizeof(si16));
  BE_to_array_16(&hdr.data_header_pos, sizeof(si16));
  BE_to_array_16(&hdr.record_num, sizeof(si16));
  BE_to_array_16(&hdr.volume_scan_num, sizeof(si16));
  BE_to_array_16(&hdr.ray_num, sizeof(si16));
  BE_to_array_16(&hdr.ray_record_num, sizeof(si16));
  BE_to_array_16(&hdr.sweep_num, sizeof(si16));
  BE_to_array_16(&hdr.lat_degrees, sizeof(si16));
  BE_to_array_16(&hdr.lat_minutes, sizeof(si16));
  BE_to_array_16(&hdr.lat_seconds, sizeof(si16));
  BE_to_array_16(&hdr.lon_degrees, sizeof(si16));
  BE_to_array_16(&hdr.lon_minutes, sizeof(si16));
  BE_to_array_16(&hdr.lon_seconds, sizeof(si16));
  BE_to_array_16(&hdr.antenna_height, sizeof(si16));
  BE_to_array_16(&hdr.year, sizeof(si16));
  BE_to_array_16(&hdr.month, sizeof(si16));
  BE_to_array_16(&hdr.day, sizeof(si16));
  BE_to_array_16(&hdr.hour, sizeof(si16));
  BE_to_array_16(&hdr.minute, sizeof(si16));
  BE_to_array_16(&hdr.second, sizeof(si16));
  BE_to_array_16(&hdr.azimuth, sizeof(si16));
  BE_to_array_16(&hdr.elevation, sizeof(si16));
  BE_to_array_16(&hdr.sweep_mode, sizeof(si16));
  BE_to_array_16(&hdr.fixed_angle, sizeof(si16));
  BE_to_array_16(&hdr.sweep_rate, sizeof(si16));
  BE_to_array_16(&hdr.gen_year, sizeof(si16));
  BE_to_array_16(&hdr.gen_month, sizeof(si16));
  BE_to_array_16(&hdr.gen_day, sizeof(si16));
  BE_to_array_16(&hdr.missing_data_val, sizeof(si16));

}

// data header to BigEndian

void UfRadar::BE_from_data_header(UF_data_header_t &hdr)
{
  BE_from_array_16(&hdr.num_ray_fields, sizeof(si16));
  BE_from_array_16(&hdr.num_ray_records, sizeof(si16));
  BE_from_array_16(&hdr.num_record_fields, sizeof(si16));
}

// BigEndian to data header

void UfRadar::BE_to_data_header(UF_data_header_t &hdr)
{
  BE_to_array_16(&hdr.num_ray_fields, sizeof(si16));
  BE_to_array_16(&hdr.num_ray_records, sizeof(si16));
  BE_to_array_16(&hdr.num_record_fields, sizeof(si16));
}

// field info to BigEndian

void UfRadar::BE_from_field_info(UF_field_info_t &info)
{
  BE_from_array_16(&info.field_pos, sizeof(si16));
}

// BigEndian to field info

void UfRadar::BE_to_field_info(UF_field_info_t &info)
{
  BE_to_array_16(&info.field_pos, sizeof(si16));
}

// field header to BigEndian

void UfRadar::BE_from_field_header(UF_field_header_t &hdr, const string &field_name)
{
  BE_from_array_16(&hdr.data_pos, sizeof(si16));
  BE_from_array_16(&hdr.scale_factor, sizeof(si16));
  BE_from_array_16(&hdr.start_range, sizeof(si16));
  BE_from_array_16(&hdr.start_center, sizeof(si16));
  BE_from_array_16(&hdr.volume_spacing, sizeof(si16));
  BE_from_array_16(&hdr.num_volumes, sizeof(si16));
  BE_from_array_16(&hdr.volume_depth, sizeof(si16));
  BE_from_array_16(&hdr.horiz_beam_width, sizeof(si16));
  BE_from_array_16(&hdr.vert_beam_width, sizeof(si16));
  BE_from_array_16(&hdr.receiver_bandwidth, sizeof(si16));
  BE_from_array_16(&hdr.polarization, sizeof(si16));
  BE_from_array_16(&hdr.wavelength, sizeof(si16));
  BE_from_array_16(&hdr.num_samples, sizeof(si16));
  BE_from_array_16(&hdr.threshold_val, sizeof(si16));
  BE_from_array_16(&hdr.scale, sizeof(si16));
  BE_from_array_16(&hdr.pulse_rep_time, sizeof(si16));
  BE_from_array_16(&hdr.volume_bits, sizeof(si16));
  if (field_name.c_str()[0] == 'V') {
    BE_from_array_16(&hdr.word20.nyquist_vel, sizeof(si16));
  } else {
    BE_from_array_16(&hdr.word20.radar_const, sizeof(si16));
    BE_from_array_16(&hdr.word21.noise_power, sizeof(si16));
  }
  BE_from_array_16(&hdr.receiver_gain, sizeof(si16));
  BE_from_array_16(&hdr.peak_power, sizeof(si16));
  BE_from_array_16(&hdr.antenna_gain, sizeof(si16));
  BE_from_array_16(&hdr.pulse_duration, sizeof(si16));
}

// BigEndian to field header

void UfRadar::BE_to_field_header(UF_field_header_t &hdr, const string &field_name)
{
  BE_to_array_16(&hdr.data_pos, sizeof(si16));
  BE_to_array_16(&hdr.scale_factor, sizeof(si16));
  BE_to_array_16(&hdr.start_range, sizeof(si16));
  BE_to_array_16(&hdr.start_center, sizeof(si16));
  BE_to_array_16(&hdr.volume_spacing, sizeof(si16));
  BE_to_array_16(&hdr.num_volumes, sizeof(si16));
  BE_to_array_16(&hdr.volume_depth, sizeof(si16));
  BE_to_array_16(&hdr.horiz_beam_width, sizeof(si16));
  BE_to_array_16(&hdr.vert_beam_width, sizeof(si16));
  BE_to_array_16(&hdr.receiver_bandwidth, sizeof(si16));
  BE_to_array_16(&hdr.polarization, sizeof(si16));
  BE_to_array_16(&hdr.wavelength, sizeof(si16));
  BE_to_array_16(&hdr.num_samples, sizeof(si16));
  BE_to_array_16(&hdr.threshold_val, sizeof(si16));
  BE_to_array_16(&hdr.scale, sizeof(si16));
  BE_to_array_16(&hdr.pulse_rep_time, sizeof(si16));
  BE_to_array_16(&hdr.volume_bits, sizeof(si16));
  if (field_name.c_str()[0] == 'V') {
    BE_to_array_16(&hdr.word20.nyquist_vel, sizeof(si16));
  } else {
    BE_to_array_16(&hdr.word20.radar_const, sizeof(si16));
    BE_to_array_16(&hdr.word21.noise_power, sizeof(si16));
  }
  BE_to_array_16(&hdr.receiver_gain, sizeof(si16));
  BE_to_array_16(&hdr.peak_power, sizeof(si16));
  BE_to_array_16(&hdr.antenna_gain, sizeof(si16));
  BE_to_array_16(&hdr.pulse_duration, sizeof(si16));
}