#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QString>
#include <QtMath>
#include <QMessageBox>
#include "library.h"
#include <QSettings>
#include <QFile>
#include <QDataStream>
#include <QFileDialog>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    m_ampCount=0;
    m_pCAmp=Q_NULLPTR;

    m_speakerCount=0;
    m_pSpeaker=Q_NULLPTR;

    m_dacCount=0;
    m_pDac=Q_NULLPTR;

    loadLibrary();
    refreshLibraryAmps();
    refreshLibraryAmpModes();
    refreshLibrarySpeaker();
    refreshLibraryDacs();
}

MainWindow::~MainWindow()
{
    delete ui;
    delete [] m_pCAmp;
}

void MainWindow::on_ComboBox_selectSensUnit_currentIndexChanged(int index)
{
    double oldValue=ui->doubleSpinBox_ampInputSens->value();
    if (index==0){
       ui->doubleSpinBox_ampInputSens->setSuffix(QString(" V"));
       ui->doubleSpinBox_ampInputSens->setDecimals(2);
       ui->doubleSpinBox_ampInputSens->setValue(0.7746*qPow(10,oldValue/20));
    }else{
       ui->doubleSpinBox_ampInputSens->setSuffix(QString(" dBu"));
       ui->doubleSpinBox_ampInputSens->setDecimals(1);
       ui->doubleSpinBox_ampInputSens->setValue(20*std::log10(oldValue/0.7746));
    }

}


