//------------------------------------------------------------------------------
//
//  Project:   Anonymizer
//
//             Brno University of Technology
//             Faculty of Information Technology
//
//------------------------------------------------------------------------------
//
//      This project was financially supported by project VG20102015006 funds
//           provided by Ministry of the Interior of the Czech republic.
//
//------------------------------------------------------------------------------
/*!

@file      mainwindow.cpp
@brief     Implementation of methods ...
@details   Details
@authors   Martin Borek (mborekcz@gmail.com)
@authors   Filip Orsag (orsag@fit.vutbr.cz)
@date      2014-2015
@note      This project was supported by MV CR project VG20102015006.
@copyright BUT OPEN SOURCE LICENCE (see License.txt)

*/

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "selection.h"
#include "colors.h"
#include "objectshape.h"

#include <cereal/archives/json.hpp>
#include <cereal/archives/xml.hpp>
#include <utility> // for std::pair
#include <fstream> // for cereal output

#include <QtGui>
#include <QDebug>
#include <QFileDialog>
#include <QPainter>
#include <QProgressDialog>
#include <QInputDialog>
#include <QColorDialog>
#include <QMessageBox>
#include <QMenu>
#include <QTranslator>
#include <QSplitter>
#include <QDockWidget>
#include <helpbrowser.h>

#define VIDEOTRACKING_10MS2S 100 // 1 second is 100*10 milliseconds
#define VIDEOTRACKING_TIMER_CONSTANT 1.5
#define VIDEOTRACKING_TIMER_CONSTANT_FAST 0.5
#define VIDEOTRACKING_TIMER_CONSTANT_SLOW 0.2

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    applicationName("Video Anonymizer"),
    ui(new Ui::MainWindow)
{

    settings = new QSettings("FIT", applicationName, this); // application and company name
    qApp->setApplicationName(applicationName);
    this->setWindowTitle(applicationName);

    // TRANSLATOR - BEGINNING
    QTranslator * translator = new QTranslator(this);
    bool checkEnglish = false; // To find out what language should be checked in application menu

    if (settings->contains("language"))
    {   // load language

        qDebug() << "Language loaded";

        QString languageFile = settings->value("language").toString();
        translator->load(languageFile);

        if (languageFile.isEmpty()) // Empty string means default language (English) is set
            checkEnglish = true;
    }
    else
    {// set language according locale

        qDebug() << "Language set according locale";

        QString locale = QLocale::system().name();
        if (!translator->load(QString("video_anonymizer_" + locale)))
            checkEnglish = true; // Loading of language was not succesful, check English (default) option
        else // English is default
            checkEnglish = false;
    }


    // apply requested language
    // must be done before setting ui (ui->setupUi) and setting any text in widgets
    qApp->installTranslator(translator);
    // TRANSLATOR - END

    ui->setupUi(this);

    // Set language menu
    if (checkEnglish)
        ui->actionEnglish->setChecked(true);
    else
        ui->actionCzech->setChecked(true);

    tracker = nullptr; // Will be initialized when new video is loaded
    timer = new QTimer(this); //Is set when play button clicked

    // Should original video be showed?
    if (settings->contains("showOriginalVideo"))
    { // Is showOriginalVideo variable set in settings?

        showOriginalVideo = settings->value("showOriginalVideo").toBool();
    }
    else
    {   // Not set => set default value
        showOriginalVideo = true;
        settings->setValue("showOriginalVideo", showOriginalVideo);
    }
    ui->originalVideoFrame->setVisible(showOriginalVideo);

    // Display time / frame numbers
    if (settings->contains("showTime"))
    { // Is displayTime variable set in settings?
        qDebug() << "Time/Frame num loaded";

        displayTime = settings->value("showTime").toBool();
    }
    else
    {
        displayTime = true;
        settings->setValue("showTime", displayTime); // Not set => set default
    }

    initial_application_settings();

    computingInfoText = tr("Computing positions of tracked object");
    computingInfoTitle = tr("Computing");

    //progressDialog = new QProgressDialog(tr("Tracking"), tr("Cancel"), 0, 0, this);
    //progressDialog->setModal(true);

    // Setup help data:
    //helpEngine = new QHelpEngine("help/help_cs.qhc", this);

    QString helpDir(QCoreApplication::applicationDirPath() +  "/" + tr("help_en.qhc"));
    qDebug() <<  "help:" << helpDir;
    helpEngine = new QHelpEngine(helpDir, this);
    if (!helpEngine->setupData())
    {
        qDebug() << "Help not loaded successfully";
        qDebug() << helpEngine->error();
    }
    else
        qDebug() << "Help loaded";

    connect_signals(); // Connects GUI elements with slots
}


MainWindow::~MainWindow()
{
    timer->stop(); // "timer" is deleted automatically; stopping is enough

    //delete tracker; // is smart pointer => will be deleted automatically
    delete ui;

    qDebug() << "Destruct: MainWindow destroyed";
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    if (!ask_save_project())
        event->ignore();
    else
        event->accept();
}

/** Using reload_video_label() instead
void MainWindow::resizeEvent(QResizeEvent *)
{
    playerWidget->setGeometry(0, 0, ui->videoWidget->width(), ui->videoWidget->height());
    ui->videoLabel->setGeometry(0, 0, ui->videoFrame->width(), ui->videoFrame->height());
    timeLabel->setGeometry(0, 0, ui->timeWidget->width(), ui->timeWidget->height());
}
*/

void MainWindow::reload_video_label()
{
    // make size of originalVideoFrame fixed and videoFrame will expand
    ui->originalVideoFrame->setFixedWidth(ui->videosLayout->geometry().width() / 2.5);

    if (tracker)
    {
        ui->videoLabel->set_image(frame, ui->videoFrame->width(), ui->videoFrame->height());

        if (showOriginalVideo)
            ui->originalVideoLabel->set_image(originalFrame, ui->originalVideoFrame->width(), ui->originalVideoFrame->height());
    }

}

void MainWindow::pause()
{
    isPlaying = false;
    ui->playButton->setText(tr("Play"));
    timer->stop();

}

/* Play / Pause */
void MainWindow::play()
{


    if (isPlaying)
        pause();
    else
    {
        if (endOfVideo) // End of video => Start from beginning
            show_first_frame();

        isPlaying = true;

        ui->playButton->setText(tr("Pause"));


        if (!tracker)
        {
            qDebug() << "WARNING: play() called with no video loaded";
            return;
        }

        timer->start(); // Displays new frames in given interval.
      }
}

/* Stop playing, return to first frame and display it */
void MainWindow::stop()
{
    isPlaying = false;

    timer->stop();

    ui->playButton->setText(tr("Play"));
    ui->stepBackButton->setEnabled(true);
    ui->positionSlider->setEnabled(true);
    set_application_menu();

    show_first_frame();
}

/* Select a file and open it */
void MainWindow::open_video()
{
    if (tracker)
    { // Project exists, ask to save/discard
        if (!ask_save_project())
            return; // User canceled the dialog, do not open a new video

        initial_application_settings(); // Restores default application settings

    }

    if (!open_video_dialog())
        return; // Cancel button pressed

    QProgressDialog progressDialog(tr("Opening video"), tr("Cancel"), 0, 0, this);
    progressDialog.setWindowTitle(tr("Opening"));
    progressDialog.setModal(true);
    progressDialog.show();

    try
    {
        tracker = std::unique_ptr<VideoTracker>(new VideoTracker(inputFileName, &progressDialog));

    } catch (OpenException) {

        QMessageBox *alert = new QMessageBox(this);
        alert->setWindowTitle(tr("Opening video"));
        alert->setText(tr("This video file cannot be opened."));
        alert->exec();

        return;

    } catch (UserCanceledOpeningException) {

        QMessageBox *alert = new QMessageBox(this);
        alert->setWindowTitle(tr("Opening video"));
        alert->setText(tr("Video opening was canceled."));
        alert->exec();

        return;
    }
    //progressDialog->reset();
    open_video_successful();
}

bool MainWindow::open_video_dialog()
{
    QFileDialog fileDialog(this);
    fileDialog.setWindowTitle(tr("Open Video"));

    if (settings->contains("lastPath"))
    { // begin with the directory where the last video was opened
        QString lastPath = settings->value("lastPath").toString();
        fileDialog.setDirectory(lastPath);
    }

    QStringList nameFilters;

    // List of abbreviations considered to belong to video files
    nameFilters << tr("Video files") + " (*.3g2 *.3gp *.asf *.avi *.flv *.m2v *.m4p *.m4v *.mkv *.mov *.mp2 *.mp4 "
                   "*.mpe *.mpeg *.mpg *.mpv *.ogg *.ogv *.qt *.rm *.rmvb *.vob *.webm *.wmv *.yuv)";
    nameFilters << tr("All files") + " (*)";

    fileDialog.setNameFilterDetailsVisible(false);
    //fileDialog.setNameFilterDetailsVisible(true); //there is too many abbreviations to show
    fileDialog.setNameFilters(nameFilters);

    fileDialog.setFileMode(QFileDialog::ExistingFile);
    fileDialog.setViewMode(QFileDialog::Detail);

    if (fileDialog.exec() && !fileDialog.selectedFiles().isEmpty())
    {
        // save directory path for next opening:

        inputFileName = fileDialog.selectedFiles().takeFirst().toStdString();

        QString directoryPath = fileDialog.directory().absolutePath();

        settings->setValue("lastPath", directoryPath);

        return true;
    }
    else
        return false; // Cancel button pressed
}

