/*
 * SPDX-License-Identifier: GPL-2.0-or-later
 * SPDX-FileCopyrightText: 2000 Curtis L. Olson - http://www.flightgear.org/~curt
 *
 * navradio.hxx -- class to manage a nav radio instance
 * Written by Curtis Olson, started April 2000.
 *
*/

#pragma once

#include <Navaids/navaids_fwd.hxx>
#include <Main/fg_props.hxx>

#include <simgear/compiler.h>
#include <simgear/timing/timestamp.hxx>

#include <Instrumentation/AbstractInstrument.hxx>

class SGSampleGroup;

class FGNavRadio : public AbstractInstrument,
                   public SGPropertyChangeListener
{
    SGPropertyNode_ptr _radio_node;

    // property inputs
    SGPropertyNode_ptr is_valid_node;   // is station data valid (may be way out
                                        // of range.)
    SGPropertyNode_ptr freq_node;       // primary freq
    SGPropertyNode_ptr alt_freq_node;   // standby freq
    SGPropertyNode_ptr is_loc_freq_node;// is the primary freq a loc/gs (paired) freq?
    SGPropertyNode_ptr sel_radial_node; // selected radial
    SGPropertyNode_ptr vol_btn_node;
    SGPropertyNode_ptr ident_btn_node;
    SGPropertyNode_ptr audio_btn_node;
    SGPropertyNode_ptr backcourse_node;
    SGPropertyNode_ptr nav_serviceable_node;
    SGPropertyNode_ptr cdi_serviceable_node;
    SGPropertyNode_ptr gs_serviceable_node;
    SGPropertyNode_ptr tofrom_serviceable_node;

    // property outputs
    SGPropertyNode_ptr fmt_freq_node;     // formatted frequency
    SGPropertyNode_ptr fmt_alt_freq_node; // formatted alternate frequency
    SGPropertyNode_ptr heading_node;      // true heading to nav station
    SGPropertyNode_ptr radial_node;       // current radial we are on (taking
                                       // into consideration the vor station
                                       // alignment which likely doesn't
                                       // match the magnetic alignment
                                       // exactly.)
    SGPropertyNode_ptr recip_radial_node; // radial_node(val) + 180 (for
                                       // convenience)
    SGPropertyNode_ptr target_radial_true_node;
                                       // true heading of selected radial
    SGPropertyNode_ptr target_auto_hdg_node;
                                       // suggested autopilot heading
                                       // to intercept selected radial
    SGPropertyNode_ptr time_to_intercept; // estimated time to intecept selected
                                       // radial at current speed and heading
    SGPropertyNode_ptr to_flag_node;
    SGPropertyNode_ptr from_flag_node;
    SGPropertyNode_ptr inrange_node;
    SGPropertyNode_ptr signal_quality_norm_node;
    SGPropertyNode_ptr cdi_deflection_node;
    SGPropertyNode_ptr cdi_deflection_norm_node;
    SGPropertyNode_ptr cdi_xtrack_error_node;
    SGPropertyNode_ptr cdi_xtrack_hdg_err_node;
    SGPropertyNode_ptr has_gs_node;
    SGPropertyNode_ptr loc_node;
    SGPropertyNode_ptr loc_dist_node;
    SGPropertyNode_ptr gs_deflection_node;
    SGPropertyNode_ptr gs_deflection_deg_node;
    SGPropertyNode_ptr gs_deflection_norm_node;
    SGPropertyNode_ptr gs_direct_node;
    SGPropertyNode_ptr gs_rate_of_climb_node;
    SGPropertyNode_ptr gs_rate_of_climb_fpm_node;
    SGPropertyNode_ptr gs_dist_node;
    SGPropertyNode_ptr gs_inrange_node;
    SGPropertyNode_ptr nav_id_node;
    SGPropertyNode_ptr id_c1_node;
    SGPropertyNode_ptr id_c2_node;
    SGPropertyNode_ptr id_c3_node;
    SGPropertyNode_ptr id_c4_node;

    // gps slaving support
    SGPropertyNode_ptr nav_slaved_to_gps_node;
    SGPropertyNode_ptr gps_cdi_deflection_node;
    SGPropertyNode_ptr gps_to_flag_node;
    SGPropertyNode_ptr gps_from_flag_node;
    SGPropertyNode_ptr gps_has_gs_node;
    SGPropertyNode_ptr gps_course_node;
    SGPropertyNode_ptr gps_xtrack_error_nm_node;
    SGPropertyNode_ptr _magvarNode;

    // realism setting, are false courses and GS lobes enabled?
    SGPropertyNode_ptr falseCoursesEnabledNode;

    // internal (private) values

    // When the selected frequency is changed or when leaving GPS slave mode,
    // the low-pass filter applied to signal quality must be disabled. Setting
    // this member to 'false' has one-shot behavior.
    bool _apply_lowpass_filter = false;
    int play_count;
    bool _nav_search;
    double _last_freq;
    FGNavRecordRef _navaid;
    FGNavRecordRef _gs;

    double target_radial;
    double effective_range;
    double target_gs;
    double twist;
    double horiz_vel;
    double last_x;
    double last_xtrack_error;
    double xrate_ms;
    double _localizerWidth; // cached localizer width in degrees

    // internal periodic station search timer
    double _time_before_search_sec;

    SGVec3d _gsCart, _gsAxis, _gsVertical, _gsBaseline;

    // CDI properties
    bool _toFlag, _fromFlag;
    double _cdiDeflection;
    double _cdiCrossTrackErrorM;
    double _gsNeedleDeflection;
    double _gsNeedleDeflectionNorm;
    double _gsDirect;

    class AudioIdent * _audioIdent;

    bool updateWithPower(double aDt);

    // model standard VOR/DME/TACAN service volumes as per AIM 1-1-8
    double adjustNavRange( double stationElev, double aircraftElev,
                           double nominalRange );

    // model standard ILS service volumes as per AIM 1-1-9
    double adjustILSRange( double stationElev, double aircraftElev,
                           double offsetDegrees, double distance );

    void updateAudio( double dt );

    void updateReceiver(double dt);
    void updateGlideSlope(double dt, const SGVec3d& aircraft, double signal_quality_norm);
    void updateGPSSlaved();
    void updateCDI(double dt);
    void updateFormattedFrequencies();

    void clearOutputs();

    FGNavRecord* findPrimaryNavaid(const SGGeod& aPos, double aFreqMHz);

    // implement SGPropertyChangeListener
    virtual void valueChanged (SGPropertyNode * prop);

public:
    FGNavRadio(SGPropertyNode *node);
    ~FGNavRadio();

    // Subsystem API.
    void init() override;
    void reinit() override;
    void update(double dt) override;

    // Subsystem identification.
    static const char* staticSubsystemClassId() { return "old-navradio"; }

    // Update nav/adf radios based on current position
    void search ();
    void updateNav();
};
