/*
 * 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/.
 */

#pragma once

#include "Base/Logger.hpp"
#include "Data/Containers/String.hpp"
#include <fmt/format.h>
#include <c4/format.hpp>
#include <map>

namespace OpenVulkano
{
	namespace
	{
		std::tuple<std::string_view, int, int> SplitAppleModelComponents(std::string_view input)
		{
			size_t commaPos = input.rfind(',');

			// Find the boundary between text and number
			size_t numberStart = commaPos - 1;
			while (numberStart > 0 && std::isdigit(input[numberStart - 1])) { --numberStart; }

			if (numberStart < 1 || commaPos == std::string_view::npos) return { input, -1, -1 };

			std::string_view beforeNumber = input.substr(0, numberStart);
			std::string_view number = input.substr(numberStart, commaPos - numberStart);
			std::string_view afterComma = input.substr(commaPos + 1);

			int major = -1, minor = -1;
			c4::from_chars(c4::csubstr(number.begin(), number.length()), &major);
			c4::from_chars(c4::csubstr(afterComma.begin(), afterComma.length()), &minor);

			return { beforeNumber, major, minor };
		}

		std::string DecodeIPhoneName(int major, int minor)
		{
			uint32_t number = std::max(major - 1, 1);
			std::string suffix;
			switch(major)
			{
				case 1: if (minor == 1) number = 1; else if (minor == 2) { number = 3; suffix = "G"; } break;
				case 2: number = 3; suffix = "GS";
				case 4: suffix = "s"; //fallthrough
				case 3: number = 4; break;
				case 6: suffix = "s"; //fallthrough
				case 5: number = 5; if(minor > 2) suffix = "c"; break;
				case 7: if (minor == 1) suffix = " Plus"; break;
				case 8: if (minor == 4) return "iPhone SE";
					number = 6; suffix = "s";
					if (minor == 2) suffix += " Plus"; break;
				case 9: number = 7; if ((minor & 1) == 0) suffix = " Plus"; break;
				case 10: if (minor < 3) { number = 8; if (minor == 2) suffix = " Plus"; } else return "iPhone X"; break;
				case 11: { switch (minor) {
						case 2: return "iPhone XS";
						case 8: return "iPhone XR";
						case 4: case 6: return "iPhone XS Max";
					} break; }
				case 12:
					if (minor == 8) return "iPhone SE (2nd generation)";
					if (minor > 2) suffix = " Pro"; if (minor > 4) suffix += " Max"; break;
				case 13:
					if (minor == 1) { suffix = " Mini"; break; }
					if (minor > 2) suffix = " Pro"; if (minor == 4) suffix += " Max"; break;
				case 14: if (minor == 6) return "iPhone SE (3nd generation)";
					if (minor == 2) suffix = " Pro"; if (minor == 3) suffix = " Pro Max";
					if (minor == 4) suffix = " Mini"; if (minor > 6) number = 14; if (minor == 8) suffix = " Plus";
					break;
				case 15:
					if (minor == 2) suffix = " Pro"; else if (minor == 3) suffix = " Pro Max";
					else if (minor > 3) number = 15; if (minor == 5) suffix = " Plus"; break;
				case 16: suffix = " Pro"; if (minor == 2) suffix += " Max"; break;
				case 17: if (minor == 1) suffix = " Pro"; else if (minor == 2) suffix = " Pro Max"; else if (minor == 4) suffix = " Plus"; break;
			}
			return fmt::format("iPhone {}{}", number, suffix);
		}

