/**
 * @file TransceiverBase.cpp
 * @brief Implementation of the TransceiverBase class
 * Implements the Transceiver abstract  interface as template methods and
 * provides  a new abstract interface  with similar functionality
 */

#include "TransceiverBase.h"

#include <QDebug>
#include <QString>
#include <QThread>
#include <QTimer>

#include <exception>

#include "moc_TransceiverBase.cpp"

namespace {
auto const unexpected = TransceiverBase::tr("Unexpected rig error");
}

void TransceiverBase::start(unsigned sequence_number) noexcept {
    QString message;
    try {
        may_update u{this, true};
        shutdown();
        startup();
        last_sequence_number_ = sequence_number;
    } catch (std::exception const &e) {
        message = e.what();
    } catch (...) {
        message = unexpected;
    }
    if (!message.isEmpty()) {
        offline(message);
    }
}

void TransceiverBase::set(TransceiverState const &s,
                          unsigned sequence_number) noexcept {
    TRACE_CAT("TransceiverBase", "#:" << sequence_number << s);

    QString message;
    try {
        may_update u{this, true};
        bool was_online{requested_.online()};
        if (!s.online() && was_online) {
            shutdown();
        } else if (s.online() && !was_online) {
            shutdown();
            startup();
        }
        if (requested_.online()) {
            bool ptt_on{false};
            bool ptt_off{false};
            if (s.ptt() != requested_.ptt()) {
                ptt_on = s.ptt();
                ptt_off = !s.ptt();
            }
            if (ptt_off) {
                do_ptt(false);
                do_post_ptt(false);
                QThread::msleep(100); // some rigs cannot process CAT
                                      // commands while switching from
                                      // Tx to Rx
            }
            if (s.frequency() // ignore bogus zero frequencies
                && ((s.frequency() != requested_.frequency() // and QSY
                     || (s.mode() != UNK &&
                         s.mode() != requested_.mode())) // or mode change
                    || ptt_off)) // or just returned to rx
            {
                do_frequency(s.frequency(), s.mode(), ptt_off);
                do_post_frequency(s.frequency(), s.mode());

                // record what actually changed
                requested_.frequency(actual_.frequency());
                requested_.mode(actual_.mode());
            }
            if (!s.tx_frequency() ||
                (s.tx_frequency() > 10000 // ignore bogus startup values
                 && s.tx_frequency() <
                        std::numeric_limits<Frequency>::max() - 10000)) {
                if ((s.tx_frequency() != requested_.tx_frequency() // and QSY
                     || (s.mode() != UNK &&
                         s.mode() != requested_.mode())) // or mode change
                    // || s.split () != requested_.split ())) // or split change
                    || (s.tx_frequency() && ptt_on)) // or about to tx split
                {
                    hamlib_bug_bandaid(s);
                    do_tx_frequency(s.tx_frequency(), s.mode(), ptt_on);
                    do_post_tx_frequency(s.tx_frequency(), s.mode());

                    // record what actually changed
                    requested_.tx_frequency(actual_.tx_frequency());
                    requested_.split(actual_.split());
                }
            }
            if (ptt_on) {
                do_ptt(true);
                do_post_ptt(true);
                QThread::msleep(100); // some rigs cannot process CAT
                                      // commands while switching from
                                      // Rx to Tx
            }

            // record what actually changed
            requested_.ptt(actual_.ptt());
        }
        last_sequence_number_ = sequence_number;
    } catch (std::exception const &e) {
        message = e.what();
    } catch (...) {
        message = unexpected;
    }
    if (!message.isEmpty()) {
        offline(message);
    }
}

void TransceiverBase::startup() {
    Q_EMIT resolution(do_start());
    do_post_start();
    actual_.online(true);
    requested_.online(true);
}

void TransceiverBase::shutdown() {
    may_update u{this};
    if (requested_.online()) {
        try {
            // try and ensure PTT isn't left set
            do_ptt(false);
            do_post_ptt(false);
            if (requested_.split()) {
                // try and reset split mode
                do_tx_frequency(0, UNK, true);
                do_post_tx_frequency(0, UNK);
            }
        } catch (...) {
            // don't care about exceptions
        }
    }
    do_stop();
    do_post_stop();
    actual_.online(false);
    requested_.online(false);
}

void TransceiverBase::stop() noexcept {
    QString message;
    try {
        shutdown();
    } catch (std::exception const &e) {
        message = e.what();
    } catch (...) {
        message = unexpected;
    }
    if (!message.isEmpty()) {
        offline(message);
    } else {
        Q_EMIT finished();
    }
}

void TransceiverBase::update_rx_frequency(Frequency rx) {
    actual_.frequency(rx);
    requested_.frequency(rx); // track rig changes
}

void TransceiverBase::update_other_frequency(Frequency tx) {
    actual_.tx_frequency(tx);
}

void TransceiverBase::update_split(bool state) { actual_.split(state); }

void TransceiverBase::update_mode(MODE m) {
    actual_.mode(m);
    requested_.mode(m); // track rig changes
}

void TransceiverBase::update_PTT(bool state) { actual_.ptt(state); }

void TransceiverBase::update_complete(bool force_signal) {
    if ((do_pre_update() && actual_ != last_) || force_signal) {
        Q_EMIT update(actual_, last_sequence_number_);
        last_ = actual_;
    }
}

void TransceiverBase::offline(QString const &reason) {
    Q_EMIT failure(reason);
    try {
        shutdown();
    } catch (...) {
        // don't care
    }
}

Q_LOGGING_CATEGORY(transceiverbase_js8, "transceiverbase.js8", QtWarningMsg)
