#include "PluginScannerSubprocess.hpp"
#include "common.h"
#include "DisableCrashReporter.hpp"

using namespace juce;

PluginScannerSubprocess::PluginScannerSubprocess()
{
    formatManager.addDefaultFormats();
}

bool PluginScannerSubprocess::initialiseFromCommandLine (const String& commandLine,
                                                         const String& commandLineUniqueID,
                                                         int timeoutMs)
{
    auto res = ChildProcessWorker::initialiseFromCommandLine(commandLine, commandLineUniqueID, timeoutMs);
    if(res) {
        disableCrashReporter();
    }
    return res;
}

void PluginScannerSubprocess::handleMessageFromCoordinator (const MemoryBlock& mb)
{
    if (mb.isEmpty())
        return;
    
    const std::lock_guard<std::mutex> lock (mutex);
    
    if (const auto results = doScan(mb); !results.isEmpty()) {
        sendResults(results);
    } else {
        pendingBlocks.emplace (mb);
        triggerAsyncUpdate();
    }
}

void PluginScannerSubprocess::handleConnectionLost()
{
    // we really don't want these processes to stick around and forkbomb the user's machine
    exitImmediately();
}

void PluginScannerSubprocess::handleAsyncUpdate()
{
    for (;;)
    {
        const std::lock_guard<std::mutex> lock (mutex);

        if (pendingBlocks.empty())
            return;

        sendResults(doScan(pendingBlocks.front()));
        pendingBlocks.pop();
    }
}

OwnedArray<PluginDescription> PluginScannerSubprocess::doScan (const MemoryBlock& block)
{
    using clock_t = std::chrono::high_resolution_clock;
    auto startTime = clock_t::now();
    
    MemoryInputStream stream { block, false };
    const auto formatName = stream.readString();
    const auto identifier = stream.readString();

    PluginDescription pd;
    pd.fileOrIdentifier = identifier;
    pd.uniqueId = pd.deprecatedUid = 0;

    const auto matchingFormat = [&]() -> AudioPluginFormat*
    {
        for (auto* format : formatManager.getFormats())
            if (format->getName() == formatName)
                return format;

        return nullptr;
    }();
    
    OwnedArray<PluginDescription> results;

    if (matchingFormat != nullptr
        && (MessageManager::getInstance()->isThisTheMessageThread()
            || matchingFormat->requiresUnblockedMessageThreadDuringCreation (pd)))
    {
        matchingFormat->findAllTypesForFile (results, identifier);
        
        auto dt = clock_t::now() - startTime;
        std::cerr << "findAllTypesForFile (pid " << getpid() << ") " << identifier << " has " << results.size() << " types (in "
                  << (float)std::chrono::duration_cast<std::chrono::microseconds>(dt).count() / 1000.f << " ms)\n";
    }
    
#ifndef _MSC_VER
    Process::setDockIconVisible(false);
#endif
    return results;
}

void PluginScannerSubprocess::sendResults (const OwnedArray<PluginDescription>& results)
{
    XmlElement xml ("LIST");

    for (const auto& desc : results)
        xml.addChildElement (desc->createXml().release());

    const auto str = xml.toString();
    sendMessageToCoordinator ({ str.toRawUTF8(), str.getNumBytesAsUTF8() });
}
