/*
 * 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 <mach/mach.h>
#include <os/proc.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()
	{
		return [NSProcessInfo processInfo].physicalMemory;
	}

	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;
	}

	std::string SystemInfo::GetUserName()
	{
		return "";
	}

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

	std::string SystemInfo::GetDeviceName()
	{
		return "Mac"; //TODO
	}

	std::string SystemInfo::GetDeviceVendorName()
	{
		return "Apple";
	}

	std::string SystemInfo::GetDeviceModelName()
	{
		return ""; //TODO
	}

	std::string SystemInfo::GetOsName()
	{
		return ""; //TODO
	}

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

	std::string SystemInfo::GetOsNameHumanReadable()
	{
		std::stringstream name;
		name << GetOsName() << ' ' << GetOsVersion().major;
		return name.str();
	}

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

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

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

	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?
	}
}
