/* Custom CPAP/Oximetry Calculations Header
 *
 * Copyright (c) 2019-2025 The OSCAR Team
 * Copyright (C) 2011-2018 Mark Watkins
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License. See the file COPYING in the main directory of the source code
 * for more details. */

#ifndef CALCS_H
#define CALCS_H

#include "day.h"

//! param samples Number of samples
//! width number of surrounding samples to consider
//! percentile fractional percentage, between 0 and 1
void percentileFilter(EventDataType *input, EventDataType *output, int samples, int width,
                      EventDataType percentile);
void xpassFilter(EventDataType *input, EventDataType *output, int samples, EventDataType weight);

enum FilterType { FilterNone = 0, FilterPercentile, FilterXPass };

struct Filter {
    Filter(FilterType t, EventDataType p1, EventDataType p2, EventDataType p3) {
        type = t;
        param1 = p1;
        param2 = p2;
        param3 = p3;
    }
    Filter() {
        type = FilterNone;
        param1 = 0;
        param2 = 0;
        param3 = 0;
    }
    Filter(const Filter &copy) {
        type = copy.type;
        param1 = copy.param1;
        param2 = copy.param2;
        param3 = copy.param3;
    }
    Filter& operator=(const Filter &copy) = default;
    ~Filter() {};

    FilterType type;
    EventDataType param1;
    EventDataType param2;
    EventDataType param3;
};

struct BreathPeak {
    BreathPeak() { min = 0; max = 0; start = 0; middle = 0; end = 0; } // peakmin=0; peakmax=0;  }
    BreathPeak(EventDataType _min, EventDataType _max, qint32 _start, qint32 _middle,
               qint32 _end) {//, qint64 _peakmin, qint64 _peakmax) {
        min = _min;
        max = _max;
        start = _start;
        middle = _middle;
        end = _end;
    }

    //error: definition of implicit copy assignment operator for 'BreathPeak' is deprecated because it has a user-provided copy constructor
    BreathPeak(const BreathPeak &copy) {
        min = copy.min;
        max = copy.max;
        start = copy.start;
        middle = copy.middle;
        end = copy.end;
    };

    bool operator<(const BreathPeak &p) ;

    BreathPeak& operator=(const BreathPeak &other) {
        if (this != &other) {  // Self-assignment check
            min = other.min;
            max = other.max;
            start = other.start;
            middle = other.middle;
            end = other.end;
        }
        return *this;
    }

    int samplelength() { return end - start; }
    int upperLength() { return middle - start; }
    int lowerLength() { return end - middle; }

    EventDataType min;   // peak value
    EventDataType max;   // peak value
    qint32 start;       // beginning zero cross
    qint32 middle;         // ending zero cross
    qint32 end;         // ending zero cross
};

bool operator<(const BreathPeak &p1, const BreathPeak &p2);

const int num_filter_buffers = 2;

const int max_filter_buf_size_entries = 0x20C000 ; // was 2097152 == 0x200000;
const int max_filter_buf_size_malloc = max_filter_buf_size_entries * sizeof(EventDataType);

//! \brief Class to process Flow Rate waveform data
class FlowParser
{
  public:
    FlowParser();
    ~FlowParser();

    //! \brief Clears the (input) filter chain
    void clearFilters();

    //! \brief Applies the filter chain to input, with supplied number of samples
    EventDataType *applyFilters(EventDataType *input, int samples);

    //! \brief Add the filter
    void addFilter(FilterType ft, EventDataType p1 = 0, EventDataType p2 = 0, EventDataType p3 = 0) {
        m_filters.push_back(Filter(ft, p1, p2, p3));
    }
    //! \brief Opens the flow rate EventList, applies the input filter chain, and calculates peaks
    void openFlow(Session *session, EventList *flow);

    //! \brief Calculates the upper and lower breath peaks
    void calcPeaks(EventDataType *input, int samples);

    // Minute vent needs Resp & TV calcs made here..
    void calc(bool calcResp, bool calcTv, bool calcTi, bool calcTe, bool calcMv);
#if defined(STEADY_BREATHING)
    EventDataType RMSOfVectorFluctuation(QVector<EventDataType> vector);
    void calcSteadyBreathingWaveform();
    void flagSteadyBreathing(Session *session);
#endif
    void flagEvents();
    void flagUserEvents(ChannelID code, EventDataType restriction, EventDataType duration);

    /*void calcTidalVolume();
    void calcRespRate();
    void calcMinuteVent(); */


    QList<Filter> m_filters;
  protected:
    QVector<BreathPeak> breaths;

    int m_samples;
    EventList *m_flow;
    Session *m_session;
    EventDataType m_gain;
    EventDataType m_rate;
    EventDataType m_minutes;
    //! \brief The filtered waveform
    EventDataType *m_filtered;
    //! \brief BreathPeak's start on positive cycle?
    bool m_startsUpper;
  private:
    EventDataType *m_buffers[num_filter_buffers];
};

bool SearchApnea(Session *session, qint64 time, double dur);

//! \brief Calculate Respiratory Rate, Tidal Volume & Minute Ventilation for PRS1 data
void calcRespRate(Session *session, FlowParser *flowparser = nullptr);

//! \brief Calculates the sliding window AHI graph
int calcAHIGraph(Session *session);

//! \brief Calculates AHI for a session between start & end (a support function for the sliding window graph)
EventDataType calcAHI(Session *session, qint64 start = -1, qint64 end = -1);

//! \brief Scans for leaks over Redline and flags as large leaks, unless device provided them already
void flagLargeLeaks(Session *session);

//! \brief Leaks calculations for PRS1
int calcLeaks(Session *session);

//! \brief Calculate Pulse change flagging, according to preferences
int calcPulseChange(Session *session);

//! \brief Calculate SPO2 Drop flagging, according to preferences
int calcSPO2Drop(Session *session);


#endif // CALCS_H
