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

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

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

/* 
   Tento kód byl sestaven na základì tutoriálù, dostupných na adrese:
   https://docs.juce.com/master/tutorial_playing_sound_files.html,
   https://docs.juce.com/master/tutorial_audio_thumbnail.html a 
   https://docs.juce.com/master/tutorial_audio_device_manager.html

   This code was built based on tutorials, which can be found on address:
   https://docs.juce.com/master/tutorial_playing_sound_files.html,
   https://docs.juce.com/master/tutorial_audio_thumbnail.html and 
   https://docs.juce.com/master/tutorial_audio_device_manager.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 basic file was generated by JUCE framework. It contains added functions  and 
* settings of GUI of the VST3 plugin. It also contains the function that handles
* visual aspects of this GUI.
*/

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

//==============================================================================
GranularSynthAudioProcessorEditor::GranularSynthAudioProcessorEditor(GranularSynthAudioProcessor& p)
    : AudioProcessorEditor(&p), audioProcessor(p)
{
    juce::Component::addAndMakeVisible(audSettings.get());

    changeState(Stopped);
    //-----------------------------------------------------------------------------
    // Setting of components bind with MIDI controler.

    juce::Component::addAndMakeVisible(group1);
    group1.setTextLabelPosition(juce::Justification::centredTop);
    group1.setText("MIDI input");

    juce::Component::addAndMakeVisible(midiInputListLabel);
    midiInputListLabel.setText("MIDI Input:", juce::dontSendNotification);
    midiInputListLabel.attachToComponent(&midiInputList, true);

    juce::Component::addAndMakeVisible(midiInputList);
    midiInputList.setTextWhenNoChoicesAvailable("No MIDI Inputs Enabled");

    midiInputListInitialize();

    AudioProcessorEditor::addAndMakeVisible(keyboardComponent);
    keyboardState.addListener(this);



    //-----------------------------------------------------------------------------
    // Settings of components bind with audio player.

    juce::Component::addAndMakeVisible(group2);
    group2.setTextLabelPosition(juce::Justification::centredTop);
    group2.setText("Data input");

    juce::Component::addAndMakeVisible(&pathToFile);
    labelInit();

    juce::Component::addAndMakeVisible(&browse);
    browse.setButtonText("Open...");
    browse.onClick = [this] { openButtonClicked(); };

    disableIfNoFileLoaded();

    juce::Component::addAndMakeVisible(&play);
    play.setButtonText("Play");
    play.onClick = [this] { playButtonClicked(); };
    play.setColour(juce::TextButton::buttonColourId, juce::Colours::green);
    play.setEnabled(false);

    juce::Component::addAndMakeVisible(&stop);
    stop.setButtonText("Stop");
    stop.onClick = [this] { stopButtonClicked(); };
    stop.setColour(juce::TextButton::buttonColourId, juce::Colours::red);
    stop.setEnabled(false);
    //-----------------------------------------------------------------------------
    // Settings of components bind with the grains.

    juce::Component::addAndMakeVisible(duration);
    duration.setText("Duration", juce::dontSendNotification);
    duration.setColour(juce::Label::textColourId, juce::Colours::lightgrey);
    duration.setFont(14.0f);

    juce::Component::addAndMakeVisible(envelope);
    envelope.setText("Envelope", juce::dontSendNotification);
    envelope.setColour(juce::Label::textColourId, juce::Colours::lightgrey);
    envelope.setFont(14.0f);

    juce::Component::addAndMakeVisible(density);
    density.setText("Density", juce::dontSendNotification);
    density.setColour(juce::Label::textColourId, juce::Colours::lightgrey);
    density.setFont(14.0f);

    juce::Component::addAndMakeVisible(timeOffset);
    timeOffset.setText("Time offset", juce::dontSendNotification);
    timeOffset.setColour(juce::Label::textColourId, juce::Colours::lightgrey);
    timeOffset.setFont(14.0f);

    juce::Component::addAndMakeVisible(volume);
    volume.setText("Volume", juce::dontSendNotification);
    volume.setColour(juce::Label::textColourId, juce::Colours::lightgrey);
    volume.setFont(14.0f);

    juce::Component::addAndMakeVisible(delka);
    delka.setSliderStyle(Rotary);
    delka.setTextBoxStyle(TextBoxBelow, false, 60, 30);
    delka.setRange(1.0, 100.0, 1);
    delka.setColour(juce::Slider::textBoxTextColourId, juce::Colours::lightgrey);
    delka.addListener(this);

    juce::Component::addAndMakeVisible(obalka);
    obalka.setSliderStyle(Rotary);
    obalka.setTextBoxStyle(TextBoxBelow, false, 60, 30);
    obalka.setRange(1.0, 3.0, 1);
    obalka.setColour(juce::Slider::textBoxTextColourId, juce::Colours::lightgrey);

    juce::Component::addAndMakeVisible(pocet);
    pocet.setSliderStyle(Rotary);
    pocet.setTextBoxStyle(TextBoxBelow, false, 60, 30);
    pocet.setRange(1.0, 10.0, 1);
    pocet.setColour(juce::Slider::textBoxTextColourId, juce::Colours::lightgrey);

    juce::Component::addAndMakeVisible(offset);
    offset.setSliderStyle(Rotary);
    offset.setTextBoxStyle(TextBoxBelow, false, 60, 30);
    offset.setRange(0.0, 120.0, 1);
    offset.setColour(juce::Slider::textBoxTextColourId, juce::Colours::lightgrey);

    juce::Component::addAndMakeVisible(hlasitost);
    hlasitost.setSliderStyle(Rotary);
    hlasitost.setTextBoxStyle(TextBoxBelow, false, 60, 30);
    hlasitost.setRange(0.0, 100.0, 1);
    hlasitost.setColour(juce::Slider::textBoxTextColourId, juce::Colours::lightgrey);

    juce::Component::addAndMakeVisible(group3);
    group3.setTextLabelPosition(juce::Justification::centredTop);
    group3.setText("Grain parameters");
    juce::Component::addAndMakeVisible(kanal1);
    kanal1.setButtonText("Channel 1");
    juce::Component::addAndMakeVisible(kanal2);
    kanal2.setButtonText("Channel 2");
    juce::Component::addAndMakeVisible(kanal3);
    kanal3.setButtonText("Channel 3");
    juce::Component::addAndMakeVisible(kanal4);
    kanal4.setButtonText("Channel 4");
    juce::Component::addAndMakeVisible(kanal5);
    kanal5.setButtonText("Channel 5");
    juce::Component::addAndMakeVisible(kanal6);
    kanal6.setButtonText("Channel 6");
    juce::Component::addAndMakeVisible(kanal7);
    kanal7.setButtonText("Channel 7");
    juce::Component::addAndMakeVisible(kanal8);
    kanal8.setButtonText("Channel 8");

    juce::Component::addAndMakeVisible(makeGrain);
    makeGrain.setButtonText("Create Grain");
    makeGrain.setColour(juce::TextButton::buttonColourId, juce::Colour::fromRGB(9, 154, 237));
    makeGrain.onClick = [this] { createGrain(); };

    juce::Component::addAndMakeVisible(resetGrain);
    resetGrain.setButtonText("Reset Grain");
    resetGrain.onClick = [this] { resetButtonClicked(); };

    juce::Component::addAndMakeVisible(playGrain);
    playGrain.setButtonText("Play Grain");
    playGrain.onClick = [this] { grainPlayButtonClicked(); };
    //-----------------------------------------------------------------------------
    // Settings of components bind with visualization of reproduction

    juce::Component::addAndMakeVisible(group4);
    group4.setTextLabelPosition(juce::Justification::centredTop);
    group4.setText("Output audio pattern");
    juce::Component::addAndMakeVisible(hvezda);
    hvezda.setButtonText("Star");
    juce::Component::addAndMakeVisible(kruh);
    kruh.setButtonText("Ring");
    juce::Component::addAndMakeVisible(kruh2);
    kruh2.setButtonText("Ring(moving)");
    juce::Component::addAndMakeVisible(pulkruhL);
    pulkruhL.setButtonText("Semicircle(left)");
    juce::Component::addAndMakeVisible(pulkruhR);
    pulkruhR.setButtonText("Semicircle(right)");
    juce::Component::addAndMakeVisible(hodiny);
    hodiny.setButtonText("Hourglass");
    juce::Component::addAndMakeVisible(showpattern);
    showpattern.setButtonText("SHOW PATTERN");
    showpattern.setColour(juce::TextButton::buttonColourId, juce::Colour::fromRGB(9, 154, 237));
    showpattern.onClick = [this] { showPatternClicked(500); };

    juce::Component::addAndMakeVisible(group5);
    group5.setTextLabelPosition(juce::Justification::centredTop);
    group5.setText("Audio visualization");

    juce::Component::addAndMakeVisible(chanel1);
    chanel1.setButtonText("CHANNEL 1");
    chanel1.setColour(juce::TextButton::buttonColourId, juce::Colours::darkgrey);
    juce::Component::addAndMakeVisible(chanel2);
    chanel2.setButtonText("CHANNEL 2");
    chanel2.setColour(juce::TextButton::buttonColourId, juce::Colours::darkgrey);
    juce::Component::addAndMakeVisible(chanel3);
    chanel3.setButtonText("CHANNEL 3");
    chanel3.setColour(juce::TextButton::buttonColourId, juce::Colours::darkgrey);
    juce::Component::addAndMakeVisible(chanel4);
    chanel4.setButtonText("CHANNEL 4");
    chanel4.setColour(juce::TextButton::buttonColourId, juce::Colours::darkgrey);
    juce::Component::addAndMakeVisible(chanel5);
    chanel5.setButtonText("CHANNEL 5");
    chanel5.setColour(juce::TextButton::buttonColourId, juce::Colours::darkgrey);
    juce::Component::addAndMakeVisible(chanel6);
    chanel6.setButtonText("CHANNEL 6");
    chanel6.setColour(juce::TextButton::buttonColourId, juce::Colours::darkgrey);
    juce::Component::addAndMakeVisible(chanel7);
    chanel7.setButtonText("CHANNEL 7");
    chanel7.setColour(juce::TextButton::buttonColourId, juce::Colours::darkgrey);
    juce::Component::addAndMakeVisible(chanel8);
    chanel8.setButtonText("CHANNEL 8");
    chanel8.setColour(juce::TextButton::buttonColourId, juce::Colours::darkgrey);
    //-----------------------------------------------------------------------------
    // Settings of components for chosing adequate audio interface and information box.

    juce::Component::addAndMakeVisible(group6);
    group6.setTextLabelPosition(juce::Justification::centredTop);
    group6.setText("Audio settings");

    juce::Component::addAndMakeVisible(group7);
    group7.setTextLabelPosition(juce::Justification::centredTop);
    group7.setText("Info");

    //-----------------------------------------------------------------------------
    // Registration of .wav and .aiff format and setting the AudioTransportSource object.

    formatManager.registerBasicFormats();      
    transportSource.addChangeListener(this);   
    //-----------------------------------------------------------------------------
    // Final audio settings and window size settings.

    startTimer(10);
    setAudioChannels(0, 8);
    juce::Component::setSize(1700, 780);
}