void MainWindow::open_video_successful()
{
    qDebug() << "Video correctly loaded";
    show_appearance_tab();

    endOfVideo = false;

    timerInterval = 1000 / (tracker->get_fps());
    timer->setInterval(timerInterval);

    ui->timeLabel->set_total_time(tracker->get_total_time());
    ui->timeLabel->set_frame_count(tracker->get_frame_count());

    if (displayTime)
        ui->timeLabel->display_time(0); // First frame is at time 0
    else
        ui->timeLabel->display_frame_num(1); // First frame is at number 1

    ui->positionSlider->setMinimum(1);

    //ui->positionSlider->setMaximum(tracker->get_total_time()/VIDEOTRACKING_10MS2S); // value is in 0.1s
    ui->positionSlider->setMaximum(tracker->get_frame_count()); // value is in 0.1s

    // Disable slider steps. Jump straight to pressed position instead (signal pressed_position);
    ui->positionSlider->setSingleStep(0);
    ui->positionSlider->setPageStep(0);

    ui->positionSlider->setEnabled(true);
    ui->playerControlsWidget->setEnabled(true);

    //ui->createOutputButton->setEnabled(true);
    ui->actionCreateOutput->setEnabled(true);

    ui->objectsControlWidget->setEnabled(true);

    ui->originalVideoTextLabel->setVisible(true);

    set_application_menu();
    show_next_frame(); // Displays first frame
}

void MainWindow::end_of_video()
{
    qDebug() << "End of video.";
    //tracker->set_frame_position(0);
    //endOfVideo = true;
    isPlaying = false;

    ui->playButton->setText(tr("Play"));
    ui->stepBackButton->setEnabled(true);
    ui->positionSlider->setEnabled(true);

    timer->stop();

    set_application_menu();
}

void MainWindow::step_back()
{
    pause();
    show_previous_frame();
}

void MainWindow::step_forward()
{
    pause();
    show_next_frame();
}

void MainWindow::show_frame()
{
    if (tracker->get_frame_number() == tracker->get_frame_count())
        endOfVideo = true;
    else
        endOfVideo = false;

    //ui->positionSlider->setValue(tracker->get_time_position()/VIDEOTRACKING_10MS2S);
    ui->positionSlider->setValue(tracker->get_frame_number());

    if (displayTime)
        ui->timeLabel->display_time(tracker->get_time_position());
    else
        ui->timeLabel->display_frame_num(tracker->get_frame_number());

    //qDebug() << "frame number" << tracker->get_frame_number();

    ui->videoLabel->set_image(frame, ui->videoFrame->width(), ui->videoFrame->height());

    if (showOriginalVideo)
        ui->originalVideoLabel->set_image(originalFrame, ui->originalVideoFrame->width(), ui->originalVideoFrame->height());

    if (tracker->get_objects_count())
        set_trajectory_tab(ui->objectsBox->currentData().toUInt(), false, false);

    //qDebug()<< "slider position: " << ui->positionSlider->value();
}

void MainWindow::show_next_frame()
{
    // Save timestamp to be restored if operation is aborted
    int64_t currentTimestamp = tracker->get_frame_timestamp();

    QProgressDialog progressDialog(computingInfoText, tr("Cancel"), 0, 0, this);
    progressDialog.setWindowTitle(computingInfoTitle);
    progressDialog.setModal(true);
    try
    {
        if (!tracker->get_next_frame(frame, originalFrame, showOriginalVideo, &progressDialog))
        {
            qDebug() << "No next frame to show";
            end_of_video(); // Frame was not read successfully -> end of video
        }
        else
            show_frame();
    } catch (UserCanceledException)
    {
        progressDialog.cancel();
        show_frame_by_timestamp(currentTimestamp); // restore player position before the computing started
        return;
    }
}

void MainWindow::show_previous_frame()
{
    // Save timestamp to be restored if operation is aborted
    int64_t currentTimestamp = tracker->get_frame_timestamp();

    QProgressDialog progressDialog(computingInfoText, tr("Cancel"), 0, 0, this);
    progressDialog.setWindowTitle(computingInfoTitle);
    progressDialog.setModal(true);
    try
    {
        if (!tracker->get_previous_frame(frame, originalFrame, showOriginalVideo, &progressDialog))
            qDebug() << "No previous frame to show";
        else
            show_frame();
    } catch (UserCanceledException)
    {
        progressDialog.cancel();
        show_frame_by_timestamp(currentTimestamp); // restore player position before the computing started
        return;
    }
}

void MainWindow::show_frame_by_time(int position)
{
    // Save timestamp to be restored if operation is aborted
    int64_t currentTimestamp = tracker->get_frame_timestamp();

    QProgressDialog progressDialog(computingInfoText, tr("Cancel"), 0, 0, this);
    progressDialog.setWindowTitle(computingInfoTitle);
    progressDialog.setModal(true);
    try
    {
        if (!tracker->get_frame_by_time(frame, originalFrame, showOriginalVideo,
                                        position*VIDEOTRACKING_10MS2S, &progressDialog))
        {
            qDebug() << "WARNING: Cannot read frame at given time position";
            end_of_video();
        }
        else
            show_frame();
    } catch (UserCanceledException)
    {
        progressDialog.cancel();
        show_frame_by_timestamp(currentTimestamp); // restore player position before the computing started
        //progressDialog->reset();
        return;
    }
}

void MainWindow::show_frame_by_timestamp(int64_t timestamp)
{
    // Save timestamp to be restored if operation is aborted
    int64_t currentTimestamp = tracker->get_frame_timestamp();

    QProgressDialog progressDialog(computingInfoText, tr("Cancel"), 0, 0, this);
    progressDialog.setWindowTitle(computingInfoTitle);
    progressDialog.setModal(true);
    try
    {
        if (!tracker->get_frame_by_timestamp(frame, originalFrame, showOriginalVideo, timestamp, &progressDialog))
        {
            qDebug() << "ERROR: show_frame_by_timestamp() - cannot read frame";
            end_of_video();
        }
        else
            show_frame();
    } catch (UserCanceledException)
    {
        progressDialog.cancel();
        show_frame_by_timestamp(currentTimestamp); // restore player position before the computing started
        //progressDialog->reset();
        return;
    }
}

void MainWindow::show_frame_by_number(unsigned long position)
{
    // Save timestamp to be restored if operation is aborted
    int64_t currentTimestamp = tracker->get_frame_timestamp();

    QProgressDialog progressDialog(computingInfoText, tr("Cancel"), 0, 0, this);
    progressDialog.setWindowTitle(computingInfoTitle);
    progressDialog.setModal(true);
    try
    {
        if (!tracker->get_frame_by_number(frame, originalFrame, showOriginalVideo, position, &progressDialog))
        {
            qDebug() << "WARNING: Cannot read frame at given number position";
            end_of_video();
        }
        else
            show_frame();
    } catch (UserCanceledException)
    {
        progressDialog.cancel();
        show_frame_by_timestamp(currentTimestamp); // restore player position before the computing started
        //progressDialog->reset();
        return;
    }
}

void MainWindow::show_first_frame()
{
    // Save timestamp to be restored if operation is aborted
    int64_t currentTimestamp = tracker->get_frame_timestamp();

    QProgressDialog progressDialog(computingInfoText, tr("Cancel"), 0, 0, this);
    progressDialog.setWindowTitle(computingInfoTitle);
    progressDialog.setModal(true);
    try
    {
        if (!tracker->get_first_frame(frame, originalFrame, showOriginalVideo, &progressDialog))
        {
            qDebug() << "WARNING: Cannot get first frame";
            end_of_video();
        }
        else
            show_frame();
    } catch (UserCanceledException)
    {
        progressDialog.cancel();
        show_frame_by_timestamp(currentTimestamp); // restore player position before the computing started
        //progressDialog->reset();
        return;
    }

}

void MainWindow::faster()
{
    timerSpeed++;
    speed_general();
}

void MainWindow::slower()
{
    if (timerSpeed <= -4)
        return; // Cannot make it slower

    timerSpeed--;
    speed_general();
}

void MainWindow::original_speed()
{
    timerSpeed = 0;
    speed_general();
}

void MainWindow::speed_general()
{
    double speed = 1;

    if (timerSpeed > 0)
        speed += timerSpeed*VIDEOTRACKING_TIMER_CONSTANT_FAST;
    else if (timerSpeed < 0)
        speed += timerSpeed*VIDEOTRACKING_TIMER_CONSTANT_SLOW;

    timer->setInterval(timerInterval / speed);
    ui->speedLabel->setText(tr("Speed:") + " " + QString::number(speed, 'f', 2) + "x");
}

void MainWindow::create_output()
{
    pause();

    // Save timestamp to restore it after the operation is complete
    int64_t currentTimestamp = tracker->get_frame_timestamp();

    if (!ask_save_appearance_changes())
        return;

    std::string input = inputFileName;
    auto extensionPosition = input.find_last_of('.');

    std::string defaultFileName;
    if (extensionPosition != std::string::npos)
    { // extension is present in the filename
        defaultFileName = input.substr(0, extensionPosition) + "_output";
        defaultFileName += input.substr(extensionPosition);
    }
    else
        defaultFileName = input + "_output";

    QString filename = QFileDialog::getSaveFileName(this, tr("Select output file"),
                                                    QString::fromStdString(defaultFileName));
    if (filename.isEmpty())
        return;

   // qApp->processEvents();

    qDebug() << "Creating output started";
    QProgressDialog fileProgressDialog(tr("Creating output file..."), tr("Cancel"), 0, tracker->get_total_time(), this);
    fileProgressDialog.setWindowTitle(tr("Creating Output"));
    fileProgressDialog.setWindowModality(Qt::WindowModal);
    //fileProgressDialog.setWindowFlags(Qt::Window | Qt::);
    fileProgressDialog.setAutoReset(false);
    fileProgressDialog.setMinimumDuration(0); // Always show the dialog; Otherwise it would be shower only if process would take at least certain time

    QProgressDialog trackingProgressDialog(tr("Computing trajectories of all tracked objects"), tr("Cancel"), 0, 0, this);
    fileProgressDialog.setWindowTitle(computingInfoTitle);
    trackingProgressDialog.setModal(true);
    //trackingProgressDialog.show();
    QMessageBox *alert = new QMessageBox(this);
    alert->setWindowTitle(tr("Creating Output"));

    std::string inFileExtension = "";
    auto inExtensionPosition  = inputFileName.find_last_of('.');
    if (inExtensionPosition != std::string::npos)
         inFileExtension = inputFileName.substr(inExtensionPosition);

    try
    {
        tracker->create_output(filename.toStdString(), fileProgressDialog, &trackingProgressDialog, inFileExtension);
        alert->setText(tr("Output successfully created"));
    } catch (OutputException) {
        qDebug() << "ERROR: Output creating not successful";
        alert->setText(tr("Error occured when creating the output file. Output was not created."));
    } catch (UserCanceledException) {
        qDebug() << "User canceled output creation";
        alert->setText(tr("Creating output file was canceled. Output was not created."));
    }

    fileProgressDialog.cancel();

    alert->exec();

    show_frame_by_timestamp(currentTimestamp); // restore player position before the computing started
}

