using LibHac.Loader;
using LibHac.Ns;
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Loaders.Processes.Extensions;
using Ryujinx.Horizon.Common;

namespace Ryujinx.HLE.Loaders.Processes
{
    public struct ProcessResult
    {
        public static ProcessResult Failed => new(null, new ApplicationControlProperty(), false, false, null, 0, 0, 0, TitleLanguage.AmericanEnglish);

        private readonly byte _mainThreadPriority;
        private readonly uint _mainThreadStackSize;

        public readonly IDiskCacheLoadState DiskCacheLoadState;

        public readonly MetaLoader                 MetaLoader;
        public readonly ApplicationControlProperty ApplicationControlProperties;

        public readonly ulong  ProcessId;
        public string          Name;
        public ulong           ProgramId;
        public readonly string ProgramIdText;
        public readonly bool   Is64Bit;
        public readonly bool   DiskCacheEnabled;
        public readonly bool   AllowCodeMemoryForJit;

        public ProcessResult(
            MetaLoader                 metaLoader,
            ApplicationControlProperty applicationControlProperties,
            bool                       diskCacheEnabled,
            bool                       allowCodeMemoryForJit,
            IDiskCacheLoadState        diskCacheLoadState,
            ulong                      pid,
            byte                       mainThreadPriority,
            uint                       mainThreadStackSize,
            TitleLanguage              titleLanguage)
        {
            _mainThreadPriority  = mainThreadPriority;
            _mainThreadStackSize = mainThreadStackSize;

            DiskCacheLoadState = diskCacheLoadState;
            ProcessId          = pid;

            MetaLoader                   = metaLoader;
            ApplicationControlProperties = applicationControlProperties;

            if (metaLoader is not null)
            {
                ulong programId = metaLoader.GetProgramId();

                if (ApplicationControlProperties.Title.ItemsRo.Length > 0)
                {
                    var langIndex = ApplicationControlProperties.Title.ItemsRo.Length > (int)titleLanguage ? (int)titleLanguage : 0;

                    Name = ApplicationControlProperties.Title[langIndex].NameString.ToString();
                }
                else
                {
                    Name = metaLoader.GetProgramName();
                }

                ProgramId     = programId;
                ProgramIdText = $"{programId:x16}";
                Is64Bit       = metaLoader.IsProgram64Bit();
            }

            DiskCacheEnabled      = diskCacheEnabled;
            AllowCodeMemoryForJit = allowCodeMemoryForJit;
        }

        public bool Start(Switch device)
        {
            device.Configuration.ContentManager.LoadEntries(device);

            Result result = device.System.KernelContext.Processes[ProcessId].Start(_mainThreadPriority, _mainThreadStackSize);
            if (result != Result.Success)
            {
                Logger.Error?.Print(LogClass.Loader, $"Process start returned error \"{result}\".");

                return false;
            }

            // TODO: LibHac npdm currently doesn't support version field.
            string version;

            if (ProgramId > 0x0100000000007FFF)
            {
                version = ApplicationControlProperties.DisplayVersionString.ToString();
            }
            else
            {
                version = device.System.ContentManager.GetCurrentFirmwareVersion().VersionString;
            }

            Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {Name} v{version} [{ProgramIdText}] [{(Is64Bit ? "64-bit" : "32-bit")}]");

            return true;
        }
    }
}