#include "csong.h"
#include <QFileInfo>

#include "getLoudness.h"
#include "getLoudness_terminate.h"
#include "rt_nonfinite.h"
#include "coder_array.h"
#include <cmath>


csong::csong()
{
    m_path="";
    m_loudness=0;
    m_peakLevel=0;
    m_widgetItem=new QTableWidgetItem[5];
    setWidgetProperties();
}

csong::csong(QString path)
{
    m_path=path;
    m_widgetItem=new QTableWidgetItem[5];
    setWidgetProperties();
}

csong::~csong()
{
    delete [] m_widgetItem;
}

void csong::analyze(float preferedLoudness)
{
    using namespace nqr;

    NyquistIO loader;
    try {
        std::shared_ptr<AudioData> fileData = std::make_shared<AudioData>();

#ifdef Q_OS_WIN
        FILE * audioFile = fopen(m_path.toLatin1().constData(), "rb");

        if (!audioFile)
        {
            throw std::runtime_error("file not found");
        }

        fseek(audioFile, 0, SEEK_END);
        size_t lengthInBytes = ftell(audioFile);
        fseek(audioFile, 0, SEEK_SET);

        // Allocate temporary buffer
        std::vector<uint8_t> fileBuffer(lengthInBytes);

        size_t elementsRead = fread(fileBuffer.data(), 1, lengthInBytes, audioFile);

        if (elementsRead == 0 || fileBuffer.size() < 64)
        {
            throw std::runtime_error("error reading file or file too small");
        }

        NyquistFileBuffer tempdata = {std::move(fileBuffer), elementsRead};

        fclose(audioFile);

        auto memory=tempdata;
#else
        auto memory= ReadFile(m_path.toStdString());
#endif

        QFileInfo fi(m_path);
        loader.Load(fileData.get(),fi.suffix().toStdString(),memory.buffer);

        coder::array<float, 2U> loudness;
        loudness.set_size(1,1);
        coder::array<float, 2U> data;
        data.set_size(fileData->samples.size()/2,fileData->channelCount);
        for (int idx0{0}; idx0 < data.size(0); idx0++) {
            for (int idx1{0}; idx1 < data.size(1); idx1++) {
              data[idx0 + data.size(0) * idx1] = fileData->samples[idx1+data.size(1)*idx0];
            }
          }
        getLoudness(data,fileData->sampleRate,loudness);
        //getLoudness_terminate();
        m_loudness=loudness[0];
        m_widgetItem[1].setData(Qt::EditRole,QVariant(m_loudness));


        m_peakLevel=0;
        for (unsigned int i=0;i<fileData->samples.size();i++){
            if (abs(fileData->samples[i])>m_peakLevel){
                m_peakLevel=abs(fileData->samples[i]);
            }
        }
        m_peakLevel=linToDb(m_peakLevel);
        if (m_peakLevel>0){
            m_peakLevel=0;
        }
        m_widgetItem[2].setData(Qt::EditRole,QVariant(m_peakLevel));

        m_correction=preferedLoudness-m_loudness;
        m_widgetItem[4].setData(Qt::EditRole,QVariant(m_correction));

        m_correctedPeakLevel=m_peakLevel+m_correction;
        m_widgetItem[3].setData(Qt::EditRole,QVariant(m_correctedPeakLevel));
        if (m_correctedPeakLevel>0){
            m_widgetItem[3].setBackground(Qt::red);
        }else {
            m_widgetItem[3].setBackground(QBrush());
        }
    }  catch (const UnsupportedExtensionEx & e)
    {
        std::cerr << "Caught: " << e.what() << std::endl;
    }
    catch (const LoadPathNotImplEx & e)
    {
        std::cerr << "Caught: " << e.what() << std::endl;
    }
    catch (const LoadBufferNotImplEx & e)
    {
        std::cerr << "Caught: " << e.what() << std::endl;
    }
    catch (const std::exception & e)
    {
        std::cerr << "Caught: " << e.what() << std::endl;
    }

}

void csong::setNewPreferedLoudness(float preferedLoudness)
{
    m_correction=preferedLoudness-m_loudness;
    m_widgetItem[4].setText(QString::number(m_correction));

    m_correctedPeakLevel=m_peakLevel+m_correction;
    m_widgetItem[3].setText(QString::number(m_correctedPeakLevel));
    if (m_correctedPeakLevel>0){
        m_widgetItem[3].setBackground(Qt::red);
    }else {
        m_widgetItem[3].setBackground(QBrush());
    }
}

void csong::setPath(QString path)
{
    m_path=path;
    m_widgetItem[0].setText(m_path);
}

float csong::get_PeakLevel() const
{
    return m_peakLevel;
}

float csong::get_Loudness() const
{
    return m_loudness;
}

float csong::get_correctedPeakLevel() const
{
    return m_correctedPeakLevel;
}

float csong::get_correction() const
{
    return m_correction;
}


float csong::linToDb(float linValue)
{
    return 20*std::log10(linValue);
}

void csong::setWidgetProperties()
{
    for (int i=0;i<5;i++){
        m_widgetItem[i].setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
        m_widgetItem[i].setTextAlignment(Qt::AlignCenter);
    }
    m_widgetItem[0].setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
}

QString csong::get_path() const{
    return m_path;
}