void MainWindow::slider_pressed()
{
    timer->stop();
}

void MainWindow::slider_released()
{
    //show_frame_by_number(ui->positionSlider->value());

    if (isPlaying)
    {
        timer->start();
    }
}

void MainWindow::set_form_values()
{
    colorsMap[Colors::BLACK] = std::make_pair(tr("Black").toStdString(), ObjectColor(0, 0, 0));
    colorsMap[Colors::GRAY] = std::make_pair(tr("Gray").toStdString(), ObjectColor(128, 128, 128));
    colorsMap[Colors::SILVER] = std::make_pair(tr("Silver").toStdString(), ObjectColor(192, 192, 192));
    colorsMap[Colors::WHITE] = std::make_pair(tr("White").toStdString(), ObjectColor(255, 255, 255));
    colorsMap[Colors::RED] = std::make_pair(tr("Red").toStdString(), ObjectColor(255, 0, 0));
    colorsMap[Colors::GREEN] = std::make_pair(tr("Green").toStdString(), ObjectColor(0, 255, 0));
    colorsMap[Colors::BLUE] = std::make_pair(tr("Blue").toStdString(), ObjectColor(0, 0, 255));
    colorsMap[Colors::YELLOW] = std::make_pair(tr("Yellow").toStdString(), ObjectColor(255, 255, 0));
    colorsMap[Colors::CYAN] = std::make_pair(tr("Cyan").toStdString(), ObjectColor(0, 255, 255));
    colorsMap[Colors::MAGENTA] = std::make_pair(tr("Magenta").toStdString(), ObjectColor(255, 0, 255));

    shapesMap.clear();
    shapesMap[ObjectShape::RECTANGLE] = tr("Rectangle");
    shapesMap[ObjectShape::ELLIPSE] = tr("Ellipse");
}

void MainWindow::show_anchors_menu(QListWidgetItem *item)
{
    editingAnchorItem = static_cast<AnchorItem *>(ui->anchorsWidget->itemWidget(item));
    ItemType type = editingAnchorItem->get_type();

    QAction *showFrame = nullptr;
    QAction *changePosition = nullptr;
    QAction *setEndFrame = nullptr;
    QAction *setVideoEnd = nullptr;
    QAction *deleteItem = nullptr;

    QMenu *menu = new QMenu(tr("Edit item"), this);

    if (editingAnchorItem->is_set())
    {
        showFrame = menu->addAction(tr("Show frame"));
    }

    if (type == ItemType::CHANGE || type == ItemType::BEGINNING)
    {
        changePosition = menu->addAction(tr("Change position"));
        changePosition->setToolTip(tr("Change the initial position of this object"));

        if (type == ItemType::CHANGE)
        {
            deleteItem = menu->addAction(tr("Delete item"));
            deleteItem->setToolTip(tr("Delete this item"));
        }
    }
    else if (type == ItemType::END)
    {
        if (editingAnchorItem->is_set())
        {
            setEndFrame = menu->addAction(tr("Change last frame of tracking"));
            setEndFrame->setToolTip(tr("Change the frame where tracking for this object shall stop"));

            setVideoEnd = menu->addAction(tr("Set tracking till the end"));
            setVideoEnd->setToolTip(tr("Set tracking till the end of the video for this object"));
        }
        else // Show different menu label for "setEndFrame", but action is the same
        {
            setEndFrame = menu->addAction(tr("Set last frame of tracking"));
            setEndFrame->setToolTip(tr("Set a frame where tracking for this object shall stop"));
        }
    }

    editingAnchorItem->set_highlight(true); // Keeps the item highlighted even with context menu over it
    //menu->exec(editingAnchorItem->mapToGlobal(QPoint(0,0))); // Show at widget position

    QAction *selectedOption = menu->exec(QCursor::pos()); // Show at cursor position
    editingAnchorItem->set_highlight(false);

    if (!selectedOption)
        return; // No option was selected

    pause(); // Pause video playing (in case it was playing)

    if (selectedOption == showFrame)
    {
        qDebug() << "Show frame";
        show_frame_by_timestamp(editingAnchorItem->get_timestamp());
    }
    else if (selectedOption == changePosition)
    {
        qDebug() << "Change position";

        editingAnchorItem->set_highlight(true);

        // Goes to the event frame so that user does not need to look for it
        show_frame_by_timestamp(editingAnchorItem->get_timestamp());

        set_selection(SelectionState::CHANGE_POSITION);
    }
    else if (selectedOption == setEndFrame)
    {
        set_end_frame();
    }
    else if (selectedOption == setVideoEnd)
    {
        set_video_end();
    }

    else if (selectedOption == deleteItem)
    {
        qDebug() << "Delete item";

        // Ask if they really want to delete this item
        QMessageBox *msgBox = new QMessageBox(this);
        msgBox->setWindowTitle(tr("Delete trajectory item"));
        msgBox->setText(tr("Do you really want to delete this item?"));
        msgBox->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
        msgBox->setButtonText(QMessageBox::Yes, tr("Yes"));
        msgBox->setButtonText(QMessageBox::No, tr("No"));
        msgBox->setDefaultButton(QMessageBox::Yes);

        if (msgBox->exec() == QMessageBox::Yes)
        { // Deletion confirmed by user
            unsigned int objectID = ui->objectsBox->currentData().toUInt();

            tracker->delete_object_trajectory_section(objectID, editingAnchorItem->get_timestamp());
            set_anchors_tab(objectID);
        }

        projectChanged = true;
    }
    else
    {
        qDebug() << "ERROR: Action from trajectory menu was not recognized";
    }
}

void MainWindow::set_change_beginning()
{
    qDebug() << "Change Beginning position";

    set_anchors_tab(ui->objectsBox->currentData().toUInt());

    QListWidgetItem *beginItem = ui->anchorsWidget->item(0); // The Beginning item
    editingAnchorItem = static_cast<AnchorItem *>(ui->anchorsWidget->itemWidget(beginItem));

    editingAnchorItem->set_highlight(true);

    // Goes to the event frame so that user does not need to look for it
    show_frame_by_timestamp(editingAnchorItem->get_timestamp());

    set_selection(SelectionState::CHANGE_POSITION);
}

void MainWindow::set_end_frame()
{
    qDebug() << "Set end frame";

    set_anchors_tab(ui->objectsBox->currentData().toUInt());
    //int width = ui->anchorsWidget->size().width()-1;
    //qDebug() << width;

    QListWidgetItem *endItem = ui->anchorsWidget->item(ui->anchorsWidget->count()-1); // The End item

    editingAnchorItem = static_cast<AnchorItem *>(ui->anchorsWidget->itemWidget(endItem));

    editingAnchorItem->set_highlight(true);

    if (editingAnchorItem->is_set())
        show_frame_by_timestamp(editingAnchorItem->get_timestamp());

    set_selection(SelectionState::SET_END_FRAME, false);
}

void MainWindow::set_video_end()
{
    qDebug() << "Set video end";

    set_anchors_tab(ui->objectsBox->currentData().toUInt());

    unsigned int objectID = ui->objectsBox->currentData().toUInt();

    tracker->change_object_end_frame(objectID, false);

    projectChanged = true;
}

void MainWindow::set_selection(SelectionState state, bool enable_selecting)
{
    QString text;
    switch (state)
    {
    case SelectionState::NEW_OBJECT:
        text = tr("Select an area for tracking");
        break;

    case SelectionState::CHANGE_POSITION:
        text = tr("Select a new position");
        break;

    case SelectionState::SET_END_FRAME:
        text = tr("Select a frame where you want the object to end");
        break;

    case SelectionState::NEW_SECTION:
        text = tr("Select a new position for this object");
        break;

    default:
        qDebug() << "ERROR: SelectionState not recognized";
        return;
    }

    selectionState = state;
    ui->selectionLabel->setText(text);
    ui->objectsControlWidget->setEnabled(false);
    ui->newObjectButton->setEnabled(false);

    //ui->openVideoButton->setEnabled(false);
    ui->actionOpenVideo->setEnabled(false);
    ui->actionLoadProject->setEnabled(false);
    ui->actionSaveProject->setEnabled(false);

    //ui->createOutputButton->setEnabled(false);
    ui->actionCreateOutput->setEnabled(false);

    ui->selectionWidget->setVisible(true);

    if (enable_selecting)
        ui->videoLabel->set_selection_enabled(true); // Enables selection of area to be tracked

    set_application_menu();
}

// When an object is selected, display its settings
void MainWindow::set_object_settings()
{
    if (!tracker || tracker->get_objects_count() == 0) // No tracking object is added
        return;
    else if (!ask_save_appearance_changes())
        return;

    unsigned int id = ui->objectsBox->currentData().toUInt();
    set_appearance_tab(id);
    set_anchors_tab(id);
}