void MainWindow::on_pushButton_requestLimiter_clicked()
{
    qreal speakerCount=ui->spinBox_speakerCount->value();
    qreal speakerImp=ui->spinBox_speakerImp->value()/speakerCount;
    qreal speakerRMS=ui->spinBox_speakerRMSPower->value()*speakerCount;
    qreal speakerPeak=ui->spinBox_speakerPeakPower->value()*speakerCount;
    qreal ampImp=ui->spinBox_AmpImp->value();

    if (speakerImp!=ampImp){
        QMessageBox msgBox(QMessageBox::Critical,"Impedance Mismatch",QString("The speaker impedance (")+QString::number(speakerImp)+QString(" Ohm) doesn't match with the amplifier impedance (")+QString::number(ampImp)+QString(" Ohm). Maybe you can change the amplifier mode?"));
        msgBox.exec();
        return;
    }

    qreal vMaxSpeakerInRMS=20*std::log10(qSqrt(speakerImp*speakerRMS)/0.7746);
    qreal vMaxSpeakerInPeak=20*std::log10(qSqrt(speakerImp*speakerPeak)/0.7746);

    qreal vAmpSens_volt=0;
    qreal vAmpsens_dBu=0;
    if(ui->ComboBox_selectSensUnit->currentIndex()==0){
        vAmpSens_volt=ui->doubleSpinBox_ampInputSens->value();
        vAmpsens_dBu=20*std::log10(vAmpSens_volt/0.7746);
    }else{
        vAmpSens_volt=qPow(10,ui->doubleSpinBox_ampInputSens->value()/20)*0.7746;
        vAmpsens_dBu=ui->doubleSpinBox_ampInputSens->value();
    }

    qreal voltageGain=20*std::log10(qSqrt(ui->spinBox_AmpImp->value()*ui->spinBox_AmpPower->value())/vAmpSens_volt);
    qreal vMaxAmpInRMS=vMaxSpeakerInRMS-voltageGain;
    qreal vMaxAmpInPeak=vMaxSpeakerInPeak-voltageGain;

    qreal thresholdRMS=-18-(ui->spinBox_digitalNominalLevel->value()-vMaxAmpInRMS);
    qreal thresholdPeak=-18-(ui->spinBox_digitalNominalLevel->value()-vMaxAmpInPeak);
    qreal attackTimeRMS=(double)1000/ui->spinBox_speakerHPF->value();
    qreal releaseTimeRMS=attackTimeRMS*8;
    qreal releaseTimePeak=attackTimeRMS;




    if (vAmpsens_dBu<vMaxAmpInRMS){
        QMessageBox msgBox(QMessageBox::Warning,"Amplifier can't deliver maximum RMS power","The maximum power level of the amplifier is below the maximum RMS power level of the speaker. To protect the speaker from a distorted signal the amplifier might deliver, a peak limiter is required with the amplifiers input sensitivty set as its threshold.");
        msgBox.exec();
        ui->label_thresholdRMS->setText(QString("not required"));
        ui->label_attackTimeRMS->setText(QString::number(attackTimeRMS,'f',2)+QString(" ms"));
        ui->label_releaseTimeRMS->setText(QString::number(releaseTimeRMS,'f',2)+QString(" ms"));
        ui->label_UMaxSpeakerInRMS->setText(QString::number(vMaxSpeakerInRMS,'f',2)+QString(" dBU (")+QString::number(qSqrt(speakerImp*speakerRMS),'f',2)+QString(" V)"));
        ui->label_UMaxAmpInRMS->setText(QString("not applicable"));


        thresholdPeak=-18-(ui->spinBox_digitalNominalLevel->value()-vAmpsens_dBu);
        ui->label_thresholdPeak->setText(QString::number(thresholdPeak,'f',1)+QString(" dBFS"));
        ui->label_releaseTimePeak->setText(QString::number(releaseTimePeak,'f',2)+QString(" ms"));
        ui->label_UMaxSpeakerInPeak->setText(QString::number(vMaxSpeakerInPeak,'f',2)+QString(" dBU (")+QString::number(qSqrt(speakerImp*speakerPeak),'f',2)+QString(" V)"));
        ui->label_UMaxAmpInPeak->setText(QString::number(vAmpSens_volt,'f',2)+QString(" V (= input sensitivity)"));
    }else if (vAmpsens_dBu<vMaxAmpInPeak){
        QMessageBox msgBox(QMessageBox::Warning,"Amplifier can't deliver maximum peak power","The maximum power level of the amplifier is below the maximum peak power level of the speaker. To protect the speaker from a distorted signal the amplifier might deliver, a peak limiter is required with the amplifiers input sensitivty set as its threshold.");
        msgBox.exec();

        ui->label_thresholdRMS->setText(QString::number(thresholdRMS,'f',1)+QString(" dBFS"));
        ui->label_attackTimeRMS->setText(QString::number(attackTimeRMS,'f',2)+QString(" ms"));
        ui->label_releaseTimeRMS->setText(QString::number(releaseTimeRMS,'f',2)+QString(" ms"));
        ui->label_UMaxSpeakerInRMS->setText(QString::number(vMaxSpeakerInRMS,'f',2)+QString(" dBU (")+QString::number(qSqrt(speakerImp*speakerRMS),'f',2)+QString(" V)"));
        ui->label_UMaxAmpInRMS->setText(QString::number(vMaxAmpInRMS,'f',2)+QString(" dBU (")+QString::number(qPow(10,vMaxAmpInRMS/20)*0.7746,'f',2)+QString(" V)"));

        thresholdPeak=-18-(ui->spinBox_digitalNominalLevel->value()-vAmpsens_dBu);
        ui->label_thresholdPeak->setText(QString::number(thresholdPeak,'f',1)+QString(" dBFS"));
        ui->label_releaseTimePeak->setText(QString::number(releaseTimePeak,'f',2)+QString(" ms"));
        ui->label_UMaxSpeakerInPeak->setText(QString::number(vMaxSpeakerInPeak,'f',2)+QString(" dBU (")+QString::number(qSqrt(speakerImp*speakerPeak),'f',2)+QString(" V)"));
        ui->label_UMaxAmpInPeak->setText(QString::number(vAmpSens_volt,'f',2)+QString(" V (= input sensitivity)"));
    }else{
        ui->label_thresholdRMS->setText(QString::number(thresholdRMS,'f',1)+QString(" dBFS"));
        ui->label_attackTimeRMS->setText(QString::number(attackTimeRMS,'f',2)+QString(" ms"));
        ui->label_releaseTimeRMS->setText(QString::number(releaseTimeRMS,'f',2)+QString(" ms"));
        ui->label_UMaxSpeakerInRMS->setText(QString::number(vMaxSpeakerInRMS,'f',2)+QString(" dBU (")+QString::number(qSqrt(speakerImp*speakerRMS),'f',2)+QString(" V)"));
        ui->label_UMaxAmpInRMS->setText(QString::number(vMaxAmpInRMS,'f',2)+QString(" dBU (")+QString::number(qPow(10,vMaxAmpInRMS/20)*0.7746,'f',2)+QString(" V)"));

        ui->label_thresholdPeak->setText(QString::number(thresholdPeak,'f',1)+QString(" dBFS"));
        ui->label_releaseTimePeak->setText(QString::number(releaseTimePeak,'f',2)+QString(" ms"));
        ui->label_UMaxSpeakerInPeak->setText(QString::number(vMaxSpeakerInPeak,'f',2)+QString(" dBU (")+QString::number(qSqrt(speakerImp*speakerPeak),'f',2)+QString(" V)"));
        ui->label_UMaxAmpInPeak->setText(QString::number(vMaxAmpInPeak,'f',2)+QString(" dBU (")+QString::number(qPow(10,vMaxAmpInPeak/20)*0.7746,'f',2)+QString(" V)"));
    }




}


