/***************************************************************************
 *   Copyright (C) 2008-2025 by Ilya Kotov                                 *
 *   forkotov02@ya.ru                                                      *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/

#include <QDialog>
#include <QMenu>
#include <QWidget>
#include <QAction>
#include <QSettings>
#include <QApplication>
#include <QMessageBox>
#include <QFileInfo>
#include <algorithm>
#include <qmmp/soundcore.h>
#include <qmmp/metadatamanager.h>
#include "filedialog.h"
#include "playlistparser.h"
#include "playlistmanager.h"
#include "qmmpuisettings.h"
#include "general.h"
#include "generalfactory.h"
#include "jumptotrackdialog_p.h"
#include "aboutdialog_p.h"
#include "addurldialog_p.h"
#include "mediaplayer.h"
#include "uihelper.h"

UiHelper *UiHelper::m_instance = nullptr;

UiHelper::UiHelper(QObject *parent)
        : QObject(parent)
{
    m_instance = this;
    General::create(parent);
    QSettings settings;
    m_lastDir = settings.value(u"General/last_dir"_s, QDir::homePath()).toString(); //last directory
}

UiHelper::~UiHelper()
{
    QSettings settings;
    settings.setValue(u"General/last_dir"_s, m_lastDir);
}

bool UiHelper::visibilityControl()
{
    const QList<GeneralFactory *> factories = General::enabledFactories();
    return std::any_of(factories.cbegin(), factories.cend(),
                       [](GeneralFactory *factory){ return factory->properties().visibilityControl; });
}

void UiHelper::addAction(QAction *action, MenuType type)
{
    connect(action, &QAction::destroyed, this, qOverload<QObject *>(&UiHelper::removeAction));

    if(!m_menus[type].actions.contains(action))
    {
        m_menus[type].actions.append(action);
        action->setShortcutVisibleInContextMenu(true);
    }
    if(m_menus[type].menu && !m_menus[type].menu->actions().contains(action))
    {
        if(m_menus[type].before)
            m_menus[type].menu->insertAction(m_menus[type].before, action);
        else
            m_menus[type].menu->addAction(action);
        m_menus[type].menu->menuAction()->setVisible(!m_menus[type].autoHide || !m_menus[type].actions.isEmpty());
    }
}

void UiHelper::removeAction(QAction *action)
{
    for(MenuType type : m_menus.keys())
    {
        m_menus[type].actions.removeAll(action);
        if(m_menus[type].menu)
        {
            m_menus[type].menu->removeAction(action);
            m_menus[type].menu->menuAction()->setVisible(!m_menus[type].autoHide || !m_menus[type].actions.isEmpty());
        }
    }
}

QList<QAction *> UiHelper::actions(MenuType type)
{
    return m_menus[type].actions;
}

QMenu *UiHelper::createMenu(MenuType type, const QString &title, bool autoHide, QWidget *parent)
{
    if(m_menus[type].menu)
    {
        m_menus[type].menu->setTitle(title);
    }
    else
    {
        m_menus[type].menu = new QMenu(title, parent);
        m_menus[type].menu->addActions(m_menus[type].actions);
    }
    m_menus[type].autoHide = autoHide;
    m_menus[type].menu->menuAction()->setVisible(!autoHide || !m_menus[type].actions.isEmpty());
    return m_menus[type].menu;
}

void UiHelper::registerMenu(UiHelper::MenuType type, QMenu *menu, bool autoHide, QAction *before)
{
    m_menus[type].menu = menu;
    m_menus[type].before = before;
    m_menus[type].autoHide = autoHide;
    if(before)
        m_menus[type].menu->insertActions(before, m_menus[type].actions);
    else
        m_menus[type].menu->addActions(m_menus[type].actions);
    m_menus[type].menu->menuAction()->setVisible(!autoHide || !m_menus[type].actions.isEmpty());
}

void UiHelper::addFiles(QWidget *parent, PlayListModel *model)
{
    QStringList filters;
    filters << tr("All Supported Bitstreams") +
               QStringLiteral(" (%1)").arg(MetaDataManager::instance()->nameFilters().join(QChar::Space));
    filters << MetaDataManager::instance()->filters();
    m_model = model;
    FileDialog::popup(parent, FileDialog::PlayDirsFiles, &m_lastDir,
                      this, SLOT(addSelectedFiles(QStringList,bool)),
                      tr("Select one or more files to open"), filters.join(u";;"_s));
}

void UiHelper::playFiles(QWidget *parent, PlayListModel *model)
{
    QStringList filters;
    filters << tr("All Supported Bitstreams") +
               QStringLiteral(" (%1)").arg(MetaDataManager::instance()->nameFilters().join(QChar::Space));
    filters << MetaDataManager::instance()->filters();
    m_model = model;
    FileDialog::popup(parent, FileDialog::AddDirsFiles, &m_lastDir,
                      this, SLOT(playSelectedFiles(QStringList)),
                      tr("Select one or more files to play"), filters.join(u";;"_s));

}

void UiHelper::addDirectory(QWidget *parent, PlayListModel *model)
{
    FileDialog::popup(parent, FileDialog::AddDirs, &m_lastDir,
                      model, SLOT(addPaths(QStringList)),
                      tr("Choose a directory"));
}

void UiHelper::addUrl(QWidget *parent, PlayListModel *model)
{
    AddUrlDialog::popup(parent, model);
}

void UiHelper::loadPlayList(QWidget *parent, PlayListModel *model)
{
    if(PlayListParser::nameFilters().isEmpty())
    {
        qCWarning(core, "There is no registered playlist parsers");
        return;
    }

    QString mask = tr("Playlist Files") + QStringLiteral(" (%1)").arg(PlayListParser::nameFilters().join(QChar::Space));
    //TODO use nonmodal dialog and multiplier playlists
    QString f_path = FileDialog::getOpenFileName(parent, tr("Open Playlist"), m_lastDir, mask);
    if (!f_path.isEmpty())
    {
        if(QmmpUiSettings::instance()->clearPreviousPlayList())
        {
            model->clear();
            model->setName(QFileInfo(f_path).baseName());
        }
        model->loadPlaylist(f_path);
        m_lastDir = QFileInfo(f_path).absoluteDir().path();
    }
}

void UiHelper::savePlayList(QWidget *parent, PlayListModel *model)
{
    if(PlayListParser::nameFilters().isEmpty())
    {
        qCWarning(core, "There is no registered playlist parsers");
        return;
    }

    QStringList filters;
    filters << tr("Playlist Files") + QStringLiteral(" (%1)").arg(PlayListParser::nameFilters().join(QChar::Space));
    filters << PlayListParser::filters();
    QString selectedFilter = filters.at(1);
    QString f_name = FileDialog::getSaveFileName(parent, tr("Save Playlist"), m_lastDir + QLatin1Char('/') +
                                                 model->name(), filters.join(u";;"_s), &selectedFilter);

    if(f_name.isEmpty())
        return;

    if(!PlayListParser::isPlayList(f_name)) //append selected extension
    {
        QStringList selectedFilters = selectedFilter.section(QLatin1Char('('), 1).remove(QLatin1Char(')')).split(QChar::Space);
        if(selectedFilters.isEmpty())
            return;

        QString ext = selectedFilters.first().remove(QLatin1Char('*')); //use first extension
        f_name.append(ext);

        QFileInfo info(f_name);

        if(info.exists())
        {
            if (QMessageBox::question(parent, tr("Save Playlist"), tr("%1 already exists.\nDo you want to replace it?")
                                      .arg(info.fileName()), QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok)
            {
                return;
            }
        }
    }

    if (!f_name.isEmpty())
    {
        model->savePlaylist(f_name);
        m_lastDir = QFileInfo(f_name).absoluteDir().path();
    }
}

void UiHelper::jumpToTrack(QWidget *parent, PlayListModel *model)
{
    if(!m_jumpDialog)
        m_jumpDialog = new JumpToTrackDialog(model, parent);

    if(m_jumpDialog->isHidden())
        m_jumpDialog->show();

    m_jumpDialog->raise();
}

void UiHelper::about(QWidget *parent)
{
    AboutDialog *dialog = new AboutDialog(parent);
    dialog->exec();
    dialog->deleteLater();
}

void UiHelper::toggleVisibility()
{
    emit toggleVisibilityCalled();
}

void UiHelper::showMainWindow()
{
    emit showMainWindowCalled();
}

void UiHelper::exit()
{
    qApp->closeAllWindows();
    qApp->quit();
}

void UiHelper::replaceAndPlay(const QStringList &paths)
{
    if(paths.isEmpty())
        return;

    MediaPlayer::instance()->stop();
    PlayListModel *pl = PlayListManager::instance()->selectedPlayList();
    PlayListManager::instance()->activatePlayList(pl);
    pl->clear();

    connect(pl, &PlayListModel::tracksAdded, MediaPlayer::instance(), &MediaPlayer::play);
    connect(pl, &PlayListModel::tracksAdded, this, &UiHelper::disconnectPl);
    connect(pl, &PlayListModel::loaderFinished, this, &UiHelper::disconnectPl);
    pl->addPaths(paths);
}

UiHelper* UiHelper::instance()
{
    return m_instance;
}

void UiHelper::removeAction(QObject *action)
{
    for(MenuType type : m_menus.keys())
    {
        for(QList<QAction *>::iterator it = m_menus[type].actions.begin(); it != m_menus[type].actions.end(); ++it)
        {
            if(*it == action)
            {
                m_menus[type].actions.erase(it);
                m_menus[type].menu->menuAction()->setVisible(!m_menus[type].autoHide || !m_menus[type].actions.isEmpty());
                break;
            }
        }
    }
}

void UiHelper::addSelectedFiles(const QStringList &files, bool play)
{
    if(files.isEmpty() || !PlayListManager::instance()->playLists().contains(m_model))
        return;
    if(play)
    {
        PlayListManager::instance()->selectPlayList(m_model);
        replaceAndPlay(files);
    }
    else
    {
        m_model->addPaths(files);
    }
}

void UiHelper::playSelectedFiles(const QStringList &files)
{
    addSelectedFiles(files, true);
}

void UiHelper::disconnectPl()
{
    PlayListModel *model = qobject_cast<PlayListModel*>(sender());
    if(model)
    {
        disconnect(model, &PlayListModel::tracksAdded, MediaPlayer::instance(), &MediaPlayer::play);
        disconnect(model, &PlayListModel::tracksAdded, this, &UiHelper::disconnectPl);
        disconnect(model, &PlayListModel::loaderFinished, this, &UiHelper::disconnectPl);
    }
}
