/*******************************************************************************
*   @author Tomáš Pospíšil
*
*   Tento kód byl sestaven na základì tutoriálu, dostupného na adrese:
*   https://docs.juce.com/master/tutorial_looping_audio_sample_buffer.html.
*
*  ==============================================================================

   Some of the code is part of the JUCE tutorials.
   Copyright (c) 2020 - Raw Material Software Limited

   The code included in this file is provided under the terms of the ISC license
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
   To use, copy, modify, and/or distribute this software for any purpose with or
   without fee is hereby granted provided that the above copyright notice and
   this permission notice appear in all copies.

   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
   PURPOSE, ARE DISCLAIMED.

  ==============================================================================
  */

/*
  ==============================================================================

    This file contains the basic framework code for a JUCE plugin editor.

  ==============================================================================
*/

#include "PluginProcessor.h"
#include "PluginEditor.h"

//==============================================================================
BufferUnderstandAudioProcessorEditor::BufferUnderstandAudioProcessorEditor (BufferUnderstandAudioProcessor& p)
    : AudioProcessorEditor (&p), audioProcessor (p)
{
    juce::Component::addAndMakeVisible(openButton);
    openButton.setButtonText("Open...");
    openButton.onClick = [this] { openButtonClicked(); };

    juce::Component::addAndMakeVisible(clearButton);
    clearButton.setButtonText("Clear");
    clearButton.onClick = [this] { clearButtonClicked(); };

    juce::Component::addAndMakeVisible(playButton);
    playButton.setButtonText("Play");
    playButton.onClick = [this] { playButtonClicked(fileBuffer); };

    juce::Component::setSize(300, 200);

    formatManager.registerBasicFormats();
}

BufferUnderstandAudioProcessorEditor::~BufferUnderstandAudioProcessorEditor()
{
}


/*
* This function is called to open an audio file, which is going to be looped.
* The audio samples are copied to the AudioSampleBuffer, which is then copied
* to the output buffer all over again in loop.
*/
void BufferUnderstandAudioProcessorEditor::openButtonClicked()
{
    shutdownAudio();                                                                         

    chooser = std::make_unique<juce::FileChooser>("Select a Wave file shorter than 40 seconds to play...",
        juce::File{},
        "*.wav");
    int chooserFlags = juce::FileBrowserComponent::openMode
        | juce::FileBrowserComponent::canSelectFiles;

    chooser->launchAsync(chooserFlags, [this](const juce::FileChooser& fc)
        {
            juce::File file = fc.getResult();

            if (file == juce::File{})
                return;

            std::unique_ptr<juce::AudioFormatReader> reader(formatManager.createReaderFor(file)); 

            if (reader.get() != nullptr)
            {
                double duration = (float)reader->lengthInSamples / reader->sampleRate;               

                fileBuffer.setSize((int)reader->numChannels, (int)reader->lengthInSamples);  
                reader->read(&fileBuffer,                                                      
                    0,                                                               
                    (int)reader->lengthInSamples,                                  
                    0,                                                               
                    true,                                                             
                    true);
                    position = 0;                                                                   
                    setAudioChannels(0, (int)reader->numChannels);                              
            }
        });
}


/*
* When the clear button is clicked all the audio is shut down.
*/
void BufferUnderstandAudioProcessorEditor::clearButtonClicked()
{
    shutdownAudio();
}


/*
* This function handles the copying of samples from AudiSampleBuffer to the output
* buffer. If the open button is clicked the whole fileBuffer is played in loop. If
* the play button is clicked the buffer, which is smaller then fileBuffer, is played.
* 
* @param bufferToFill   Outpout buffer, on which the samples are copied.
*/
void BufferUnderstandAudioProcessorEditor::getNextAudioBlock(const juce::AudioSourceChannelInfo& bufferToFill)
{
    if (play == false)
    {
        int numInputChannels = fileBuffer.getNumChannels();
        int numOutputChannels = bufferToFill.buffer->getNumChannels();

        int outputSamplesRemaining = bufferToFill.numSamples;                                  
        int outputSamplesOffset = bufferToFill.startSample;                                    

        while (outputSamplesRemaining > 0)
        {
            int bufferSamplesRemaining = fileBuffer.getNumSamples() - position;                
            int samplesThisTime = juce::jmin(outputSamplesRemaining, bufferSamplesRemaining); 

            for (int channel = 0; channel < numOutputChannels; ++channel)
            {
                bufferToFill.buffer->copyFrom(channel,                                         
                    outputSamplesOffset,                             
                    fileBuffer,                                      
                    channel % numInputChannels,                      
                    position,                                        
                    samplesThisTime);                               
            }

            outputSamplesRemaining -= samplesThisTime;                                          
            outputSamplesOffset += samplesThisTime;                                            
            position += samplesThisTime;                                                        

            if (position == fileBuffer.getNumSamples())
                position = 0;                                                                   
        }
    }
    else
    {
        int numInputChannels = buffer.getNumChannels();
        int numOutputChannels = bufferToFill.buffer->getNumChannels();

        int outputSamplesRemaining = bufferToFill.numSamples;                                  
        int outputSamplesOffset = bufferToFill.startSample;                                    

        while (outputSamplesRemaining > 0)
        {
            int bufferSamplesRemaining = buffer.getNumSamples() - position;                
            int samplesThisTime = juce::jmin(outputSamplesRemaining, bufferSamplesRemaining); 

            for (int channel = 0; channel < numOutputChannels; ++channel)
            {
                bufferToFill.buffer->copyFrom(channel,                                         
                    outputSamplesOffset,                             
                    buffer,                                      
                    channel % numInputChannels,                      
                    position,                                        
                    samplesThisTime);                                
            }

            outputSamplesRemaining -= samplesThisTime;                                          
            outputSamplesOffset += samplesThisTime;                                             
            position += samplesThisTime;                                                        

            if (position == buffer.getNumSamples())
                position = 0;                                                                   
        }
    }
}

void BufferUnderstandAudioProcessorEditor::prepareToPlay(int, double)
{
}

/*
* Function that cleans the memory, sets the buffers to the initial state  
*/
void BufferUnderstandAudioProcessorEditor::releaseResources()
{
    fileBuffer.setSize(0, 0);
    buffer.setSize(0, 0);
}

/*
* This function is called, when the play button is clicked. It copies the samples from
* fileBuffer to the buffer and the plays this buffer.
* 
* @param fileBuffer     The buffer with audio file save in it.
*/
void BufferUnderstandAudioProcessorEditor::playButtonClicked(juce::AudioSampleBuffer fileBuffer)
{
    shutdownAudio();
    buffer.setSize(fileBuffer.getNumChannels(), 44100);
    for (int channel = 0; channel < fileBuffer.getNumChannels(); channel++)
    {
        buffer.copyFrom(channel, 0, fileBuffer, channel, 44100, 44100);
    }
    position = 0;
    play = true;
    setAudioChannels(0, 2);
}

//==============================================================================

/*
* This function paints the plugin window.
*
* @param g      Graphical object, the window of the plugin itself.
*/
void BufferUnderstandAudioProcessorEditor::paint (juce::Graphics& g)
{
    g.fillAll (juce::Component::getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));

    g.setColour (juce::Colours::white);
    g.setFont (15.0f);
}


/*
* This function sets the position of all components in the plugin window.
*/
void BufferUnderstandAudioProcessorEditor::resized()
{
    openButton.setBounds(10, 10, juce::Component::getWidth() - 20, 20);
    clearButton.setBounds(10, 40, juce::Component::getWidth() - 20, 20);
    playButton.setBounds(10, 70, juce::Component::getWidth() - 20, 20);
}