void MainWindow::set_appearance_tab(unsigned int id)
{
    originalObjectAppearance = tracker->get_object_appearance(id);
    alteredObjectAppearance = originalObjectAppearance;

    settingObjectSettings = true; // Disables slots for appearance change (change_color(), ...)

    ui->shapeBox->setCurrentIndex(ui->shapeBox->findData(originalObjectAppearance.shape));
    //qDebug() << "defocus:" << originalObjectAppearance.defocus;
    ui->defocusButton->setChecked(originalObjectAppearance.defocus);
    ui->colorButton->setChecked(!originalObjectAppearance.defocus);
    ui->defocusSizeBox->setValue(originalObjectAppearance.defocusSize);
    ui->drawInsideBox->setChecked(originalObjectAppearance.drawInside);
    ui->drawBorderBox->setChecked(originalObjectAppearance.drawBorder);
    ui->colorBox->setCurrentIndex(ui->colorBox->findData(originalObjectAppearance.colorID));
    ui->borderColorBox->setCurrentIndex(ui->borderColorBox->findData(originalObjectAppearance.borderColorID));
    ui->borderThicknessBox->setValue(originalObjectAppearance.borderThickness);

    ui->defocusWidget->setEnabled(originalObjectAppearance.defocus);
    ui->colorWidget->setEnabled(!originalObjectAppearance.defocus);

    settingObjectSettings = false;
}

/* if clear==true, trajectory is set from scratch */
void MainWindow::set_trajectory_tab(unsigned int id, bool clear, bool showProgressBar)
{
    if (clear)
        ui->trajectoryWidget->clear(); // Clears to add all items again to empty list

    QListWidgetItem *listItem = nullptr;
    TrajectoryItem *item = nullptr;

    auto trajectory = tracker->get_object_trajectory(id);

    auto iterator = trajectory.begin();
    if (ui->trajectoryWidget->count() > 0)
    { // the list is not empty, find the last one and start adding from the following one

        // Find last item and its timestamp:
        QListWidgetItem *lastItem = ui->trajectoryWidget->item(ui->trajectoryWidget->count()-1); // The Last item
        TrajectoryItem *lastTrajectoryItem = static_cast<TrajectoryItem *>(ui->trajectoryWidget->itemWidget(lastItem));
        int64_t lastTimestamp = lastTrajectoryItem->get_timestamp();

        iterator = trajectory.find(lastTimestamp);

        if (iterator == trajectory.end())
        { // This should never occur; It is here just for a safety reason.
            iterator = trajectory.begin();
            ui->trajectoryWidget->clear();
        }
        else
        {
            iterator++; // Start adding from the next one
        }
    }

    QProgressDialog progressDialog(tr("Setting Trajectory tab"), tr("Cancel"), 0, 0, this);
    if (showProgressBar && iterator != trajectory.end())
    { // This operation might take a while -> show a progress dialog
        progressDialog.setWindowTitle(tr("Trajectory tab"));
        progressDialog.setModal(true);
        progressDialog.show();
    }
    while (iterator != trajectory.end())
    {
        TrajectoryEntry entry = iterator->second;
        listItem = new QListWidgetItem();
        item = new TrajectoryItem(iterator->first, entry.timePosition, tracker->get_total_time(),
                                  entry.frameNumber, tracker->get_frame_count(), entry.position,
                                  this, displayTime);
        ui->trajectoryWidget->addItem(listItem);
        ui->trajectoryWidget->setItemWidget(listItem, item); // Takes ownership of listItem => frees automatically
        listItem->setSizeHint(QSize(listItem->sizeHint().width(), 50));

        if (progressDialog.wasCanceled()) // User cancelled the progress dialog
            return;

        qApp->processEvents(); // Keeps progress bar active

        iterator++;
    }
    progressDialog.cancel();

}

void MainWindow::trajectory_show_frame(QListWidgetItem *item)
{

    TrajectoryItem *selectedItem = static_cast<TrajectoryItem *>(ui->trajectoryWidget->itemWidget(item));
    show_frame_by_timestamp(selectedItem->get_timestamp());
}

void MainWindow::set_anchors_tab(unsigned int id)
{
    assert(tracker != nullptr);


    ui->anchorsWidget->clear(); // Clears to add all items again to empty list

    QListWidgetItem *listItem = nullptr;
    AnchorItem *item = nullptr;

    ItemType type = ItemType::BEGINNING;
    auto trajectorySections = tracker->get_object_trajectory_sections(id);
    for (auto &section: trajectorySections)
    {
        listItem = new QListWidgetItem();
        item = new AnchorItem(type, section.first, section.second.initialTimePosition, tracker->get_total_time(),
                                  section.second.initialFrameNumber, tracker->get_frame_count(), this, true, displayTime);
        ui->anchorsWidget->addItem(listItem);
        ui->anchorsWidget->setItemWidget(listItem, item); // Takes ownership of listItem => frees automatically
        type = ItemType::CHANGE;
    }

    int64_t endTimestamp;
    unsigned long endTimePosition;
    unsigned long endFrameNumber;

    isEndTimestampSet = tracker->get_object_end(id, endTimestamp, endTimePosition, endFrameNumber);
    listItem = new QListWidgetItem();
    item = new AnchorItem(ItemType::END, endTimestamp, endTimePosition, tracker->get_total_time(),
                              endFrameNumber, tracker->get_frame_count(), this, isEndTimestampSet, displayTime);
    ui->anchorsWidget->addItem(listItem);
    ui->anchorsWidget->setItemWidget(listItem, item);

    set_trajectory_tab(id, true, true);

    set_application_menu(); // Because of isEndTimestampSet that might be changed
}

void MainWindow::add_trajectory_change()
{
    set_anchors_tab(ui->objectsBox->currentData().toUInt());
    set_selection(SelectionState::NEW_SECTION);
}

void MainWindow::change_shape()
{
    if (settingObjectSettings)
        return;

    //qDebug() << "Shape changed";
    alteredObjectAppearance.shape = ui->shapeBox->currentData().toUInt();

    change_appearance();
}

void MainWindow::change_defocus()
{
    if (settingObjectSettings)
        return;

    alteredObjectAppearance.defocus = ui->defocusButton->isChecked();

    ui->defocusWidget->setEnabled(alteredObjectAppearance.defocus);
    ui->colorWidget->setEnabled(!alteredObjectAppearance.defocus);

    change_appearance();
}

void MainWindow::change_defocus_size()
{
    if (settingObjectSettings)
        return;

    alteredObjectAppearance.defocusSize = ui->defocusSizeBox->value();

    change_appearance();
}

void MainWindow::change_color()
{
    if (settingObjectSettings)
        return;

    //qDebug() << "Change color";
    unsigned int newColorID = ui->colorBox->currentData().toUInt();
    if (newColorID == 0)
        custom_color();
    else
    {
        alteredObjectAppearance.colorID = newColorID;

        alteredObjectAppearance.color = colorsMap[newColorID].second;

        change_appearance();
    }
}

void MainWindow::change_border_color()
{
    if (settingObjectSettings)
        return;

    //qDebug() << "Change border color";
    unsigned int newColorID = ui->borderColorBox->currentData().toUInt();
    if (newColorID == 0)
        custom_border_color();
    else
    {
        alteredObjectAppearance.borderColorID = newColorID;

        alteredObjectAppearance.borderColor = colorsMap[newColorID].second;

        change_appearance();
    }
}

void MainWindow::change_border_thickness()
{
    if (settingObjectSettings)
        return;

    //qDebug() << "Border thickness changed";
    alteredObjectAppearance.borderThickness = ui->borderThicknessBox->value();

    change_appearance();
}

void MainWindow::change_draw_inside()
{
     if (settingObjectSettings)
        return;

    //qDebug() << "Draw inside changed";
    alteredObjectAppearance.drawInside = ui->drawInsideBox->isChecked();

    change_appearance();
}

void MainWindow::change_draw_border()
{
     if (settingObjectSettings)
        return;

    //qDebug() << "Draw border changed";
    alteredObjectAppearance.drawBorder = ui->drawBorderBox->isChecked();

    change_appearance();
}

void MainWindow::change_appearance()
{

    //qDebug() << "Change appearance";
    if (!appearanceChanged)
    {
        ui->appearanceButtonsWidget->setVisible(true);
        appearanceChanged = true;
    }

    tracker->change_object_appearance(ui->objectsBox->currentData().toUInt(), alteredObjectAppearance);

    if (!tracker->get_current_frame(frame, originalFrame, showOriginalVideo))
        qDebug() << "ERROR: change_appearance(): Cannot show current frame";
    else
        show_frame();
}

bool MainWindow::ask_save_appearance_changes()
{
    if (!appearanceChanged) // No changes to appearance made, everything is all right
        return true;

    QMessageBox *msgBox = new QMessageBox(this);
    msgBox->setWindowTitle(tr("Appearance changes"));
    msgBox->setText(tr("Changes to appearance of object") + " \"" + ui->objectsBox->currentText() +
                    "\" "+ tr("were made. Do you wish to save your changes?"));
    msgBox->setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
    msgBox->setButtonText(QMessageBox::Yes, tr("Yes"));
    msgBox->setButtonText(QMessageBox::No, tr("No"));
    msgBox->setButtonText(QMessageBox::Cancel, tr("Cancel"));
    msgBox->setDefaultButton(QMessageBox::Yes);

    int state = msgBox->exec();

    if (state == QMessageBox::Yes)
    {
        confirm_appearance_changes();
        return true;
    }
    else if (state == QMessageBox::No)
    {
        discard_appearance_changes();
        return true;
    }
    else // Cancel button pressed
        return false;
}

void MainWindow::confirm_appearance_changes()
{
    assert(appearanceChanged);
    appearanceChanged = false;
    projectChanged = true;

    originalObjectAppearance = alteredObjectAppearance;
    ui->appearanceButtonsWidget->setVisible(false);

}

void MainWindow::discard_appearance_changes()
{
    assert(appearanceChanged);

    alteredObjectAppearance = originalObjectAppearance;

    change_appearance(); // Is needed to revert changes and show current image

    appearanceChanged = false;

    unsigned int id = ui->objectsBox->currentData().toUInt();
    set_appearance_tab(id); // Sets Appearance tab to original values (state before changes were made)

    ui->appearanceButtonsWidget->setVisible(false);
}

