/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

#include "../jucer_Headers.h"
#include "jucer_DownloadCompileEngineThread.h"
#include "../LiveBuildEngine/projucer_CompileEngineDLL.h"

bool DownloadCompileEngineThread::downloadAndInstall()
{
    DownloadCompileEngineThread d;

    if (d.runThread())
    {
        if (d.result.failed())
            return withError (d.result.getErrorMessage());

        return true;
    }

    if (d.cancelledByUser)
        return false;

    return withError (d.result.getErrorMessage());
}

DownloadCompileEngineThread::DownloadCompileEngineThread()
    : ThreadWithProgressWindow ("Downloading live-build engine", true, true),
      result (Result::ok()), cancelledByUser (false)
{
}

void DownloadCompileEngineThread::threadComplete (bool userPressedCancel)
{
    cancelledByUser = userPressedCancel;
}

void DownloadCompileEngineThread::run()
{
    setProgress (-1.0);
    setStatusMessage ("Downloading...");

    MemoryBlock zipData;
    result = download (zipData);

    if (result.failed())
        return;

    setStatusMessage ("Installing...");

    File installFolder = getInstallFolder();
    if (! installFolder.createDirectory())
    {
        result = Result::fail ("Install error: cannot create target directory");
        return;
    }

    result = install (zipData, installFolder);
}

Result DownloadCompileEngineThread::download (MemoryBlock& dest)
{
    int statusCode = 302;
    const int timeoutMs = 10000;
    StringPairArray responseHeaders;

    URL url = getDownloadUrl();
    ScopedPointer<InputStream> in = url.createInputStream (false, nullptr, nullptr,
                                                           String(), timeoutMs, &responseHeaders,
                                                           &statusCode, 0);

    if (in == nullptr || statusCode != 200)
        return Result::fail ("Download error: cannot establish connection");

    MemoryOutputStream mo (dest, true);

    int64 size = in->getTotalLength();
    int64 bytesReceived = -1;
    String msg("Downloading...  (123)");

    for (int64 pos = 0; pos < size; pos += bytesReceived)
    {
        setStatusMessage (msg.replace ("123", File::descriptionOfSizeInBytes (pos)));

        if (threadShouldExit())
            return Result::fail ("Download error: operation interrupted");

        bytesReceived = mo.writeFromInputStream (*in, 8192);

        if (bytesReceived == 0)
            return Result::fail ("Download error: lost connection");
    }

    return Result::ok();
}

Result DownloadCompileEngineThread::install (const MemoryBlock& data, File& targetFolder)
{
    MemoryInputStream input (data, false);
    ZipFile zip (input);

    if (zip.getNumEntries() == 0)
        return Result::fail ("Install error: downloaded file is corrupt");

    if (threadShouldExit())
        return Result::fail ("Install error: operation interrupted");

    return zip.uncompressTo (targetFolder);
}

URL DownloadCompileEngineThread::getDownloadUrl()
{
    String urlStub ("http://assets.roli.com/juce/JUCECompileEngine_");

   #if JUCE_MAC
    urlStub << "osx_";
   #elif JUCE_WINDOWS
    urlStub << "windows_";
   #else
    jassertfalse;
   #endif

    return urlStub + ProjectInfo::versionString + ".zip";
}

File DownloadCompileEngineThread::getInstallFolder()
{
    return CompileEngineDLL::getVersionedUserAppSupportFolder();
}

bool DownloadCompileEngineThread::withError(const String& msg)
{
    AlertWindow::showMessageBox (AlertWindow::WarningIcon,
                                 "Download and install", msg);
    return false;
}
