//
// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
// LICENSE: Atomic Game Engine Editor and Tools EULA
// Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
// license information: https://github.com/AtomicGameEngine/AtomicGameEngine
//

#include <Atomic/Core/ProcessUtils.h>
#include <Atomic/IO/Log.h>
#include <Atomic/IO/FileSystem.h>
#include <Atomic/Engine/Engine.h>
#include <Atomic/Resource/ResourceCache.h>

#include <ToolCore/ToolSystem.h>
#include <ToolCore/ToolEnvironment.h>
#include <ToolCore/Build/BuildSystem.h>
#include <ToolCore/License/LicenseEvents.h>
#include <ToolCore/License/LicenseSystem.h>
#include <ToolCore/Command/Command.h>
#include <ToolCore/Command/CommandParser.h>

#include "AtomicTool.h"

DEFINE_APPLICATION_MAIN(AtomicTool::AtomicTool);

using namespace ToolCore;

namespace AtomicTool
{

AtomicTool::AtomicTool(Context* context) :
    Application(context),
    deactivate_(false)
{

}

AtomicTool::~AtomicTool()
{

}

void AtomicTool::Setup()
{
    const Vector<String>& arguments = GetArguments();

    for (unsigned i = 0; i < arguments.Size(); ++i)
    {
        if (arguments[i].Length() > 1)
        {
            String argument = arguments[i].ToLower();
            String value = i + 1 < arguments.Size() ? arguments[i + 1] : String::EMPTY;

            if (argument == "--cli-data-path")
            {
                if (!value.Length())
                    ErrorExit("Unable to parse --cli-data-path");

                cliDataPath_ = AddTrailingSlash(value);
            }
            else if (argument == "--activate")
            {
                if (!value.Length())
                    ErrorExit("Unable to parse --activation product key");

                activationKey_ = value;
            }
            else if (argument == "--deactivate")
            {
                deactivate_ = true;
            }

        }

    }

    engineParameters_["Headless"] = true;
    engineParameters_["LogLevel"] = LOG_INFO;

    // no default resources (will be initialized later)
    engineParameters_["ResourcePaths"] = "";
}

void AtomicTool::HandleCommandFinished(StringHash eventType, VariantMap& eventData)
{
    GetSubsystem<Engine>()->Exit();
}

void AtomicTool::HandleCommandError(StringHash eventType, VariantMap& eventData)
{
    String error = "Command Error";

    const String& message = eventData[CommandError::P_MESSAGE].ToString();
    if (message.Length())
        error = message;

    ErrorExit(error);
}

void AtomicTool::HandleLicenseEulaRequired(StringHash eventType, VariantMap& eventData)
{
    ErrorExit("\nActivation Required: Please run: atomic-cli activate\n");
}

void AtomicTool::HandleLicenseActivationRequired(StringHash eventType, VariantMap& eventData)
{
    ErrorExit("\nActivation Required: Please run: atomic-cli activate\n");
}

void AtomicTool::HandleLicenseSuccess(StringHash eventType, VariantMap& eventData)
{
    if (command_.Null())
    {
        GetSubsystem<Engine>()->Exit();
        return;
    }

    command_->Run();
}

void AtomicTool::HandleLicenseError(StringHash eventType, VariantMap& eventData)
{
    ErrorExit("\nActivation Required: Please run: atomic-cli activate\n");
}

void AtomicTool::HandleLicenseActivationError(StringHash eventType, VariantMap& eventData)
{
    String message = eventData[LicenseActivationError::P_MESSAGE].ToString();
    ErrorExit(message);
}

void AtomicTool::HandleLicenseActivationSuccess(StringHash eventType, VariantMap& eventData)
{
    LOGRAW("\nActivation successful, thank you!\n\n");
    GetSubsystem<Engine>()->Exit();
}

void AtomicTool::DoActivation()
{
    LicenseSystem* licenseSystem = GetSubsystem<LicenseSystem>();

    if (!licenseSystem->ValidateKey(activationKey_))
    {
        ErrorExit(ToString("\nProduct key \"%s\" is invalid, keys are in the form ATOMIC-XXXX-XXXX-XXXX-XXXX\n", activationKey_.CString()));
        return;
    }

    licenseSystem->LicenseAgreementConfirmed();

    SubscribeToEvent(E_LICENSE_ACTIVATIONERROR, HANDLER(AtomicTool, HandleLicenseActivationError));
    SubscribeToEvent(E_LICENSE_ACTIVATIONSUCCESS, HANDLER(AtomicTool, HandleLicenseActivationSuccess));

    licenseSystem->RequestServerActivation(activationKey_);

}

void AtomicTool::HandleLicenseDeactivationError(StringHash eventType, VariantMap& eventData)
{
    String message = eventData[LicenseDeactivationError::P_MESSAGE].ToString();
    ErrorExit(message);
}

void AtomicTool::HandleLicenseDeactivationSuccess(StringHash eventType, VariantMap& eventData)
{
    LOGRAW("\nDeactivation successful\n\n");
    GetSubsystem<Engine>()->Exit();
}

void AtomicTool::DoDeactivation()
{
    LicenseSystem* licenseSystem = GetSubsystem<LicenseSystem>();

    if (!licenseSystem->LoadLicense())
    {
        ErrorExit("\nNot activated");
        return;
    }

    if (!licenseSystem->Deactivate())
    {
        ErrorExit("\nNot activated\n");
        return;
    }

    SubscribeToEvent(E_LICENSE_DEACTIVATIONERROR, HANDLER(AtomicTool, HandleLicenseDeactivationError));
    SubscribeToEvent(E_LICENSE_DEACTIVATIONSUCCESS, HANDLER(AtomicTool, HandleLicenseDeactivationSuccess));
}

void AtomicTool::Start()
{
    // Subscribe to events
    SubscribeToEvent(E_COMMANDERROR, HANDLER(AtomicTool, HandleCommandError));
    SubscribeToEvent(E_COMMANDFINISHED, HANDLER(AtomicTool, HandleCommandFinished));

    SubscribeToEvent(E_LICENSE_EULAREQUIRED, HANDLER(AtomicTool, HandleLicenseEulaRequired));
    SubscribeToEvent(E_LICENSE_ACTIVATIONREQUIRED, HANDLER(AtomicTool, HandleLicenseActivationRequired));
    SubscribeToEvent(E_LICENSE_ERROR, HANDLER(AtomicTool, HandleLicenseError));
    SubscribeToEvent(E_LICENSE_SUCCESS, HANDLER(AtomicTool, HandleLicenseSuccess));

    const Vector<String>& arguments = GetArguments();

    ToolSystem* tsystem = new ToolSystem(context_);
    context_->RegisterSubsystem(tsystem);

    ToolEnvironment* env = new ToolEnvironment(context_);
    context_->RegisterSubsystem(env);

//#ifdef ATOMIC_DEV_BUILD

    if (!env->InitFromJSON())
    {
        ErrorExit(ToString("Unable to initialize tool environment from %s", env->GetDevConfigFilename().CString()));
        return;
    }

    if (!cliDataPath_.Length())
    {
        cliDataPath_ = env->GetRootSourceDir() + "/Resources/";
    }

//#endif

    ResourceCache* cache = GetSubsystem<ResourceCache>();
    cache->AddResourceDir(env->GetCoreDataDir());
    cache->AddResourceDir(env->GetPlayerDataDir());

    tsystem->SetCLI();
    tsystem->SetDataPath(cliDataPath_);


    if (activationKey_.Length())
    {
        DoActivation();
        return;
    } else if (deactivate_)
    {
        DoDeactivation();
        return;
    }

    BuildSystem* buildSystem = GetSubsystem<BuildSystem>();

    SharedPtr<CommandParser> parser(new CommandParser(context_));

    SharedPtr<Command> cmd(parser->Parse(arguments));
    if (!cmd)
    {
        String error = "No command found";

        if (parser->GetErrorMessage().Length())
            error = parser->GetErrorMessage();

        ErrorExit(error);
        return;
    }

    if (cmd->RequiresProjectLoad())
    {
        FileSystem* fileSystem = GetSubsystem<FileSystem>();

        String projectDirectory = fileSystem->GetCurrentDir();

        Vector<String> projectFiles;
        fileSystem->ScanDir(projectFiles, projectDirectory, "*.atomic", SCAN_FILES, false);
        if (!projectFiles.Size())
        {
            ErrorExit(ToString("No .atomic project file in %s", projectDirectory.CString()));
            return;
        }
        else if (projectFiles.Size() > 1)
        {
            ErrorExit(ToString("Multiple .atomic project files found in %s", projectDirectory.CString()));
            return;
        }

        String projectFile = projectDirectory + "/" + projectFiles[0];

        if (!tsystem->LoadProject(projectFile))
        {
            //ErrorExit(ToString("Failed to load project: %s", projectFile.CString()));
            //return;
        }

        // Set the build path
        String buildFolder = projectDirectory + "/" + "Build";
        buildSystem->SetBuildPath(buildFolder);

        if (!fileSystem->DirExists(buildFolder))
        {
            fileSystem->CreateDir(buildFolder);

            if (!fileSystem->DirExists(buildFolder))
            {
                ErrorExit(ToString("Failed to create build folder: %s", buildFolder.CString()));
                return;
            }
        }

    }

    command_ = cmd;

    // BEGIN LICENSE MANAGEMENT
    if (cmd->RequiresLicenseValidation())
    {
        GetSubsystem<LicenseSystem>()->Initialize();
    }
    else
    {
        if (command_.Null())
        {
            GetSubsystem<Engine>()->Exit();
            return;
        }

        command_->Run();
    }
    // END LICENSE MANAGEMENT

}

void AtomicTool::Stop()
{

}

void AtomicTool::ErrorExit(const String& message)
{
    engine_->Exit(); // Close the rendering window
    exitCode_ = EXIT_FAILURE;

    // Only for WIN32, otherwise the error messages would be double posted on Mac OS X and Linux platforms
    if (!message.Length())
    {
        #ifdef WIN32
        Atomic::ErrorExit(startupErrors_.Length() ? startupErrors_ :
            "Application has been terminated due to unexpected error.", exitCode_);
        #endif
    }
    else
        Atomic::ErrorExit(message, exitCode_);
}



}