/* Sets items in Appearance tab to behave correctly */
void MainWindow::show_appearance_tab()
{
    settingObjectSettings = true; // Disables signal handlings

    ui->colorBox->clear();
    ui->colorBox->insertItem(0, tr("+ Add new"), 0);
    ui->colorBox->insertSeparator(1);

    ui->borderColorBox->clear();
    ui->borderColorBox->insertItem(0, tr("+ Add new"), 0);
    ui->borderColorBox->insertSeparator(1);

    QPixmap colorPixmap(ui->colorBox->iconSize());
    for (auto const &color: colorsMap)
    {
        ObjectColor const &objColor = color.second.second;
        colorPixmap.fill(QColor(objColor.r, objColor.g, objColor.b));

        QIcon colorIcon(colorPixmap);
        ui->colorBox->addItem(colorIcon, QString::fromStdString(color.second.first), color.first);
        ui->borderColorBox->addItem(colorIcon, QString::fromStdString(color.second.first), color.first);
    }

    ui->shapeBox->clear();
    for (auto const &shape: shapesMap)
    {
       ui->shapeBox->addItem(shape.second, shape.first);
    }

    ui->defocusSizeBox->setRange(2, 10000); // Value 1 would not have any effect; Maximum is much higher than could be needed.
    ui->borderThicknessBox->setRange(0, 255); // OpenCV accepts border thickness in range 0-255

    settingObjectSettings = false;
}

void MainWindow::custom_color()
{
    unsigned int newColorID;
    if (color_picker(newColorID, colorsMap[alteredObjectAppearance.colorID].second))
        ui->colorBox->setCurrentIndex(ui->colorBox->findData(newColorID));
    else // No color selected, set previous color
        ui->colorBox->setCurrentIndex(ui->colorBox->findData(alteredObjectAppearance.colorID));
}

void MainWindow::custom_border_color()
{
    unsigned int newColorID;
    if (color_picker(newColorID, colorsMap[alteredObjectAppearance.borderColorID].second))
        ui->borderColorBox->setCurrentIndex(ui->borderColorBox->findData(newColorID));
    else // No color selected, set previous color
        ui->borderColorBox->setCurrentIndex(ui->borderColorBox->findData(alteredObjectAppearance.borderColorID));
}

bool MainWindow::color_picker(unsigned int &newColorID, ObjectColor const &initialColor)
{
    //static unsigned int num = 1;
    QColorDialog dialog(this);
    QColor selectedColor = dialog.getColor(QColor(initialColor.r, initialColor.g, initialColor.b));
    if (!selectedColor.isValid()) // user hit "Cancel" button
        return false;

    QPixmap colorPixmap(ui->colorBox->iconSize());
    colorPixmap.fill(selectedColor);
    QIcon colorIcon(colorPixmap);

    QString colorName = tr("Custom #") + QString::number(++customColorsCount);

    newColorID = colorsMap.rbegin()->first + 1; // Gets the highest value and increases; careful if changing order e.g. alphabetically
    colorsMap[newColorID] = std::make_pair(colorName.toStdString(), ObjectColor(selectedColor.red(), selectedColor.green(), selectedColor.blue()));

    ui->colorBox->addItem(colorIcon, colorName, newColorID);
    ui->borderColorBox->addItem(colorIcon, colorName, newColorID);

    return true;
}

bool MainWindow::enter_object_name(bool change)
{
    assert(tracker);

    //static unsigned int objectNumber = 1; // Used for default (recommended) text
    unsigned int objectNumber = tracker->get_objects_count() + 1; // Used for default (recommended) text
    //bool ok; // or check "name" for null
    int ok;
    bool firstTime = true;

    QString defaultText;
    QString heading;
    if (change)
    {
        heading = tr("Change object name");

        defaultText = ui->objectsBox->currentText();
    }
    else
    {
        heading = tr("New object");

        defaultText = tr("Object") + " " + QString::number(objectNumber);
        while (ui->objectsBox->findText(defaultText) >= 0) // name already exists, increase default number
            defaultText = tr("Object") + " " + QString::number(++objectNumber);
    }
    while (true)
    { // If user enters a name that already exists, allow him to enter another one
        if (!firstTime)
            defaultText = newObjectName;
        else
            firstTime = false;

       // newObjectName = QInputDialog::getText(this, heading, tr("Object name:"), QLineEdit::Normal, defaultText, &ok);
        QInputDialog dialog(this);
        dialog.setInputMode(QInputDialog::TextInput);
        dialog.setLabelText(tr("Object name:"));
        dialog.setWindowTitle(heading);
        dialog.setTextValue(defaultText);
        dialog.setOkButtonText(tr("Ok"));
        dialog.setCancelButtonText(tr("Cancel"));

        ok = dialog.exec();

        if (ok)
        { // OK button pressed
            newObjectName = dialog.textValue();

            if (newObjectName.isEmpty())
            { // Input string is empty
                QMessageBox *alert = new QMessageBox(this);
                alert->setWindowTitle(tr("Object name"));
                alert->setText(tr("Object name cannot be empty"));
                alert->exec();
                continue;
            }
            else if (ui->objectsBox->findText(newObjectName) >= 0)
            { // Object with this name already exists, show warning and continue with adding

                if (change && newObjectName == ui->objectsBox->currentText())
                { // When changing object name, user left the old name and clicked OK
                    return false;
                }
                else
                {
                    QMessageBox *alert = new QMessageBox(this);
                    alert->setWindowTitle(tr("Object exists"));
                    alert->setText(tr("Object with this name already exists. Enter a new one."));
                    alert->exec();
                    continue;
                }
            }

            return true;

        }
        else // Cancel button pressed
            return false;
    }

}

void MainWindow::add_new_object()
{
    pause();

    if (!ask_save_appearance_changes())
        return;

    if (enter_object_name(false))
    { // User selected name correctly
        set_selection(SelectionState::NEW_OBJECT);
    }
}

void MainWindow::compute_trajectory()
{
    assert(tracker);
    pause();

    // Save timestamp to restore it after the operation is complete
    int64_t currentTimestamp = tracker->get_frame_timestamp();
    unsigned currentObject = ui->objectsBox->currentData().toUInt();

    QProgressDialog progressDialog(computingInfoText, tr("Cancel"), 0, 0, this);
    progressDialog.setWindowTitle(computingInfoTitle);
    progressDialog.setModal(true);
    progressDialog.show();
    QMessageBox *alert = new QMessageBox(this);
    alert->setWindowTitle(tr("Computing trajectory"));
    try
    {
        if (!tracker->track_object(currentObject, &progressDialog))
        {
            qDebug() << "WARNING: Cannot compute trajectory";
            alert->setText(tr("Trajectory was not computed"));
        }
        else
        { //success
            alert->setText(tr("Trajectory was successfully computed"));
        }
    } catch (UserCanceledException)
    {
        if (displayTime)
        {
            TimeConversion::Time timePosition;
            timePosition.raw = tracker->get_time_position();
            TimeConversion::convert_time(timePosition);
            bool showHours = timePosition.hr ? true : false;
            QString timeString;
            TimeConversion::Time2QString(timePosition, timeString, showHours);

            alert->setText(tr("Trajectory computing was cancelled. Last computed frame time position is ")
                           + timeString);
        }
        else
        {
            alert->setText(tr("Trajectory computing was cancelled. Last computed frame's number is ")
                           + QString::number(tracker->get_frame_number()));
        }
    }

    progressDialog.cancel();

    alert->exec();

    set_trajectory_tab(currentObject, false, true);

    show_frame_by_timestamp(currentTimestamp); // restore player position before the computing started
}

void MainWindow::change_name()
{
    if (enter_object_name(true))
    { // User selected name correctly
        tracker->set_object_name(ui->objectsBox->currentData().toUInt(), newObjectName.toStdString());

        show_objects_box(true);
        ui->objectsBox->setCurrentText(newObjectName);
        projectChanged = true;
    }
}

void MainWindow::selection_confirmed()
{
    if (selectionState == SelectionState::NEW_OBJECT)
    {
        Selection selectedPosition = ui->videoLabel->get_selection();
        new_object_confirm(selectedPosition);
    }
    else if (selectionState == SelectionState::CHANGE_POSITION)
    {
        Selection selectedPosition = ui->videoLabel->get_selection();
        if (!change_position_confirm(selectedPosition))
        { // Position cannot take place after the End frame

            QMessageBox *alert = new QMessageBox(this);
            alert->setWindowTitle(tr("Changing position"));
            alert->setText(tr("Beginning frame cannot be after End frame"));
            alert->exec();

            return;
        }
    }
    else if (selectionState == SelectionState::SET_END_FRAME)
    {
        if (!set_end_frame_confirm())
        { // End frame cannot be before Beginning frame

            QMessageBox *alert = new QMessageBox(this);
            alert->setWindowTitle(tr("Setting end frame"));
            alert->setText(tr("End frame cannot be before Beginning frame"));
            alert->exec();

            return;

        }
    }
    else if (selectionState == SelectionState::NEW_SECTION)
    {
        Selection selectedPosition = ui->videoLabel->get_selection();
        if (!new_section_confirm(selectedPosition))
        { // Position cannot take place after the End frame

            QMessageBox *alert = new QMessageBox(this);
            alert->setWindowTitle(tr("New trajectory change"));
            alert->setText(tr("Section cannot begin after End frame"));
            alert->exec();

            return;
        }
    }

    if (!tracker->get_current_frame(frame, originalFrame, showOriginalVideo))
        qDebug() << "ERROR-selection_confirmed(): Cannot show current frame";
    else
        show_frame();

    projectChanged = true;
    selection_end();
}

/* Confirm new trajectory change */
bool MainWindow::new_section_confirm(Selection const &selectedPosition)
{
    if (!tracker->set_object_trajectory_section(ui->objectsBox->currentData().toUInt(), tracker->get_frame_timestamp(),
                                                selectedPosition, tracker->get_time_position(), tracker->get_frame_number()))
        return false;

    set_anchors_tab(ui->objectsBox->currentData().toUInt());
    return true;
}

bool MainWindow::change_position_confirm(Selection const &selectedPosition)
{
    if (!tracker->change_object_trajectory_section(ui->objectsBox->currentData().toUInt(), editingAnchorItem->get_timestamp(),
                                                   tracker->get_frame_timestamp(), selectedPosition, tracker->get_time_position(),
                                                   tracker->get_frame_number()))
        return false;

    set_anchors_tab(ui->objectsBox->currentData().toUInt());
    return true;
}