/*
* Destructor of the GranularSynthAudioProcessorEditor class, class generated by JUCE framework.
* It releases the blocks of data from memory.
*/
GranularSynthAudioProcessorEditor::~GranularSynthAudioProcessorEditor()
{
    shutdownAudio();
    releaseResources();
    keyboardState.removeListener(this);
    other.removeMidiInputDeviceCallback(juce::MidiInput::getAvailableDevices()[midiInputList.getSelectedItemIndex()].identifier, this);
    
}


/*
* This function prints the text into thumbnail based on the state of program. 
* 
* @param g                  Graphical object, the window of the plugin itself.
* @param thumbnailBounds    The boundery of the thumbnail object. 
*/
void GranularSynthAudioProcessorEditor::paintIfNoFileLoaded(juce::Graphics& g, const juce::Rectangle<int>& thumbnailBounds)
{
    disableIfNoFileLoaded();
    if(fileTypeNotSupported == true)
        g.drawFittedText("This file type is not supported. Please load the .wav or .aiff file.", thumbnailBounds, juce::Justification::centred, 1);
    else
        g.drawFittedText("No file loaded. Browse for file or drag the file to this window.", thumbnailBounds, juce::Justification::centred, 1);
}


/*
* If the file is loaded to the plugin, the waveshape representing its content is rendered.
* 
* @param g                  Graphical object, the window of the plugin itself.
* @param thumbnailBounds    The boundery of the thumbnail object. 
*/
void GranularSynthAudioProcessorEditor::paintIfFileLoaded(juce::Graphics& g, const juce::Rectangle<int>& thumbnailBounds)
{
    enableIfFileLoaded();
    float min, max;

    float audioLength = (float)thumbnail.getTotalLength();
    thumbnail.getApproximateMinMax(0.0, transportSource.getLengthInSeconds(), 0, min, max);
    if (max > 0.5) {
        thumbnail.drawChannels(g, thumbnailBounds, 0.0, audioLength, 0.7f);
    }
    else {
        thumbnail.drawChannels(g, thumbnailBounds, 0.0, audioLength, 1.7f);
    }

    if (audioLength > 0.0f)
    {
        float audioPosition = (float)transportSource.getCurrentPosition();
        float drawPosition = (audioPosition / audioLength) * (float)thumbnailBounds.getWidth() + (float)thumbnailBounds.getX();                                // [13]

        g.setColour(juce::Colours::darkred);
        g.drawLine(drawPosition, (float)thumbnailBounds.getY(), drawPosition, (float)thumbnailBounds.getBottom(), 2.0f);
    }
}


