#  NanoVNASaver
#
#  A python program to view and export Touchstone data from a NanoVNA
#  Copyright (C) 2019, 2020  Rune B. Broberg
#  Copyright (C) 2020,2021 NanoVNA-Saver Authors
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
import logging
import math
from typing import Dict, List

from PyQt5 import QtWidgets

import NanoVNASaver.AnalyticTools as at
from NanoVNASaver.Analysis.Base import Analysis, CUTOFF_VALS
from NanoVNASaver.Formatting import format_frequency

logger = logging.getLogger(__name__)


class HighPassAnalysis(Analysis):
    def __init__(self, app):
        super().__init__(app)

        self.label["octave"] = QtWidgets.QLabel()
        self.label["decade"] = QtWidgets.QLabel()
        for attn in CUTOFF_VALS:
            self.label[f"{attn:.1f}dB"] = QtWidgets.QLabel()
            self.label[f"{attn:.1f}dB"] = QtWidgets.QLabel()

        layout = self.layout
        layout.addRow(self.label["titel"])
        layout.addRow(QtWidgets.QLabel(
            f"Please place {self.app.markers[0].name}"
            f" in the filter passband."))
        layout.addRow("Result:", self.label["result"])
        layout.addRow("Cutoff frequency:", self.label["3.0dB"])
        layout.addRow("-6 dB point:", self.label["6.0dB"])
        layout.addRow("-60 dB point:", self.label["60.0dB"])
        layout.addRow("Roll-off:", self.label["octave"])
        layout.addRow("Roll-off:", self.label["decade"])

        self.set_titel('Highpass analysis')

    def runAnalysis(self):
        if not self.app.data.s21:
            logger.debug("No data to analyse")
            self.set_result("No data to analyse.")
            return

        self.reset()
        s21 = self.app.data.s21
        gains = [d.gain for d in s21]

        if (peak := self.find_level(gains)) < 0:
            return
        peak_db = gains[peak]
        logger.debug("Passband position: %d(%fdB)", peak, peak_db)

        cutoff_pos = self.find_cutoffs(gains, peak, peak_db)
        cutoff_freq = {
            att: s21[val].freq if val >= 0 else math.nan
            for att, val in cutoff_pos.items()
        }
        cutoff_gain = {
            att: gains[val] if val >= 0 else math.nan
            for att, val in cutoff_pos.items()
        }
        logger.debug("Cuttoff frequencies: %s", cutoff_freq)
        logger.debug("Cuttoff gains: %s", cutoff_gain)

        octave, decade = at.calculate_rolloff(
            s21, cutoff_pos["10.0dB"], cutoff_pos["20.0dB"])

        if cutoff_gain['3.0dB'] < -4:
            logger.debug("Cutoff frequency found at %f dB"
                         " - insufficient data points for true -3 dB point.",
                         cutoff_gain)
        logger.debug("Found true cutoff frequency at %d", cutoff_freq['3.0dB'])

        for label, val in cutoff_freq.items():
            self.label[label].setText(
                f"{format_frequency(val)}"
                f" ({cutoff_gain[label]:.1f} dB)")

        self.label['octave'].setText(f'{octave:.3f}dB/octave')
        self.label['decade'].setText(f'{decade:.3f}dB/decade')

        self.app.markers[0].setFrequency(str(s21[peak].freq))
        self.app.markers[1].setFrequency(str(cutoff_freq['3.0dB']))
        self.app.markers[2].setFrequency(str(cutoff_freq['6.0dB']))

        self.set_result(f"Analysis complete ({len(s21)}) points)")

    def find_level(self, gains: List[float]) -> int:
        marker = self.app.markers[0]
        logger.debug("Pass band location: %d", marker.location)
        if marker.location < 0:
            self.set_result(f"Please place {marker.name} in the passband.")
            return -1
        return at.center_from_idx(gains, marker.location)

    def find_cutoffs(self,
                     gains: List[float],
                     peak: int, peak_db: float) -> Dict[str, int]:
        return {
            f"{attn:.1f}dB": at.cut_off_left(
                gains, peak, peak_db, attn)
            for attn in CUTOFF_VALS
        }