bool MainWindow::set_end_frame_confirm()
{
    if (!tracker->change_object_end_frame(ui->objectsBox->currentData().toUInt(), true, tracker->get_frame_timestamp(),
                                          tracker->get_time_position(), tracker->get_frame_number()))
       return false;

    set_anchors_tab(ui->objectsBox->currentData().toUInt());
    return true;
}

void MainWindow::new_object_confirm(Selection const &selectedPosition)
{
    assert(selectionState == SelectionState::NEW_OBJECT);

    unsigned defaultColor = Colors::BLACK;
    unsigned defaultBorderColor = Colors::RED;

    ObjectColor const &color = colorsMap[defaultColor].second;
    ObjectColor const &borderColor = colorsMap[defaultBorderColor].second;

    Characteristics defaultAppearance(ObjectShape::RECTANGLE, true, 20, true, color,
                                      Colors::BLACK, true, borderColor, Colors::RED, 3);


    int id;
    if ((id = tracker->add_object(defaultAppearance, newObjectName.toStdString(), selectedPosition, tracker->get_frame_timestamp(),
                                  tracker->get_time_position(), tracker->get_frame_number(), false, 0, 0, 0)) < 0)
    {
        qDebug() << "ERROR-new_object_confirm(): Object wasn't added correctly";
        return;
    }

    //ui->videoLabel->set_image(frame, ui->videoFrame->width(), ui->videoFrame->height());

    show_objects_box(true);
    if (ui->objectsBox->currentText() == newObjectName)
    { // the object is already active. thus, tabs need to be updated manually
        set_object_settings();
    }
    else
    { // Changing current text updates all tabs
        ui->objectsBox->setCurrentText(newObjectName); // this changes values in all 3 tabs //test
    }
}

void MainWindow::show_objects_box(bool noTabsUpdate)
{
    // First disconnect this signal so alterig ui->objectsBox does not fire it every time
    // Connect it again at the end of this function
    if (noTabsUpdate)
        QObject::disconnect(ui->objectsBox, SIGNAL(currentIndexChanged(int)), this, SLOT(set_object_settings()));

    ui->objectsBox->clear();

    std::vector<std::string> objects;

    if (!tracker || (objects=tracker->get_all_objects_names()).empty())
    { // No objects exist, disable Objects menu
        ui->objectsBox->addItem(tr("No objects added"));
        ui->objectsBox->setEnabled(false);
        ui->tabWidget->setEnabled(false);
    }
    else
    {
        unsigned int i = 0;
        for (std::string const &object: objects)
        {
            ui->objectsBox->setEnabled(true);
            ui->tabWidget->setEnabled(true);

            ui->objectsBox->addItem(QString::fromStdString(object), i);
            i++;
        }
    }

    set_application_menu();

    // Connect the signal that was disconnected at the beginning of this function
    if (noTabsUpdate)
        QObject::connect(ui->objectsBox, SIGNAL(currentIndexChanged(int)), this, SLOT(set_object_settings()));
}

void MainWindow::selection_end()
{
    assert(selectionState != SelectionState::NO_SELECTION);

    if (selectionState == SelectionState::CHANGE_POSITION || selectionState == SelectionState::SET_END_FRAME)
    {
        editingAnchorItem->set_highlight(false);
        editingAnchorItem = nullptr;
    }

    ui->objectsControlWidget->setEnabled(true);
    ui->newObjectButton->setEnabled(true);
    //ui->openVideoButton->setEnabled(true);
    ui->actionOpenVideo->setEnabled(true);
    ui->actionLoadProject->setEnabled(true);
    ui->actionSaveProject->setEnabled(true);

    //ui->createOutputButton->setEnabled(true);
    ui->actionCreateOutput->setEnabled(true);

    ui->selectionWidget->setVisible(false);
    ui->videoLabel->set_selection_enabled(false); // Disables selection of area to be tracked

    selectionState = SelectionState::NO_SELECTION;

    set_application_menu();
}

void MainWindow::delete_object()
{
    // Ask if they really want to delete this object
    QMessageBox *msgBox = new QMessageBox(this);
    msgBox->setWindowTitle(tr("Delete object"));
    msgBox->setText(tr("Do you really want to delete object") + " \"" + ui->objectsBox->currentText() + "\"?");
    msgBox->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
    msgBox->setButtonText(QMessageBox::Yes, tr("Yes"));
    msgBox->setButtonText(QMessageBox::No, tr("No"));
    msgBox->setDefaultButton(QMessageBox::Yes);

    if (msgBox->exec() == QMessageBox::Yes)
    { // Deletion confirmed by user

        tracker->delete_object(ui->objectsBox->currentData().toUInt());
        appearanceChanged = false; // Object is deleted -> no object's appearance changes

        show_objects_box(); // Changes were made, display current objectsBox

        // Display current frame (without deleted object)
        if (!tracker->get_current_frame(frame, originalFrame, showOriginalVideo))
            qDebug() << "ERROR-delete_object: Cannot show current frame";
        else
            show_frame();

        projectChanged = true;
    }

}

void MainWindow::display_time()
{
    displayTime = true;
    settings->setValue("showTime", true);

    ui->actionTime->setChecked(true);
    ui->actionFrameNumbers->setChecked(false);


    if (tracker)
    {   // Video is loaded, display its time
        for (int i = 0; i < ui->anchorsWidget->count(); i++)
        {
            QListWidgetItem* item = ui->anchorsWidget->item(i);
            AnchorItem * anchorItem = static_cast<AnchorItem *>(ui->anchorsWidget->itemWidget(item));
            anchorItem->display_time();
        }

        for (int i = 0; i < ui->trajectoryWidget->count(); i++)
        {
            QListWidgetItem* item = ui->trajectoryWidget->item(i);
            TrajectoryItem* trajectoryItem = static_cast<TrajectoryItem *>(ui->trajectoryWidget->itemWidget(item));
            trajectoryItem->display_time();
        }

        ui->timeLabel->display_time(tracker->get_time_position());
    }
    else // Shows default text when no video is loaded
        ui->timeLabel->setText("--:--");

   }

void MainWindow::display_frame_numbers()
{
    displayTime = false;
    settings->setValue("showTime", false);

    ui->actionFrameNumbers->setChecked(true);
    ui->actionTime->setChecked(false);

    if (tracker)
    {   // Video is loaded, display its frame numbers
        for (int i = 0; i < ui->anchorsWidget->count(); i++)
        {
            QListWidgetItem* item = ui->anchorsWidget->item(i);

            AnchorItem * anchorItem = static_cast<AnchorItem *>(ui->anchorsWidget->itemWidget(item));
            anchorItem->display_frame_num();
        }

        for (int i = 0; i < ui->trajectoryWidget->count(); i++)
        {
            QListWidgetItem* item = ui->trajectoryWidget->item(i);

            TrajectoryItem* trajectoryItem = static_cast<TrajectoryItem *>(ui->trajectoryWidget->itemWidget(item));
            trajectoryItem->display_frame_num();
        }

        ui->timeLabel->display_frame_num(tracker->get_frame_number());
    }
    else // Shows default text when no video is loaded
        ui->timeLabel->setText("--/--");

}

void MainWindow::show_original_video()
{
    showOriginalVideo = !showOriginalVideo;

    settings->setValue("showOriginalVideo", showOriginalVideo);

    ui->actionShowOriginalVideo->setChecked(showOriginalVideo);
    ui->originalVideoFrame->setVisible(showOriginalVideo);

    if (showOriginalVideo && tracker)
    {   // Video is loaded, current frame needs to be loaded since originalFrame does not exist yet

        if (!tracker->get_current_frame(frame, originalFrame, showOriginalVideo))
            qDebug() << "ERROR: show_original_video(): Cannot show current frame";
        else
            show_frame();
    }

}

void MainWindow::set_czech_language()
{
    ui->actionPlay->setEnabled(false);
    qDebug() << "Czech language set";

    // Only one language item must be checked
    ui->actionCzech->setChecked(true);
    ui->actionEnglish->setChecked(false);
    settings->setValue("language", ":/language/video_anonymizer_cs");

    QMessageBox *alert = new QMessageBox(this);
    alert->setWindowTitle("Čeština"); // No need for translation (tr()) - display message in selected language
    alert->setText("Změna se projeví až při novém spuštění aplikace");
    alert->exec();

}

void MainWindow::set_english_language()
{
    qDebug() << "English set";

    // Only one language item must be checked
    ui->actionEnglish->setChecked(true);
    ui->actionCzech->setChecked(false);
    settings->setValue("language", "");

    QMessageBox *alert = new QMessageBox(this);
    alert->setWindowTitle("English"); // No need for translation (tr()) - display message in selected language
    alert->setText("The change will take effect after restarting the application");
    alert->exec();

}

bool MainWindow::ask_save_project()
{
    if (!ask_save_appearance_changes()) // Before saving the project, changed appearance of tracked objects must be saved or discarded
            return false;

    if (!tracker || !projectChanged) // Project is empty, cannot be saved
        return true;

    QMessageBox *msgBox = new QMessageBox(this);
    msgBox->setWindowTitle(tr("Save project"));
    msgBox->setText(tr("Do you wish to save the current project?"));
    msgBox->setStandardButtons(QMessageBox::Yes | QMessageBox::No);

    msgBox->setStandardButtons(msgBox->standardButtons() | QMessageBox::Cancel);
    msgBox->setButtonText(QMessageBox::Cancel, tr("Cancel"));

    msgBox->setButtonText(QMessageBox::Yes, tr("Yes"));
    msgBox->setButtonText(QMessageBox::No, tr("No"));
    msgBox->setDefaultButton(QMessageBox::Yes);

    int state = msgBox->exec();

    if (state == QMessageBox::Yes)
        return save_project();
    else if (state == QMessageBox::No)
        return true;
    else // Cancel button pressed
        return false;
}