/*
* This function is called everytime the Timer ends the cycle. It is called to repainted objects
* like cursor, beckground of the TextButton ac.
*/
void GranularSynthAudioProcessorEditor::timerCallback()
{
    if (showPat) {
        showPattern();
    }
    juce::Component::repaint();
}


/*
* This function reacts on event, when the mouse button is clicked. It controls the cursor which 
* sets the position of the AudioTransportSource object.
* 
* @param event      The event, which caused the function calling.
*/
void GranularSynthAudioProcessorEditor::mouseDown(const juce::MouseEvent& event)
{
    double duration = transportSource.getLengthInSeconds();
    juce::Rectangle<int> thumbnailBounds(20, 200, 765, 140);            
    if (duration > 0.0)
    {
        float clickPosition = event.position.x;
        double audioPosition = ((clickPosition - 20) / (float)thumbnailBounds.getWidth()) * duration;
        transportSource.setPosition(audioPosition);
    }
}

/*
* Function called by repaint function. It paints and repaints all the visual objects. The function
* prints the information box for user, repaint the cursor of the audio player and paint the thumbnails
* content.
* 
* @param g       Graphical object, the window of the plugin itself.      
*/
void GranularSynthAudioProcessorEditor::paint (juce::Graphics& g)
{
    g.fillAll(juce::Component::getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId));
    g.setColour(juce::Colours::white);
    g.setFont(18.0f);

    g.drawFittedText("Please make sure your audio device has at least 8 output channels. Then select these\noutputs in Output section above.", 
        juce::Rectangle<int>(1360,460,335,50), juce::Justification::left, 1);
    g.drawFittedText("In MIDI input section you can choose your keybord. You can open an audio file with Open... button or by dragging it into the\n"
        "window. Note that this plug-in supports\njust .WAV and .AIFF files.", juce::Rectangle<int>(1360, 520, 335, 70), juce::Justification::left, 1);
    g.drawFittedText("After loading the file you can proccess the grains.Red pointer shows the start position of those grains.You can click the waveform\n"
        "to change its position.", juce::Rectangle<int>(1360, 617, 335, 60), juce::Justification::left, 1);
    g.drawFittedText("Finally you need to choose the playback pattern, showed by the SHOW PATTERN\nbutton and hit the key.", juce::Rectangle<int>(1360, 695, 335, 60), juce::Justification::left, 1);

    juce::Rectangle<int> thumbnailBounds(20, 200, 765, 140);            //pozor pri zmene velikosti thumbnailu
    g.setColour(juce::Colours::darkgrey);
    g.fillRect(thumbnailBounds);
    g.setColour(juce::Colours::lightgrey);

    if (thumbnail.getNumChannels() == 0)
        paintIfNoFileLoaded(g, thumbnailBounds);
    else if (fileTypeNotSupported == true) {
        paintIfNoFileLoaded(g, thumbnailBounds);
        disableIfFileNotSupported();
    }
    else
        paintIfFileLoaded(g, thumbnailBounds);
}


