/*
 * Copyright (C) by Klaas Freitag <freitag@owncloud.com>
 *
 * 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.
 */

#include "logbrowser.h"

#include "stdio.h"
#include <iostream>

#include <QDialogButtonBox>
#include <QTextDocument>
#include <QLayout>
#include <QPushButton>
#include <QLabel>
#include <QFileDialog>
#include <QDir>
#include <QTextStream>
#include <QMessageBox>
#include <QCoreApplication>
#include <QSettings>
#include <QAction>

#include "configfile.h"
#include "logger.h"

namespace OCC {

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

LogWidget::LogWidget(QWidget *parent)
    : QPlainTextEdit(parent)
{
    setReadOnly(true);
    QFont font;
    font.setFamily(QLatin1String("Courier New"));
    font.setFixedPitch(true);
    document()->setDefaultFont(font);
}

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

LogBrowser::LogBrowser(QWidget *parent)
    : QDialog(parent)
    , _logWidget(new LogWidget(parent))
{
    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
    setObjectName("LogBrowser"); // for save/restoreGeometry()
    setWindowTitle(tr("Log Output"));
    setMinimumWidth(600);

    QVBoxLayout *mainLayout = new QVBoxLayout;
    // mainLayout->setMargin(0);

    mainLayout->addWidget(_logWidget);

    QHBoxLayout *toolLayout = new QHBoxLayout;
    mainLayout->addLayout(toolLayout);

    // Search input field
    QLabel *lab = new QLabel(tr("&Search:") + " ");
    _findTermEdit = new QLineEdit;
    lab->setBuddy(_findTermEdit);
    toolLayout->addWidget(lab);
    toolLayout->addWidget(_findTermEdit);

    // find button
    QPushButton *findBtn = new QPushButton;
    findBtn->setText(tr("&Find"));
    connect(findBtn, &QAbstractButton::clicked, this, &LogBrowser::slotFind);
    toolLayout->addWidget(findBtn);

    // stretch
    toolLayout->addStretch(1);
    _statusLabel = new QLabel;
    toolLayout->addWidget(_statusLabel);
    toolLayout->addStretch(5);

    // Debug logging
    _logDebugCheckBox = new QCheckBox(tr("&Capture debug messages") + " ");
    connect(_logDebugCheckBox, &QCheckBox::stateChanged, this, &LogBrowser::slotDebugCheckStateChanged);
    toolLayout->addWidget(_logDebugCheckBox);

    QDialogButtonBox *btnbox = new QDialogButtonBox;
    QPushButton *closeBtn = btnbox->addButton(QDialogButtonBox::Close);
    connect(closeBtn, &QAbstractButton::clicked, this, &QWidget::close);

    mainLayout->addWidget(btnbox);

    // button to permanently save logs
    _permanentLogging = new QCheckBox;
    _permanentLogging->setText(tr("Permanently save logs"));
    _permanentLogging->setToolTip(
        tr("When this option is enabled and no other logging is configured, "
           "logs will be written to a temporary folder and expire after a few hours. "
           "This setting persists across client restarts.\n"
           "\n"
           "Logs will be written to %1")
            .arg(Logger::instance()->temporaryFolderLogDirPath()));
    _permanentLogging->setChecked(ConfigFile().automaticLogDir());
    btnbox->addButton(_permanentLogging, QDialogButtonBox::ActionRole);
    connect(_permanentLogging, &QCheckBox::toggled, this, &LogBrowser::togglePermanentLogging);

    // clear button
    _clearBtn = new QPushButton;
    _clearBtn->setText(tr("Clear"));
    _clearBtn->setToolTip(tr("Clear the log display."));
    btnbox->addButton(_clearBtn, QDialogButtonBox::ActionRole);
    connect(_clearBtn, &QAbstractButton::clicked, this, &LogBrowser::slotClearLog);

    // save Button
    _saveBtn = new QPushButton;
    _saveBtn->setText(tr("S&ave"));
    _saveBtn->setToolTip(tr("Save the log file to a file on disk for debugging."));
    btnbox->addButton(_saveBtn, QDialogButtonBox::ActionRole);
    connect(_saveBtn, &QAbstractButton::clicked, this, &LogBrowser::slotSave);

    setLayout(mainLayout);

    setModal(false);

    Logger::instance()->setLogWindowActivated(true);
    // Direct connection for log coming from this thread, and queued for the one in a different thread
    connect(Logger::instance(), &Logger::logWindowLog, this, &LogBrowser::slotNewLog, Qt::AutoConnection);

    QAction *showLogWindow = new QAction(this);
    showLogWindow->setShortcut(QKeySequence("F12"));
    connect(showLogWindow, &QAction::triggered, this, &QWidget::close);
    addAction(showLogWindow);

    ConfigFile cfg;
    cfg.restoreGeometry(this);
    int lines = cfg.maxLogLines();
    _logWidget->document()->setMaximumBlockCount(lines);
}

LogBrowser::~LogBrowser()
{
}

void LogBrowser::showEvent(QShowEvent *)
{
    // This could have been changed through the --logdebug argument passed through the single application.
    _logDebugCheckBox->setCheckState(Logger::instance()->logDebug() ? Qt::Checked : Qt::Unchecked);
}

void LogBrowser::closeEvent(QCloseEvent *)
{
    ConfigFile cfg;
    cfg.saveGeometry(this);
}


void LogBrowser::slotNewLog(const QString &msg)
{
    if (_logWidget->isVisible()) {
        _logWidget->appendPlainText(msg);
    }
}


void LogBrowser::slotFind()
{
    QString searchText = _findTermEdit->text();

    if (searchText.isEmpty())
        return;

    search(searchText);
}

void LogBrowser::slotDebugCheckStateChanged(int checkState)
{
    Logger::instance()->setLogDebug(checkState == Qt::Checked);
}

void LogBrowser::search(const QString &str)
{
    QList<QTextEdit::ExtraSelection> extraSelections;

    _logWidget->moveCursor(QTextCursor::Start);
    QColor color = QColor(Qt::gray).lighter(130);
    _statusLabel->clear();

    while (_logWidget->find(str)) {
        QTextEdit::ExtraSelection extra;
        extra.format.setBackground(color);

        extra.cursor = _logWidget->textCursor();
        extraSelections.append(extra);
    }

    QString stat = QString::fromLatin1("Search term %1 with %2 search results.").arg(str).arg(extraSelections.count());
    _statusLabel->setText(stat);

    _logWidget->setExtraSelections(extraSelections);
}

void LogBrowser::slotSave()
{
    _saveBtn->setEnabled(false);

    QString saveFile = QFileDialog::getSaveFileName(this, tr("Save log file"), QDir::homePath());

    if (!saveFile.isEmpty()) {
        QFile file(saveFile);

        if (file.open(QIODevice::WriteOnly)) {
            QTextStream stream(&file);
            stream << _logWidget->toPlainText();
            file.close();
        } else {
            QMessageBox::critical(this, tr("Error"), tr("Could not write to log file %1").arg(saveFile));
        }
    }
    _saveBtn->setEnabled(true);
}

void LogBrowser::slotClearLog()
{
    _logWidget->clear();
}

void LogBrowser::togglePermanentLogging(bool enabled)
{
    ConfigFile().setAutomaticLogDir(enabled);

    auto logger = Logger::instance();
    if (enabled) {
        if (!logger->isLoggingToFile()) {
            logger->setupTemporaryFolderLogDir();
        }
    } else {
        logger->disableTemporaryFolderLogDir();
    }
}

} // namespace
