//=============================================================================
//  MuseScore
//  Music Composition & Notation
//
//  Copyright (C) 2019 Werner Schweer and others
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2.
//
//  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., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================

#include "crashreporter.h"

#include "ui_crashreporter.h"
#include "config.h"

#include <QDir>
#include <QFileInfo>
#include <QHttpMultiPart>
#include <QMessageBox>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QProcess>

namespace Ms {

//---------------------------------------------------------
//   CrashReporter
//---------------------------------------------------------

CrashReporter::CrashReporter(const QString& miniDumpFile, const QUrl& uploadUrl)
   : _ui(new Ui::CrashReporter), _miniDump(miniDumpFile), _uploadUrl(uploadUrl)
      {
      _ui->setupUi(this);
      }

//---------------------------------------------------------
//   ~CrashReporter
//---------------------------------------------------------

CrashReporter::~CrashReporter()
      {
      delete _ui;
      }

//---------------------------------------------------------
//   CrashReporter::uploadReport
//---------------------------------------------------------

void CrashReporter::uploadReport()
      {
      if (!_networkManager) {
            _networkManager = new QNetworkAccessManager(this);
            connect(_networkManager, &QNetworkAccessManager::finished, this, &CrashReporter::onUploadFinished);
            }

      QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);

      QHttpPart miniDumpPart;
      miniDumpPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
      const QString filename = QFileInfo(_miniDump).fileName();
      const QString contentDisposition(QString("form-data; name=\"upload_file_minidump\"; filename=\"%1\"").arg(filename));
      miniDumpPart.setHeader(QNetworkRequest::ContentDispositionHeader, contentDisposition);
      QFile* file = new QFile(_miniDump, multiPart);
      file->open(QIODevice::ReadOnly);
      miniDumpPart.setBodyDevice(file);
      multiPart->append(miniDumpPart);

      QHttpPart prodPart;
      prodPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"prod\""));
      prodPart.setBody("MS_EDITOR");
      multiPart->append(prodPart);

      QHttpPart verPart;
      verPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"ver\""));
      verPart.setBody(QString("%1.%2").arg(VERSION).arg(BUILD_NUMBER).toLatin1());
      multiPart->append(verPart);

      QHttpPart commentPart;
      commentPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"comments\""));
      commentPart.setBody(_ui->commentText->toPlainText().toUtf8());
      multiPart->append(commentPart);

      QNetworkRequest request(_uploadUrl);
      // TODO: custom user-agent?

      QNetworkReply* reply = _networkManager->post(request, multiPart);
      multiPart->setParent(reply);
      }

//---------------------------------------------------------
//   CrashReporter::onUploadFinished
//---------------------------------------------------------

void CrashReporter::onUploadFinished(QNetworkReply* reply)
      {
      bool success = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200;

      if (success) {
            QMessageBox::information(this, tr("Success!"), tr("Crash report uploaded successfully!"));
            if (_ui->restartCheckBox->isChecked()) {
                  static_assert(sizeof(MSCORE_EXECUTABLE) > 1,
                     "MSCORE_EXECUTABLE should be defined to make it possible to restart MuseScore"
                     );
                  const QDir appDir(qApp->applicationDirPath());
                  QProcess::startDetached(appDir.filePath(MSCORE_EXECUTABLE));
                  }
            close();
            }
      else
            QMessageBox::warning(this, tr("Error"), tr("Error while uploading crash report:\n%1").arg(reply->errorString()));

      reply->deleteLater();
      }

//---------------------------------------------------------
//   CrashReporter::on_sendReportButton_clicked
//---------------------------------------------------------

void CrashReporter::on_sendReportButton_clicked()
      {
      uploadReport();
      }

//---------------------------------------------------------
//   CrashReporter::on_cancelButton_clicked
//---------------------------------------------------------

void CrashReporter::on_cancelButton_clicked()
      {
      close();
      }

} // namespace Ms

//---------------------------------------------------------
//   main
//---------------------------------------------------------

int main(int argc, char** argv)
      {
      if (argc < 2)
            qFatal("Usage: %s <dump_file>", argv[0]);

      QString miniDump(argv[1]);
      if (!QFileInfo(miniDump).exists())
            qFatal("minidump file does not exist");

      QApplication app(argc, argv);

      static_assert(sizeof(CRASH_REPORT_URL) > 1, "Crash report URL must be valid");
      const char* crashReportUrl = CRASH_REPORT_URL;

      Ms::CrashReporter reporter(miniDump, QUrl(crashReportUrl));
      reporter.show();

      return app.exec();
      }
