//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021 Edouard Griffiths, F4EXB <f4exb06@gmail.com>                                          //
// Copyright (C) 2022 Jiří Pinkava <jiri.pinkava@rossum.ai>                                                 //
//                                                                                                          //
// 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 as 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 V3 for more details.                                                          //
//                                                                                                          //
// You should have received a copy of the GNU General Public License                                        //
// along with this program. If not, see <http://www.gnu.org/licenses/>.                                     //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <algorithm>
#include <QMutexLocker>

#include "dcsdetector.h"

DCSDetector::DCSDetector() :
    m_bitIndex(0.0),
    m_sampleRate(48000),
    m_eqSamples(nullptr),
    m_high(0.0f),
    m_low(0.0f),
    m_mid(0.0f),
    m_prevSample(0.0f),
    m_dcsWord(0)
{
    setBitrate(134.3);
    setEqWindow(23);
}

bool DCSDetector::analyze(Real *sample, unsigned int& dcsCode)
{
    QMutexLocker mlock(&m_mutex);
    bool codeAvailable = false;

    if (!m_eqSamples) {
        return false;
    }
    // Equalizer
    m_eqSamples[m_eqIndex++] = *sample;

    if (m_eqIndex == m_eqSize)
    {
        m_high = *std::max_element(m_eqSamples, m_eqSamples + m_eqSize);
        m_low  = *std::min_element(m_eqSamples, m_eqSamples + m_eqSize);
        m_mid = (m_high + m_low) / 2.0f;
        // qDebug("DCSDetector::analyze: %f %f %f", m_low, m_mid, m_high);
        m_eqIndex = 0;
    }

    // Edge detection
    if (((m_prevSample < m_mid) && (*sample >= m_mid)) || ((m_prevSample > m_mid) && (*sample <= m_mid))) {
        m_bitIndex = 0.0;
    }

    // Symbol detection
    m_prevSample = *sample;
    float fprev = m_bitIndex;
    m_bitIndex += m_bitsPerSample;

    if ((fprev < 0.5f) && (m_bitIndex >= 0.5f)) // mid point detection
    {
        unsigned int bit = *sample > m_mid ? 1 : 0; // always work in positive mode
        m_dcsWord = (bit << 23) + (m_dcsWord >> 1);

        if (((m_dcsWord >> 9) & 0x07) == 4) // magic signature
        {
            codeAvailable = m_golay2312.decodeParityFirst(&m_dcsWord);

            if (codeAvailable) {
                dcsCode = m_dcsWord & 0x1FF;
            }
        }
    }

    if (m_bitIndex > 1.0f) {
        m_bitIndex -= 1.0f;
    }

    return codeAvailable;
}

void DCSDetector::setBitrate(float bitrate)
{
    m_bitrate = bitrate;
    m_bitsPerSample = m_bitrate / m_sampleRate;
    m_samplesPerBit = m_sampleRate / m_bitrate;
}

void DCSDetector::setSampleRate(int sampleRate)
{
    m_sampleRate  = sampleRate;
    m_bitsPerSample = m_bitrate / m_sampleRate;
    m_samplesPerBit = m_sampleRate / m_bitrate;
}

void DCSDetector::setEqWindow(int nbBits)
{
    QMutexLocker mlock(&m_mutex);

    m_eqBits = nbBits;
    m_eqSize = (int) m_samplesPerBit * m_eqBits;

    if (m_eqSamples) {
        delete[] m_eqSamples;
    }

    m_eqSamples = new float[m_eqSize];
    m_eqIndex = 0;
}

DCSDetector::~DCSDetector()
{
    if (m_eqSamples) {
        delete[] m_eqSamples;
    }
}