bool MainWindow::save_project()
{
    assert(tracker);

    if (!ask_save_appearance_changes())
        return false;

    QString jsonFilter = "JSON (*.json)";
    QString xmlFilter = "XML (*.xml)";
    QString filters = jsonFilter + ";;" + xmlFilter;
    QString selectedFilter;

    // projectFormat is saved in settings so the last choice is remembered even after restarting the application
    if (settings->contains("projectFormat"))
    { // Is projectFormat variable set in settings?
        selectedFilter = settings->value("projectFormat").toString();
    }
    else // default
        selectedFilter = jsonFilter;

    QString defaultPath = projectFileName;  // If project was loaded, sets its destination, otherwise is empty

    if (defaultPath.isEmpty() && settings->contains("lastPath"))
    { // begin with the directory where the last video was opened
        defaultPath = settings->value("lastPath").toString();
        qDebug() << "last path:" << defaultPath;
    }

    QString filename = QFileDialog::getSaveFileName(this, tr("Select project file name"),
                                                    defaultPath,
                                                    filters, &selectedFilter);
    if (filename.isEmpty())
        return false; // No filename selected

    settings->setValue("projectFormat", selectedFilter);

    { // Serialize with CEREAL

        if (selectedFilter == xmlFilter)
        { // XML

            if (!filename.endsWith(".xml"))
                filename += ".xml"; // If it doesn't have the right extension, append it
            std::ofstream os(filename.toStdString());

            cereal::XMLOutputArchive oarchive(os);

            oarchive(CEREAL_NVP(inputFileName), CEREAL_NVP(tracker), CEREAL_NVP(customColorsCount), cereal::make_nvp("colors", colorsMap));
            //oarchive(inputFileName, tracker, colorsMap);
        }
        else
        { // JSON
            if (!filename.endsWith(".json"))
                filename += ".json"; // If it doesn't have the right extension, append it

            std::ofstream os(filename.toStdString());

            cereal::JSONOutputArchive oarchive(os);

            //oarchive(inputFileName, tracker, colorsMap);
            oarchive(CEREAL_NVP(inputFileName), CEREAL_NVP(tracker), CEREAL_NVP(customColorsCount), cereal::make_nvp("colors", colorsMap));
        }

    }

    projectFileName = filename;

    int projectNameBeginning = projectFileName.lastIndexOf("/");
    if (projectNameBeginning < 0)
        projectNameBeginning = 0;
    else
        projectNameBeginning++;
    projectName = projectFileName.mid(projectNameBeginning);
    set_application_menu(); // It shows the window's title with the project's name

    projectChanged = false;

    return true;
}

void MainWindow::load_project()
{
    if (tracker)
    { // Project exists, ask to save/discard
        if (!ask_save_project())
            return; // User canceled the dialog, do not open a new video

        initial_application_settings(); // Restores default application settings

    }

    QString jsonFilter = "JSON (*json)";
    QString xmlFilter = "XML (*.xml)";
    QString filters = jsonFilter + ";;" + xmlFilter;
    QString selectedFilter;

    // projectFormat is saved in settings so the last choice is remembered even after restarting the application
    if (settings->contains("projectFormat"))
    { // Is projectFormat variable set in settings?
        selectedFilter = settings->value("projectFormat").toString();
    }
    else
        selectedFilter = "JSON (*json)";


    QString defaultDir = "";

    if (settings->contains("lastPath"))
    { // begin with the directory where the last video was opened
        defaultDir = settings->value("lastPath").toString();
        qDebug() << "last path:" << defaultDir;
    }

    projectFileName = QFileDialog::getOpenFileName(this, tr("Load project"),
                                                    defaultDir,
                                                    filters, &selectedFilter);
    if (projectFileName.isEmpty())
    {
        qDebug() << "projectFileName is empty";
        return; // No filename selected
    }


    settings->setValue("projectFormat", selectedFilter); // Remember file format choice also after restarting the application

    colorsMap.clear();
    try
    {
        { // Deserialize with CEREAL

        std::ifstream os(projectFileName.toStdString());

        if (selectedFilter == xmlFilter)
        { // XML
            cereal::XMLInputArchive iarchive(os);
            iarchive(CEREAL_NVP(inputFileName), CEREAL_NVP(tracker), CEREAL_NVP(customColorsCount), cereal::make_nvp("colors", colorsMap));
            //iarchive(inputFileName, tracker, colorsMap);
        }
        else
        { // JSON
            cereal::JSONInputArchive iarchive(os);
            iarchive(CEREAL_NVP(inputFileName), CEREAL_NVP(tracker), CEREAL_NVP(customColorsCount), cereal::make_nvp("colors", colorsMap));
            //iarchive(inputFileName, tracker, colorsMap);
        }

        }

    } catch (...) {
        QMessageBox *alert = new QMessageBox(this);
        alert->setWindowTitle(tr("Project opening"));
        alert->setText(tr("Project could not be loaded"));
        alert->exec();
        return;
    }

    while (true)
    { // loading video - until correct video is loaded or user pressed "Cancel"
        QProgressDialog progressDialog(tr("Opening video"), tr("Cancel"), 0, 0, this);
        progressDialog.setWindowTitle(tr("Opening"));
        progressDialog.setModal(true);
        progressDialog.show();

        try
        {

            tracker->load_video(inputFileName, &progressDialog); // inputFileName was loaded with the loaded project
        } catch (OpenException) {

            qDebug() << "Video cannot be opened.";

            QMessageBox *alert = new QMessageBox(this);
            alert->setWindowTitle(tr("Opening video"));
            alert->setText(tr("Video from the project was not found or cannot be opened. Please set its path."));
            alert->exec();

            if (!open_video_dialog()) // Shows dialog to select a file
            { // Cancel button pressed
                tracker = nullptr;
                return;
            }
            else // Video file selected, try opening again
                continue;

        } catch (UserCanceledOpeningException) {

            QMessageBox *alert = new QMessageBox(this);
            alert->setWindowTitle(tr("Opening video"));
            alert->setText(tr("Video opening was canceled."));
            alert->exec();

            return;
        }

        break;
    }

    tracker->erase_object_trajectories_to_comply();

    int projectNameBeginning = projectFileName.lastIndexOf("/");

    if (projectNameBeginning < 0)
        projectNameBeginning = 0;
    else
        projectNameBeginning++;
    projectName = projectFileName.mid(projectNameBeginning);
    // set_application_menu() is needed to show the window's title
    // with the project's name. However, the open_video_successful()
    // calls the function.

    open_video_successful();
    show_objects_box();
}

void MainWindow::show_help()
{
    qDebug() << "showing help";
    QTabWidget* tWidget = new QTabWidget;
    tWidget->setMaximumWidth(200);
    tWidget->addTab((QWidget*)helpEngine->contentWidget(), tr("Contents"));
    tWidget->addTab((QWidget*)helpEngine->indexWidget(), tr("Index"));
    HelpBrowser *textViewer = new HelpBrowser(helpEngine);
    //textViewer->setSource( QUrl("qthelp://walletfox.qt.helpexample/doc/index.html"));
    textViewer->setSource(QUrl("qthelp://fit.cz.videoanonymizer/doc/" + tr("en_doc.html")));

    connect((QWidget*)helpEngine->contentWidget(), SIGNAL(linkActivated(QUrl)), textViewer, SLOT(setSource(QUrl)));

    connect((QWidget*)helpEngine->indexWidget(), SIGNAL(linkActivated(QUrl, QString)), textViewer, SLOT(setSource(QUrl)));

    QSplitter *horizSplitter = new QSplitter(Qt::Horizontal);
    horizSplitter->insertWidget(0, tWidget);
    horizSplitter->insertWidget(1, textViewer);
    horizSplitter->hide();

    QDockWidget *helpWindow = new QDockWidget(tr("Help"), this);
    helpWindow->setWidget(horizSplitter);
  //  helpWindow->hide();
    addDockWidget(Qt::BottomDockWidgetArea, helpWindow);
    qDebug() << "help showed";

}

void MainWindow::show_about()
{
    QMessageBox::about(this, tr("Anonymizer"),
                       tr("Application for anonymization of objects in a video.\n") +
                       tr("(C) 2015 FIT BUT.\n"));

}

void MainWindow::initial_application_settings()
{
    if (tracker)
    {
        //delete tracker;
        tracker = nullptr; //is deleted automatically as tracker is a smart pointer
    }

    if (timer)
        timer->stop();

    isPlaying = false;
    selectionState = SelectionState::NO_SELECTION;
    editingAnchorItem = nullptr;
    appearanceChanged = false;
    projectChanged = false;
    settingObjectSettings = false; // Disables slots for appearance change (change_color(), ...) when setting new object
    isEndTimestampSet = false;

    projectFileName = "";
    projectName = "";

    timerInterval = 0; // Original interval based on fps
    timerSpeed = 0; // power used with constant VIDEOTRACKING_TIMER_CONSTANT to play video faster/slower

    customColorsCount = 0;

    ui->selectionWidget->setVisible(false);
    ui->appearanceButtonsWidget->setVisible(false);
    ui->originalVideoTextLabel->setVisible(false);

    ui->playerControlsWidget->setEnabled(false);
    ui->positionSlider->setEnabled(false);
    ui->objectsControlWidget->setEnabled(false);

    //ui->createOutputButton->setEnabled(false);
    ui->actionCreateOutput->setEnabled(false);


    // Move position slider to the beginning
    ui->positionSlider->setValue(ui->positionSlider->minimum());

    set_application_menu();

    set_form_values();
    //show_appearance_tab(); // It is called from open_video_successful()

    show_objects_box();

    // Display default time/frame numbers label
    if (displayTime)
        display_time();
    else
        display_frame_numbers();

    // Make the videoLabel empty
    ui->videoLabel->set_selection_enabled(false);
    ui->videoLabel->clear();
    ui->originalVideoLabel->set_selection_enabled(false);
    ui->originalVideoLabel->clear();
}
/**
void MainWindow::exit_application()
{
    if (!ask_save_project()) // User canceled the dialog telling him to Save or Discard the current project
        return;
}
*/