void MainWindow::on_actionEditLibrary_triggered()
{
    library* dialog=new library;
    dialog->setAmpPointer(m_pCAmp,m_ampCount);
    dialog->setSpeakerPointer(m_pSpeaker,m_speakerCount);
    dialog->setDacPoint(m_pDac,m_dacCount);

    dialog->open();
    m_ampCount=dialog->getAmpCount();
    m_pCAmp=dialog->getAmps();
    m_pSpeaker=dialog->getSpeaker();
    m_speakerCount=dialog->getSpeakerCount();
    m_pDac=dialog->getDacs();
    m_dacCount=dialog->getDacCount();

    refreshLibraryAmps();
    refreshLibraryAmpModes();
    refreshLibrarySpeaker();
    refreshLibraryDacs();

    delete dialog;
    saveLibrary();
}

void MainWindow::refreshLibraryAmps()
{
    ui->comboBox_libraryAmp->clear();
    for (unsigned int i=0;i<m_ampCount;i++){
        ui->comboBox_libraryAmp->addItem(QString::fromStdString(m_pCAmp[i].getName()));
    }
}

void MainWindow::refreshLibraryAmpModes()
{
    ui->comboBox_libraryAmoMode->clear();
    if (ui->comboBox_libraryAmp->currentIndex()>=0){
        for (unsigned int i=0;i<m_pCAmp[ui->comboBox_libraryAmp->currentIndex()].getAmpModesCount();i++){
            ui->comboBox_libraryAmoMode->addItem(QString::fromStdString(m_pCAmp[ui->comboBox_libraryAmp->currentIndex()].getAmpMode(i).getName()));
        }
    }
}

void MainWindow::refreshLibrarySpeaker()
{
    ui->comboBox_librarySpeaker->clear();
    for (unsigned int i=0;i<m_speakerCount;i++){
        ui->comboBox_librarySpeaker->addItem(QString::fromStdString(m_pSpeaker[i].getName()));
    }
}

void MainWindow::refreshLibraryDacs()
{
    ui->comboBox_dacPreset->clear();
    for (unsigned int i=0;i<m_dacCount;i++){
        ui->comboBox_dacPreset->addItem(QString::fromStdString(m_pDac[i].name()));
    }
}

void MainWindow::loadLibrary()
{
    QSettings settings("DKM-Tech","Pa-Calculator");
    QString fileName = settings.value("library/path",QString::fromStdString("")).toString();
    if (fileName.isEmpty())
            return;
        else {
        QFile file(fileName);
            if (!file.open(QIODevice::ReadOnly)) {
                   QMessageBox::information(this, tr("Unable to open file. Will create a new one."),file.errorString());
                   return;
             }
        QDataStream in(&file);
        in.setVersion(QDataStream::Qt_4_5);
        uint count;
        in >> count;
        m_ampCount=count;
        if (m_ampCount>0){
            delete[] m_pCAmp;
            m_pCAmp=new cAmp[m_ampCount];
        }
        for (unsigned int j=0;j<m_ampCount;j++){
            in >> m_pCAmp[j];
        }

        in >> count;
        m_speakerCount=count;
        if (m_speakerCount>0){
            delete[] m_pSpeaker;
            m_pSpeaker=new cSpeaker[m_speakerCount];
        }
        for (unsigned int j=0;j<m_speakerCount;j++){
            in >> m_pSpeaker[j];
        }
        in >> count;
        m_dacCount=count;
        if (m_dacCount>0){
            delete [] m_pDac;
            m_pDac=new cdac[m_dacCount];
        }

        for (unsigned int j=0;j<m_dacCount;j++){
            in >> m_pDac[j];
        }
    }
}

