/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
#include "cmCMakeHostSystemInformationCommand.h"

#include <sstream>

#include "cmMakefile.h"
#include "cmsys/SystemInformation.hxx"

#if defined(_WIN32)
#    include "cmAlgorithms.h"
#    include "cmGlobalGenerator.h"
#    include "cmGlobalVisualStudio15Generator.h"
#    include "cmSystemTools.h"
#    include "cmVSSetupHelper.h"
#    define HAVE_VS_SETUP_HELPER
#endif

class cmExecutionStatus;

// cmCMakeHostSystemInformation
bool
cmCMakeHostSystemInformationCommand::InitialPass(
    std::vector<std::string> const& args, cmExecutionStatus&)
{
    size_t current_index = 0;

    if(args.size() < (current_index + 2) || args[current_index] != "RESULT")
    {
        this->SetError("missing RESULT specification.");
        return false;
    }

    std::string const& variable = args[current_index + 1];
    current_index += 2;

    if(args.size() < (current_index + 2) || args[current_index] != "QUERY")
    {
        this->SetError("missing QUERY specification");
        return false;
    }

    cmsys::SystemInformation info;
    info.RunCPUCheck();
    info.RunOSCheck();
    info.RunMemoryCheck();

    std::string result_list;
    for(size_t i = current_index + 1; i < args.size(); ++i)
    {
        std::string const& key = args[i];
        if(i != current_index + 1)
        {
            result_list += ";";
        }
        std::string value;
        if(!this->GetValue(info, key, value))
        {
            return false;
        }
        result_list += value;
    }

    this->Makefile->AddDefinition(variable, result_list.c_str());

    return true;
}

bool
cmCMakeHostSystemInformationCommand::GetValue(cmsys::SystemInformation& info,
                                              std::string const&        key,
                                              std::string&              value)
{
    if(key == "NUMBER_OF_LOGICAL_CORES")
    {
        value = this->ValueToString(info.GetNumberOfLogicalCPU());
    } else if(key == "NUMBER_OF_PHYSICAL_CORES")
    {
        value = this->ValueToString(info.GetNumberOfPhysicalCPU());
    } else if(key == "HOSTNAME")
    {
        value = this->ValueToString(info.GetHostname());
    } else if(key == "FQDN")
    {
        value = this->ValueToString(info.GetFullyQualifiedDomainName());
    } else if(key == "TOTAL_VIRTUAL_MEMORY")
    {
        value = this->ValueToString(info.GetTotalVirtualMemory());
    } else if(key == "AVAILABLE_VIRTUAL_MEMORY")
    {
        value = this->ValueToString(info.GetAvailableVirtualMemory());
    } else if(key == "TOTAL_PHYSICAL_MEMORY")
    {
        value = this->ValueToString(info.GetTotalPhysicalMemory());
    } else if(key == "AVAILABLE_PHYSICAL_MEMORY")
    {
        value = this->ValueToString(info.GetAvailablePhysicalMemory());
    } else if(key == "IS_64BIT")
    {
        value = this->ValueToString(info.Is64Bits());
    } else if(key == "HAS_FPU")
    {
        value = this->ValueToString(info.DoesCPUSupportFeature(
            cmsys::SystemInformation::CPU_FEATURE_FPU));
    } else if(key == "HAS_MMX")
    {
        value = this->ValueToString(info.DoesCPUSupportFeature(
            cmsys::SystemInformation::CPU_FEATURE_MMX));
    } else if(key == "HAS_MMX_PLUS")
    {
        value = this->ValueToString(info.DoesCPUSupportFeature(
            cmsys::SystemInformation::CPU_FEATURE_MMX_PLUS));
    } else if(key == "HAS_SSE")
    {
        value = this->ValueToString(info.DoesCPUSupportFeature(
            cmsys::SystemInformation::CPU_FEATURE_SSE));
    } else if(key == "HAS_SSE2")
    {
        value = this->ValueToString(info.DoesCPUSupportFeature(
            cmsys::SystemInformation::CPU_FEATURE_SSE2));
    } else if(key == "HAS_SSE_FP")
    {
        value = this->ValueToString(info.DoesCPUSupportFeature(
            cmsys::SystemInformation::CPU_FEATURE_SSE_FP));
    } else if(key == "HAS_SSE_MMX")
    {
        value = this->ValueToString(info.DoesCPUSupportFeature(
            cmsys::SystemInformation::CPU_FEATURE_SSE_MMX));
    } else if(key == "HAS_AMD_3DNOW")
    {
        value = this->ValueToString(info.DoesCPUSupportFeature(
            cmsys::SystemInformation::CPU_FEATURE_AMD_3DNOW));
    } else if(key == "HAS_AMD_3DNOW_PLUS")
    {
        value = this->ValueToString(info.DoesCPUSupportFeature(
            cmsys::SystemInformation::CPU_FEATURE_AMD_3DNOW_PLUS));
    } else if(key == "HAS_IA64")
    {
        value = this->ValueToString(info.DoesCPUSupportFeature(
            cmsys::SystemInformation::CPU_FEATURE_IA64));
    } else if(key == "HAS_SERIAL_NUMBER")
    {
        value = this->ValueToString(info.DoesCPUSupportFeature(
            cmsys::SystemInformation::CPU_FEATURE_SERIALNUMBER));
    } else if(key == "PROCESSOR_NAME")
    {
        value = this->ValueToString(info.GetExtendedProcessorName());
    } else if(key == "PROCESSOR_DESCRIPTION")
    {
        value = info.GetCPUDescription();
    } else if(key == "PROCESSOR_SERIAL_NUMBER")
    {
        value = this->ValueToString(info.GetProcessorSerialNumber());
    } else if(key == "OS_NAME")
    {
        value = this->ValueToString(info.GetOSName());
    } else if(key == "OS_RELEASE")
    {
        value = this->ValueToString(info.GetOSRelease());
    } else if(key == "OS_VERSION")
    {
        value = this->ValueToString(info.GetOSVersion());
    } else if(key == "OS_PLATFORM")
    {
        value = this->ValueToString(info.GetOSPlatform());
#ifdef HAVE_VS_SETUP_HELPER
    } else if(key == "VS_15_DIR")
    {
        // If generating for the VS 15 IDE, use the same instance.
        cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
        if(cmHasLiteralPrefix(gg->GetName(), "Visual Studio 15 "))
        {
            cmGlobalVisualStudio15Generator* vs15gen =
                static_cast<cmGlobalVisualStudio15Generator*>(gg);
            if(vs15gen->GetVSInstance(value))
            {
                return true;
            }
        }

        // Otherwise, find a VS 15 instance ourselves.
        cmVSSetupAPIHelper vsSetupAPIHelper;
        if(vsSetupAPIHelper.GetVSInstanceInfo(value))
        {
            cmSystemTools::ConvertToUnixSlashes(value);
        }
#endif
    } else
    {
        std::string e = "does not recognize <key> " + key;
        this->SetError(e);
        return false;
    }

    return true;
}

std::string
cmCMakeHostSystemInformationCommand::ValueToString(size_t value) const
{
    std::ostringstream tmp;
    tmp << value;
    return tmp.str();
}

std::string
cmCMakeHostSystemInformationCommand::ValueToString(const char* value) const
{
    std::string safe_string = value ? value : "";
    return safe_string;
}

std::string
cmCMakeHostSystemInformationCommand::ValueToString(
    std::string const& value) const
{
    return value;
}
