//    tonalisa - software to look at overtone-structures
//    Copyright (C) 2016  Dominik Schmidt-Philipp
//
//    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 <http://www.gnu.org/licenses/>.
//
#include "audiofile.h"
#include "clm.h"
#include <QObject>
#include <QFileInfo>
#include <cstdlib>
#include <QVector>
#include <QtMath>
#include <QDebug>

#define SMALLEST_FFT 64

AudioFile::AudioFile(QObject *parent)
  : QObject(parent)
{
  mus_sound_initialize();
  m_sampleRate = 44100;
  validFile = false;
  m_select1 = 0;
  m_select2 = 8192;
  m_fftSize=8192;
  m_windowSize = 501;
  m_windowIndex = MUS_KAISER_WINDOW;
}

//-----------------------------------------------------------
// Getter Functions for properties:
QString AudioFile::path() const
{
  return m_path;
}
float AudioFile::select1() const
{
  return m_select1;
}
float AudioFile::select2() const
{
  return m_select2;
}
QList<double> AudioFile::samples() const
{
  return m_samples;
}
int AudioFile::sampleRate() const { return m_sampleRate; }
long AudioFile::npts() const { return m_fftSize; }
QList<double> AudioFile::spectrum() const { return m_spectrum; }
int AudioFile::windowIndex() const { return m_windowIndex;}
long AudioFile::windowSize() const { return m_windowSize; }

//-----------------------------------------------------------
// Setter Functions for properties:
void AudioFile::setPath(QString &path)
{
  path.remove(0,7); // removes file:// which comes from dialog
  QFileInfo fi(path);
  
  if ( path != m_path ) {
    m_path = path;
    if ( fi.isFile() ) {
      read_header();
      read_samples();
      validFile = true;
    } else validFile = false;
    emit pathChanged();
    }
}

void AudioFile::setSelect1(const float &sample)
{
    if( sample != m_select1 ) {
        m_select1 = sample;
        m_select2 = m_select1 + m_windowSize;
        emit selectionChanged();
    }
}
/*void AudioFile::setSelect2(const float &sample)
{
    int tmp;
    if( sample != m_select2 ) {
        m_select2 = sample;
        emit selectionChanged();
    }

    tmp = m_select1 + m_fftSize;
    if (tmp != m_select2) {
        m_select2 = tmp;
        emit selectionChanged();
    }
    }*/
void AudioFile::setNpts(long &npts)
{
     if( npts != m_fftSize ) {
        if (npts < SMALLEST_FFT ) npts=SMALLEST_FFT;
        m_fftSize = nextPowerOfTwo((long long) npts - 1 );
        emit nptsChanged();
    }
}
void AudioFile::setWindowIndex( const int &indx )
{
  if( m_windowIndex != indx ) {
    if(indx != MUS_DPSS_WINDOW) {
      m_windowIndex = (mus_fft_window_t)indx;
      emit windowChanged();
    }
  }
}
void AudioFile::setWindowSize( int &windowSize )
{
  if ( m_windowSize != windowSize ) {
    if ( windowSize > m_fftSize ) windowSize = m_fftSize - 1;
    if ( windowSize % 2 ) m_windowSize = windowSize;
    else m_windowSize = windowSize + 1;
    emit windowSizeChanged();
    emit windowChanged();
    m_select2 = m_select1+m_windowSize;
    emit selectionChanged();
    calculateFFT();
  }
}
//-----------------------------------------------------------
// TOOLS

int AudioFile::read_header() {
    QByteArray p = m_path.toLocal8Bit();
    int srate;
    srate = mus_sound_srate( p );
    chans = mus_sound_chans( p );
    framples = mus_sound_framples( p );

    if (srate == -1) return -1;
    if (srate != m_sampleRate) {
        m_sampleRate = srate;
        emit sampleRateChanged();
    }
    return 0;
}
void AudioFile::read_samples() {
    int fd;
    mus_float_t **bufs;
    m_samples.clear();
    fd = mus_sound_open_input( m_path.toLocal8Bit() );
    if ( fd != -1 ) {
        bufs = (mus_float_t**)calloc(chans, sizeof(mus_float_t*));
        for ( int i=0; i<chans; i++ )
            bufs[i] = (mus_float_t*)calloc(framples, sizeof(mus_float_t));

        mus_sound_read(fd, 0, framples, chans, bufs);
        for ( int i=0; i<framples; i++ )
            m_samples << bufs[0][i];

        for ( int i=0; i<chans; i++) free(bufs[i]);
        free(bufs);
    }
    emit samplesChanged();
}

/* Fourier Transform */
void AudioFile::calculateFFT()
{
    int indx,size;
    mus_float_t re[m_fftSize];
    mus_float_t im[m_fftSize];
    mus_float_t* window;
    mus_float_t sample;
    window = mus_make_fft_window(m_windowIndex,m_windowSize,6.0);
    size = m_samples.size();
    for ( int i=0; i<m_fftSize; i++){
        indx = m_select1+i;

        if ( indx < size  && i < m_windowSize ){
            sample= m_samples[indx] * window[i];
            re[i] = sample;
        }
        else re[i] = 0;
        im[i]=0;
    }
    free(window);
    mus_fft(re,im,m_fftSize,1);

    m_spectrum.clear();
    m_spectrum.reserve(m_fftSize/2);
    for (int i=0; i<(m_fftSize/2); i++)
        m_spectrum << sqrt(re[i]*re[i]+im[i]*im[i])/m_sampleRate;

    emit spectrumChanged();

}
mus_long_t AudioFile::nextPowerOfTwo(long long x) {
    mus_long_t y = 1;
    if (x < 0)
        return x;

    while (x > y)
        y <<= 1;

    return y;
}