		std::string DecodeIPadName(int major, int minor)
		{
			std::string type = "unknown";
			uint32_t gen = major * 1000 + minor;
			std::map<std::pair<int, int>, std::pair<std::string, uint32_t>> options = {
					{{1, 1}, {"", 1}}, {{1, 2}, {"", 1}}, {{1, 3}, {"", 1}},
					{{2, 1}, {"", 2}}, {{2, 2}, {"", 2}}, {{2, 3}, {"", 2}}, {{2, 4}, {"", 2}},
					{{3, 1}, {"", 3}}, {{3, 2}, {"", 3}}, {{3, 3}, {"", 3}},
					{{3, 4}, {"", 4}}, {{3, 5}, {"", 4}}, {{3, 6}, {"", 4}},
					{{6, 11}, {"", 5}}, {{6, 12}, {"", 5}},
					{{7, 5}, {"", 6}}, {{7, 6}, {"", 6}},
					{{7, 11}, {"", 7}}, {{7, 12}, {"", 7}},
					{{11, 6}, {"", 8}}, {{11, 7}, {"", 8}},
					{{12, 1}, {"", 9}}, {{12, 2}, {"", 9}},
					{{13, 18}, {"", 10}}, {{13, 19}, {"", 10}},
					{{2, 5}, {"Mini", 1}}, {{2, 6}, {"Mini", 1}}, {{2, 7}, {"Mini", 1}},
					{{4, 4}, {"Mini", 2}}, {{4, 5}, {"Mini", 2}}, {{4, 6}, {"Mini", 2}},
					{{4, 7}, {"Mini", 3}}, {{4, 8}, {"Mini", 3}}, {{4, 9}, {"Mini", 3}},
					{{5, 1}, {"Mini", 4}}, {{5, 2}, {"Mini", 4}},
					{{11, 1}, {"Mini", 5}}, {{11, 2}, {"Mini", 5}},
					{{14, 1}, {"Mini", 6}}, {{14, 2}, {"Mini", 6}},
					{{4, 1}, {"Air", 1}}, {{4, 2}, {"Air", 1}}, {{4, 3}, {"Air", 1}},
					{{5, 3}, {"Air", 2}}, {{5, 4}, {"Air", 2}},
					{{11, 3}, {"Air", 3}}, {{11, 4}, {"Air", 3}},
					{{13, 1}, {"Air", 4}}, {{13, 2}, {"Air", 4}},
					{{13, 16}, {"Air", 5}}, {{13, 17}, {"Air", 5}},
					{{6, 7}, {"Pro 12.9\"", 1}}, {{6, 8}, {"Pro 12.9\"", 1}},
					{{7, 1}, {"Pro 12.9\"", 2}}, {{7, 2}, {"Pro 12.9\"", 2}},
					{{8, 5}, {"Pro 12.9\"", 3}}, {{8, 6}, {"Pro 12.9\"", 3}}, {{8, 7}, {"Pro 12.9\"", 3}}, {{8, 8}, {"Pro 12.9\"", 3}},
					{{8, 11}, {"Pro 12.9\"", 4}}, {{8, 12}, {"Pro 12.9\"", 4}},
					{{13, 8}, {"Pro 12.9\"", 5}}, {{13, 9}, {"Pro 12.9\"", 5}}, {{13, 10}, {"Pro 12.9\"", 5}}, {{13, 11}, {"Pro 12.9\"", 5}},
					{{14, 5}, {"Pro 12.9\"", 6}}, {{14, 6}, {"Pro 12.9\"", 6}},
					{{16, 5}, {"Pro 12.9\"", 7}}, {{16, 6}, {"Pro 12.9\"", 7}},
					{{6, 3}, {"Pro 9.7\"", 1}}, {{6, 4}, {"Pro 9.7\"", 1}},
					{{7, 3}, {"Pro 10.5\"", 1}}, {{7, 4}, {"Pro 10.5\"", 1}},
					{{8, 1}, {"Pro 11\"", 1}}, {{8, 2}, {"Pro 11\"", 1}}, {{8, 3}, {"Pro 11\"", 1}}, {{8, 4}, {"Pro 11\"", 1}},
					{{8, 9}, {"Pro 11\"", 2}}, {{8, 10}, {"Pro 11\"", 2}},
					{{13, 4}, {"Pro 11\"", 3}}, {{13, 5}, {"Pro 11\"", 3}}, {{13, 6}, {"Pro 11\"", 3}}, {{13, 7}, {"Pro 11\"", 3}},
					{{14, 3}, {"Pro 11\"", 4}}, {{14, 4}, {"Pro 11\"", 4}},
					{{16, 3}, {"Pro 11\"", 5}}, {{16, 4}, {"Pro 11\"", 5}},
			};
			auto iter = options.find({major, minor});
			if (iter != options.end())
			{
				type = iter->second.first;
				gen = iter->second.second;
			}
			else
			{
				spdlog::warn("Could not decode iPad identifier iPad{},{}", major, minor);
			}
			return fmt::format("iPad {} ({}{} generation)", type, gen, String::GetOrdinal(gen));
		}

		std::string DecodeModelString(const std::string& model)
		{
			if (model == "i386" || model == "x86_64") return "Simulator";
			auto [type, major, minor] = SplitAppleModelComponents(model);
			if (type == "iPhone") return DecodeIPhoneName(major, minor);
			else if (type == "iPad") return DecodeIPadName(major, minor);
			else if (type == "iPod")
			{
				if (major == 7) major = 6; else if (major == 9) major = 7;
				return fmt::format("iPod touch ({}{} generation)", major, String::GetOrdinal(major));
			}
			else
			{
				spdlog::warn("Could not decode identifier {}", model);
			}
			return "";
		}
	}
}