void MainWindow::saveLibrary()
{
    QSettings settings("DKM-Tech","Pa-Calculator");
    QString fileName = settings.value("library/path",QString::fromStdString("")).toString();
    if (fileName.isEmpty())
            return;
        else {
            QFile file(fileName);
            if (!file.open(QIODevice::WriteOnly)) {
                QMessageBox::information(this, tr("Unable to open file. New one will be created on exit at :")+fileName,
                    file.errorString());
                return;
            }
            QDataStream out(&file);
            out.setVersion(QDataStream::Qt_4_5);
            out << (uint)m_ampCount;
            for (unsigned int j=0;j<m_ampCount;j++){
                out << m_pCAmp[j];
            }
            out << (uint)m_speakerCount;
            for (unsigned int j=0;j<m_speakerCount;j++){
                out << m_pSpeaker[j];
            }
            out << (uint)m_dacCount;
            for (unsigned int j=0;j<m_dacCount;j++){
                out << m_pDac[j];
            }
    }
}

void MainWindow::on_comboBox_libraryAmp_currentIndexChanged(int index)
{
    refreshLibraryAmpModes();
}


void MainWindow::on_pushButton_clicked()
{
    if (ui->comboBox_libraryAmp->currentIndex()>=0 && ui->comboBox_libraryAmoMode->currentIndex()>=0){
        if (m_pCAmp[ui->comboBox_libraryAmp->currentIndex()].getAmpModesCount()>=1){
            ui->spinBox_AmpPower->setValue(m_pCAmp[ui->comboBox_libraryAmp->currentIndex()].getAmpMode(ui->comboBox_libraryAmoMode->currentIndex()).getRatedPower());
            ui->spinBox_AmpImp->setValue(m_pCAmp[ui->comboBox_libraryAmp->currentIndex()].getAmpMode(ui->comboBox_libraryAmoMode->currentIndex()).getRatedImp());
            ui->ComboBox_selectSensUnit->setCurrentIndex(m_pCAmp[ui->comboBox_libraryAmp->currentIndex()].getSensivityUnit());
            ui->doubleSpinBox_ampInputSens->setValue(m_pCAmp[ui->comboBox_libraryAmp->currentIndex()].getSensitivity());
        }

    }
}


void MainWindow::on_actionChange_Library_Location_triggered()
{
    QSettings settings("DKM-Tech","Pa-Calculator");
    settings.setValue("library/path",QFileDialog::getSaveFileName(this,"Where to store the libary file?",settings.value("library/path",QString::fromStdString("")).toString(),"Pa Calculator Library File (*.palib)"));
    saveLibrary();
}


void MainWindow::on_actionOpen_Library_triggered()
{
    QSettings settings("DKM-Tech","Pa-Calculator");
    settings.setValue("library/path",QFileDialog::getOpenFileName(this,"Select library storage file",settings.value("library/path",QString::fromStdString("")).toString(),"Pa Calculator Library File (*.palib)"));
    loadLibrary();
    refreshLibraryAmps();
    refreshLibrarySpeaker();
}


void MainWindow::on_pushButton_loadSpeaker_clicked()
{
    if (ui->comboBox_librarySpeaker->currentIndex()>=0){
        ui->spinBox_speakerImp->setValue(m_pSpeaker[ui->comboBox_librarySpeaker->currentIndex()].getImpedance());
        ui->spinBox_speakerRMSPower->setValue(m_pSpeaker[ui->comboBox_librarySpeaker->currentIndex()].getRMSPower());
        ui->spinBox_speakerPeakPower->setValue(m_pSpeaker[ui->comboBox_librarySpeaker->currentIndex()].getPeakPower());
        ui->spinBox_speakerHPF->setValue(m_pSpeaker[ui->comboBox_librarySpeaker->currentIndex()].getHPF());
    }
}




void MainWindow::on_pushButton_loadDac_clicked()
{
    if (ui->comboBox_dacPreset->currentIndex()>=0){
        ui->spinBox_digitalNominalLevel->setValue(m_pDac[ui->comboBox_dacPreset->currentIndex()].outputLevel());
    }
}

