/*
 * DSD Nexus - Qt6 frontend for DSD audio conversion
 * Copyright (c) 2026 Alexander Wichers
 *
 * Based on MystiQ by Maikel Llamaret Heredia (GPLv3)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef CONVERTLIST_H
#define CONVERTLIST_H

#include <QWidget>
#include <QVector>
#include <QElapsedTimer>
#include <QHash>
#include <QMessageBox>
#include <QModelIndex>
#include "pipeline/dsdpipeparameters.h"

class DsdConverter;
class DsdProbe;
class QTreeWidget;
class QTreeWidgetItem;
class ConvertListDelegate;

class Task;

class ConvertList : public QWidget
{
    Q_OBJECT
public:

    explicit ConvertList(QWidget *parent = nullptr);
    ~ConvertList();

    /*! Append a task to the list.
     * @param param the DSD pipeline parameters including source path and output config.
     * @return If the function succeeds, it returns true.
     *  Otherwise, it returns false.
     */
    bool addTask(DsdPipeParameters param);

    /*! Append a list of tasks to the list.
     * @param paramList the list of DSD pipeline parameters
     * @return the number of successfully added tasks.
     */
    int addTasks(const QList<DsdPipeParameters>& paramList);

    bool isBusy() const;
    bool isEmpty() const;
    int count() const;
    int selectedCount() const;
    int finishedCount() const;

    /*! Get the elapsed time of the session (in milliseconds).
     *  @retval 0 the converter is idle.
     */
    int elapsedTime() const;

    /*! Returns the pointer to the DsdPipeParameters object of the
     *  currently selected item.
     *  @return If the function fails, it returns nullptr.
     */
    const DsdPipeParameters* getCurrentIndexParameter() const;

    /*! Determine whether the selected task has failed.
     *  @note If multiple tasks are selected, this function always returns false.
     */
    bool selectedTaskFailed() const;

signals:
    void start_conversion(int index, DsdPipeParameters param);
    void task_finished(int);
    void all_tasks_finished();
    void customContextMenuRequested(const QPoint &pos);
    void itemSelectionChanged();
    void started();
    void stopped();
    void filesDropped(const QStringList &files);

public slots:

    /*! Start the conversion progress.
     *  If another task is being processed, the function does nothing.
     */
    void start();

    /*! Stop the conversion progress.
     */
    void stop();

    /*! Remove all selected tasks but quietly ignore tasks in progress.
     */
    void removeSelectedItems();

    /*! Remove all tasks marked as completed.
     */
    void removeCompletedItems();

    /*! Popup DsdParamDialog to edit conversion parameters of selected tasks.
     */
    void editSelectedParameters();

    /*! Popup an input box to change the output filename
     *  of the **first** selected file.
     *  @warning If multiple files are selected, only the first
     *   file will be changed.
     */
    void changeSelectedOutputFile();

    /*! Popup a directory selection dialog to change the output directory.
     */
    void changeSelectedOutputDirectory();

    /*! Mark selected items as queued so that they will be converted again.
     *  If the converter is idle, start converting those items.
     *  Otherwise, the items are simply marked as queued.
     */
    void retrySelectedItems();

    void retryAll();

    /*! Popup a message box to show the error message.
     *  This function only shows the error message for the first selected task.
     */
    void showErrorMessage();

    /*! Remove all tasks but quietly ignore tasks that are in progress.
     */
    void clear();

private slots:
    void task_finished_slot(int);
    void progress_refreshed(int);
    void show_background_image();
    void hide_background_image();
    void slotHeaderContextMenu(QPoint);
    void slotHeaderContextMenuTriggered(QAction*);
    void slotRestoreListHeaders();
    void slotDoubleClick(QModelIndex);
    void slotAllItemsRemoved();

protected:
    bool list_keyPressEvent(QKeyEvent *event);
    void list_dragEnterEvent(QDragEnterEvent *event);
    void list_dragMoveEvent(QDragMoveEvent *event);
    void list_dragLeaveEvent(QDragLeaveEvent *event);
    void list_dropEvent(QDropEvent *event);
    void list_childRemovedEvent(QChildEvent *event);

private:
    Q_DISABLE_COPY(ConvertList)

    class ListEventFilter;
    friend class ListEventFilter;

    QTreeWidget *m_list;
    ListEventFilter *m_listEventFilter;
    int prev_index;
    DsdConverter *m_converter;
    DsdProbe *m_probe;
    Task *m_current_task;
    bool is_busy;
    bool run_next; ///< run next task regardless of the value of is_busy

    /** this variable should only be accessed by the output_filename_set* functions */
    QHash<QString, int> m_outputFileNames;
    void output_filenames_push(const QString& filename);
    void output_filenames_pop(const QString& filename);
    QHash<QString, int>& output_filenames();

    QElapsedTimer m_startTime;
    void init_treewidget(QTreeWidget*);
    void init_treewidget_fill_column_titles(QStringList&);
    void init_treewidget_columns_visibility(QTreeWidget*);
    bool run_first_queued_task();
    void fill_list_fields(const DsdPipeParameters &param, QStringList &columns);
    void reset_task(Task *task);
    void remove_items(const QList<QTreeWidgetItem*>&);
    void setProgressData(Task *task, int percentage, const QString &text = QString());
    bool change_output_dir(Task *task, const QString &new_dir,
                           QMessageBox::StandardButtons &overwrite, bool show_all_buttons);
    void remove_item(QTreeWidgetItem *item);
    Task* first_selected_task() const;
    Task* get_task(QTreeWidgetItem*) const;
    void refresh_progressbar(Task*);
    void update_tooltip(QTreeWidgetItem *item);
    QList<QTreeWidgetItem*> finished_items() const;
};

#endif // CONVERTLIST_H
