/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

#include "Host/SystemInfo.hpp"
#include "Base/Logger.hpp"
#include <sstream>
#include <fmt/core.h>

#include <mach/mach.h>
#include <os/proc.h>
#include <sys/utsname.h>

#import <Foundation/NSString.h>
#import <Foundation/NSProcessInfo.h>

namespace OpenVulkano
{
	Event<> SystemInfo::OnLowPowerModeChanged;
	Event<> SystemInfo::OnBatteryStateChanged;
	Event<> SystemInfo::OnBatteryLevelChanged;
	Event<> SystemInfo::OnDeviceOrientationChanged;

	size_t SystemInfo::GetSystemRam()
	{
		static const size_t ram = [NSProcessInfo processInfo].physicalMemory;
		return ram;
	}

	size_t SystemInfo::GetSystemRamAvailable()
	{
		return 0; //TODO
	}

	size_t SystemInfo::GetAppRamMax()
	{
		return GetAppRamAvailable() + GetAppRamUsed();
	}

	size_t SystemInfo::GetAppVirtualMemoryMax()
	{
		return INT64_MAX;
	}

	size_t SystemInfo::GetAppRamAvailable()
	{
		return 0; //TODO
	}

	size_t SystemInfo::GetAppRamUsed()
	{
		struct task_basic_info info;
		mach_msg_type_number_t size = sizeof(info);
		kern_return_t kerr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
		if( kerr == KERN_SUCCESS )
		{
			return info.resident_size;
		}
		Logger::PERF->error("Failed to read memory consumption: {}", mach_error_string(kerr));
		return 0;
	}

	const std::string& SystemInfo::GetUserName()
	{
		static const std::string userName = "";
		return userName;
	}

	const std::string& SystemInfo::GetHostName()
	{
		static const std::string hostName = [[NSProcessInfo processInfo].hostName UTF8String];
		return hostName;
	}

	const std::string& SystemInfo::GetDeviceName()
	{
		static const std::string devName = "Mac"; //TODO
		return devName;
	}

	const std::string& SystemInfo::GetDeviceVendorName()
	{
		static const std::string vendorName = "Apple";
		return vendorName;
	}

	const std::string& SystemInfo::GetDeviceModelName()
	{
		static std::string machine;
		if (machine.empty())
		{
			struct utsname systemInfo;
			uname(&systemInfo);
			machine = systemInfo.machine;
		}
		return machine;
	}

	const std::string& SystemInfo::GetOsName()
	{
		static const std::string osName = "MacOS";
		return osName;
	}

	OsVersion   SystemInfo::GetOsVersion()
	{
		static OsVersion osVersion = {};
		if (osVersion.major == 0 && osVersion.minor == 0)
		{
			NSOperatingSystemVersion sysVersion = [NSProcessInfo processInfo].operatingSystemVersion;
			osVersion = { static_cast<int>(sysVersion.majorVersion), static_cast<int>(sysVersion.minorVersion),
						  static_cast<int>(sysVersion.patchVersion), 0 };
		}
		return osVersion;
	}

	const std::string& SystemInfo::GetOsNameHumanReadable()
	{
		static const std::string hrName = fmt::format("{} {}", GetOsName(), GetOsVersion().major);
		return hrName;
	}

	DeviceType  SystemInfo::GetDeviceType()
	{
		return DeviceType::PC;
	}

	size_t SystemInfo::GetCpuCoreCount()
	{
		static const size_t coreCount = [NSProcessInfo processInfo].processorCount;
		return coreCount;
	}

	size_t SystemInfo::GetCpuThreadCount()
	{
		static size_t procCount = [NSProcessInfo processInfo].activeProcessorCount;
		return procCount;
	}

	int32_t SystemInfo::GetCpuTemperature()
	{
		switch([NSProcessInfo processInfo].thermalState)
		{
			case NSProcessInfoThermalStateNominal: return 20;
			case NSProcessInfoThermalStateFair: return 50;
			case NSProcessInfoThermalStateSerious: return 80;
			case NSProcessInfoThermalStateCritical: return 100;
		}
		return -1;
	}

	CpuThermalState SystemInfo::GetCpuThermalState()
	{
		switch([NSProcessInfo processInfo].thermalState)
		{
			case NSProcessInfoThermalStateNominal: return CpuThermalState::Normal;
			case NSProcessInfoThermalStateFair: return CpuThermalState::Fair;
			case NSProcessInfoThermalStateSerious: return CpuThermalState::Serious;
			case NSProcessInfoThermalStateCritical: return CpuThermalState::Critical;
		}
		return CpuThermalState::Normal;
	}

	bool SystemInfo::IsDeviceInLowPowerMode()
	{
		return [NSProcessInfo processInfo].lowPowerModeEnabled;
	}

	void SystemInfo::EnableEnergyEvents()
	{
		//TODO
	}

	BatteryState SystemInfo::GetDeviceBatteryState()
	{
		return BatteryState::Unavailable; //TODO
	}

	float SystemInfo::GetDeviceBatteryLevel()
	{
		return 0; //TODO
	}

	bool SystemInfo::IsMultitaskingSupported()
	{
		return true;
	}

	DeviceOrientation SystemInfo::GetDeviceOrientation()
	{
		return DeviceOrientation::LandscapeRight;
	}

	void SystemInfo::EnableDeviceOrientationEvents()
	{
		//TODO?
	}

	InterfaceOrientation SystemInfo::GetInterfaceOrientation()
	{
		return InterfaceOrientation::Landscape; //TODO?
	}
}