void MainWindow::set_application_menu()
{
    // Set window title:
    if (projectName.isEmpty())
    {
        if (tracker)
            setWindowTitle(tr("Unsaved Project") + " | " + applicationName);
        else
            setWindowTitle(applicationName);
    }
    else
        setWindowTitle(projectName + " | " + applicationName);

    ui->actionPlay->setEnabled(ui->playButton->isEnabled());
    ui->actionStop->setEnabled(ui->stopButton->isEnabled());
    ui->actionStepBack->setEnabled(ui->stepBackButton->isEnabled());
    ui->actionStepForward->setEnabled(ui->stepForwardButton->isEnabled());

    ui->actionSlower->setEnabled(ui->slowerButton->isEnabled());
    ui->actionFaster->setEnabled(ui->fasterButton->isEnabled());

    if (tracker)
        ui->actionSaveProject->setEnabled(true);
    else
        ui->actionSaveProject->setEnabled(false);

    ui->actionNewObject->setEnabled(ui->newObjectButton->isEnabled());

    if (ui->generalTab->isEnabled())
    {
        ui->actionChangeName->setEnabled(true);
        ui->actionRemoveObject->setEnabled(true);
    }
    else
    {
        ui->actionChangeName->setEnabled(false);
        ui->actionRemoveObject->setEnabled(false);
    }

    ui->menuTrajectory->setEnabled(ui->anchorsTab->isEnabled());
    ui->actionComputeTrajectory->setEnabled(ui->computeTrajectoryButton->isEnabled());
    ui->actionVideoEnd->setChecked(!isEndTimestampSet);

    ui->actionShowOriginalVideo->setChecked(showOriginalVideo);

}

/**
 * Connects signals (from GUI elements and others) with slots of this object.
 * @brief MainWindow::connect_signals
 */
void MainWindow::connect_signals()
{
    // Signals are automatically disconnected with QObject destructor

    // PLAYER CONTROL SECTION
    QObject::connect(ui->playButton, SIGNAL(clicked()), this, SLOT(play()));
    QObject::connect(ui->actionPlay, SIGNAL(triggered()), this, SLOT(play()));

    QObject::connect(ui->stopButton, SIGNAL(clicked()), this, SLOT(stop()));
    QObject::connect(ui->actionStop, SIGNAL(triggered()), this, SLOT(stop()));

    QObject::connect(ui->stepForwardButton, SIGNAL(clicked()), this, SLOT(step_forward()));
    QObject::connect(ui->actionStepForward, SIGNAL(triggered()), this, SLOT(step_forward()));

    QObject::connect(ui->stepBackButton, SIGNAL(clicked()), this, SLOT(step_back()));
    QObject::connect(ui->actionStepBack, SIGNAL(triggered()), this, SLOT(step_back()));

    //QObject::connect(ui->fasterButton, SIGNAL(clicked()), this, SLOT(faster()));
    //QObject::connect(ui->slowerButton, SIGNAL(clicked()), this, SLOT(slower()));

    QObject::connect(ui->fasterButton, SIGNAL(clicked()), this, SLOT(faster()));
    QObject::connect(ui->actionFaster, SIGNAL(triggered()), this, SLOT(faster()));

    QObject::connect(ui->slowerButton, SIGNAL(clicked()), this, SLOT(slower()));
    QObject::connect(ui->actionSlower, SIGNAL(triggered()), this, SLOT(slower()));

    QObject::connect(ui->actionOriginalSpeed, SIGNAL(triggered()), this, SLOT(original_speed()));

    //QObject::connect(ui->positionSlider, SIGNAL(sliderMoved(int)), this, SLOT(show_frame_by_time(int)));
    QObject::connect(ui->positionSlider, SIGNAL(clicked(ulong)), this, SLOT(show_frame_by_number(ulong)));
    QObject::connect(ui->positionSlider, SIGNAL(sliderPressed()), this, SLOT(slider_pressed()));
    QObject::connect(ui->positionSlider, SIGNAL(sliderReleased()), this, SLOT(slider_released()));
    //QObject::connect(ui->positionSlider, SIGNAL(), this, SLOT(show_frame_by_number(int)));


    // FILE
    //QObject::connect(ui->openVideoButton, SIGNAL(clicked()), this, SLOT(open_video()));
    QObject::connect(ui->actionOpenVideo, SIGNAL(triggered()), this, SLOT(open_video()));

    //QObject::connect(ui->createOutputButton, SIGNAL(clicked()), this, SLOT(create_output()));
    QObject::connect(ui->actionCreateOutput, SIGNAL(triggered()), this, SLOT(create_output()));

    QObject::connect(ui->actionSaveProject, SIGNAL(triggered()), this, SLOT(save_project()));
    QObject::connect(ui->actionLoadProject, SIGNAL(triggered()), this, SLOT(load_project()));
    QObject::connect(ui->actionQuit, SIGNAL(triggered()), this, SLOT(close()));

    // OBJECTS
    QObject::connect(ui->objectsBox, SIGNAL(currentIndexChanged(int)), this, SLOT(set_object_settings()));

    QObject::connect(ui->newObjectButton, SIGNAL(clicked()), this, SLOT(add_new_object()));
    QObject::connect(ui->actionNewObject, SIGNAL(triggered()), this, SLOT(add_new_object()));


    // OBJECT APPEARANCE TAB
    QObject::connect(ui->shapeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(change_shape()));

    //handling defocusButton handles also colorButton since toggling colorButton switches off defocusButton
    QObject::connect(ui->defocusButton, SIGNAL(toggled(bool)), this, SLOT(change_defocus()));
    QObject::connect(ui->defocusSizeBox, SIGNAL(valueChanged(int)), this, SLOT(change_defocus_size()));
    QObject::connect(ui->drawInsideBox, SIGNAL(stateChanged(int)), this, SLOT(change_draw_inside()));
    QObject::connect(ui->drawBorderBox, SIGNAL(stateChanged(int)), this, SLOT(change_draw_border()));
    QObject::connect(ui->colorBox, SIGNAL(currentIndexChanged(int)), this, SLOT(change_color()));
    QObject::connect(ui->borderColorBox, SIGNAL(currentIndexChanged(int)), this, SLOT(change_border_color()));
    QObject::connect(ui->borderThicknessBox, SIGNAL(valueChanged(int)), this, SLOT(change_border_thickness()));

    QObject::connect(ui->saveAppearanceChangesButton, SIGNAL(clicked()), this, SLOT(confirm_appearance_changes()));
    QObject::connect(ui->discardAppearanceChangesButton, SIGNAL(clicked()), this, SLOT(discard_appearance_changes()));


    // OBJECT APPEARANCE - CUSTOM COLORS
    //QObject::connect(ui->colorBox, SIGNAL(activated(int)), this, SLOT(custom_color(int)));
    //QObject::connect(ui->borderColorBox, SIGNAL(activated(int)), this, SLOT(custom_border_color(int)));


    // OBJECT ANCHORS TAB
    QObject::connect(ui->anchorsWidget, SIGNAL(itemPressed(QListWidgetItem*)), this, SLOT(show_anchors_menu(QListWidgetItem*)));

    QObject::connect(ui->addTrajectoryChangeButton, SIGNAL(clicked()), this, SLOT(add_trajectory_change()));
    QObject::connect(ui->actionChangeTrajectory, SIGNAL(triggered()), this, SLOT(add_trajectory_change()));

    QObject::connect(ui->actionVideoEnd, SIGNAL(triggered()), this, SLOT(set_video_end()));
    QObject::connect(ui->actionSetEndFrame, SIGNAL(triggered()), this, SLOT(set_end_frame()));
    QObject::connect(ui->actionChangeBeginning, SIGNAL(triggered()), this, SLOT(set_change_beginning()));


    // OBJECT TRAJECTORY TAB
    QObject::connect(ui->trajectoryWidget, SIGNAL(itemPressed(QListWidgetItem*)), this, SLOT(trajectory_show_frame(QListWidgetItem*)));

    QObject::connect(ui->computeTrajectoryButton, SIGNAL(clicked()), this, SLOT(compute_trajectory()));
    QObject::connect(ui->actionComputeTrajectory, SIGNAL(triggered()), this, SLOT(compute_trajectory()));

    // OBJECT GENERAL TAB
    QObject::connect(ui->changeNameButton, SIGNAL(clicked()), this, SLOT(change_name()));
    QObject::connect(ui->actionChangeName, SIGNAL(triggered()), this, SLOT(change_name()));

    QObject::connect(ui->removeObjectButton, SIGNAL(clicked()), this, SLOT(delete_object()));
    QObject::connect(ui->actionRemoveObject, SIGNAL(triggered()), this, SLOT(delete_object()));


    // SELECTION
    QObject::connect(ui->confirmSelectionButton, SIGNAL(clicked()), this, SLOT(selection_confirmed()));
    QObject::connect(ui->cancelSelectionButton, SIGNAL(clicked()), this, SLOT(selection_end()));


    // LANGUAGE MENU
    QObject::connect(ui->actionEnglish, SIGNAL(triggered()), this, SLOT(set_english_language()));
    QObject::connect(ui->actionCzech, SIGNAL(triggered()), this, SLOT(set_czech_language()));

    // SETTINGS MENU
    QObject::connect(ui->actionTime, SIGNAL(triggered()), this, SLOT(display_time()));
    QObject::connect(ui->actionFrameNumbers, SIGNAL(triggered()), this, SLOT(display_frame_numbers()));

    QObject::connect(ui->actionShowOriginalVideo, SIGNAL(triggered()), this, SLOT(show_original_video()));

    // OTHERS
    QObject::connect(ui->videoFrame, SIGNAL(resized()), this, SLOT(reload_video_label()));
    QObject::connect(timer, SIGNAL(timeout()), this, SLOT(show_next_frame()));

    QObject::connect(ui->actionHelp, SIGNAL(triggered()), this, SLOT(show_help()));
    QObject::connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(show_about()));
    //QObject::connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(exit_application()));
 }