/*
* This function sets the positions of all the components in the plugin window.
* Each component has its own position, height and width.
*/
void GranularSynthAudioProcessorEditor::resized()
{
    group1.setBounds(5, 15, 795, 75);
    midiInputListLabel.setBounds(15, 40, 120, 30);
    midiInputList.setBounds(130, 40, 655, 30);
    group2.setBounds(5, 95, 795, 265);
    pathToFile.setBounds(20, 120, 500, 30);
    browse.setBounds(530, 120, 255, 30);
    play.setBounds(20, 160, 378, 30);
    stop.setBounds(407, 160, 378, 30);
    group3.setBounds(5, 365, 795, 200);
    duration.setBounds(35, 380, 120, 30);
    envelope.setBounds(145, 380, 120, 30);
    density.setBounds(260, 380, 120, 30);
    timeOffset.setBounds(357, 380, 120, 30);
    volume.setBounds(478, 380, 120, 30);
    delka.setBounds(10, 400, 110, 120);
    obalka.setBounds(120, 400, 110, 120);
    pocet.setBounds(230, 400, 110, 120);
    offset.setBounds(340, 400, 110, 120);
    hlasitost.setBounds(450, 400, 110, 120);
    kanal1.setBounds(22, 515, 80, 50);
    kanal2.setBounds(117, 515, 80, 50);
    kanal3.setBounds(212, 515, 80, 50);
    kanal4.setBounds(307, 515, 80, 50);
    kanal5.setBounds(402, 515, 80, 50);
    kanal6.setBounds(497, 515, 80, 50);
    kanal7.setBounds(592, 515, 80, 50);
    kanal8.setBounds(687, 515, 80, 50);
    makeGrain.setBounds(560, 390, 225, 30);
    resetGrain.setBounds(560, 430, 225, 30);
    playGrain.setBounds(560, 490, 225, 30);
    group4.setBounds(805, 15, 535, 85);
    hvezda.setBounds(820, 30, 120, 30);
    pulkruhL.setBounds(920, 30, 120, 30);
    kruh.setBounds(1050, 30, 120, 30);
    hodiny.setBounds(820, 60, 120, 30);
    pulkruhR.setBounds(920, 60, 120, 30);
    kruh2.setBounds(1050, 60, 120, 30);
    showpattern.setBounds(1180, 45, 145, 30);
    group5.setBounds(805, 105, 535, 460);
    chanel1.setBounds(1020, 135, 100, 30);
    chanel5.setBounds(1020, 515, 100, 30);
    chanel7.setBounds(820, 325, 100, 30);
    chanel3.setBounds(1220, 325, 100, 30);
    chanel8.setBounds(885, 215, 100, 30);
    chanel6.setBounds(885, 435, 100, 30);
    chanel2.setBounds(1165, 215, 100, 30);
    chanel4.setBounds(1165, 435, 100, 30);
    group6.setBounds(1345, 15, 350, 420);
    audSettings->setBounds(1360, 30, 335, 350);
    group7.setBounds(1345, 440, 350, 325);
       
    keyboardComponent.setBounds(200, 570, 1000, 150);
}