using System;
using System.Runtime.InteropServices;

namespace AdvChkSys.Util
{
    /// <summary>
    /// Provides utility methods for querying system memory information.
    /// </summary>
    public static class MemoryHelper
    {
        /// <summary>
        /// Gets the amount of available physical memory in bytes.
        /// </summary>
        /// <returns>Available memory in bytes</returns>
        public static ulong GetAvailableMemoryBytes()
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                return GetAvailableMemoryWindows();
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                return GetAvailableMemoryLinux();
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            {
                return GetAvailableMemoryMac();
            }
            else
            {
                // Fallback: unknown, assume 1GB
                return 1UL << 30;
            }
        }

        private static ulong GetAvailableMemoryWindows()
        {
            var memStatus = new MEMORYSTATUSEX
            {
                dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX))
            };
            if (GlobalMemoryStatusEx(ref memStatus))
            {
                return memStatus.ullAvailPhys;
            }
            return 1UL << 30;
        }

        private static ulong GetAvailableMemoryLinux()
        {
            try
            {
                string[] lines = System.IO.File.ReadAllLines("/proc/meminfo");
                ulong memFree = 0, buffers = 0, cached = 0;
                foreach (var line in lines)
                {
                    if (line.StartsWith("MemAvailable:"))
                        memFree = ParseKb(line);
                    else if (line.StartsWith("Buffers:"))
                        buffers = ParseKb(line);
                    else if (line.StartsWith("Cached:"))
                        cached = ParseKb(line);
                }
                return memFree > 0 ? memFree * 1024 : (buffers + cached) * 1024;
            }
            catch
            {
                return 1UL << 30;
            }
        }

        private static ulong GetAvailableMemoryMac()
        {
            // macOS: fallback to 1GB (implementing this is complex and rarely needed)
            return 1UL << 30;
        }

        private static ulong ParseKb(string line)
        {
            var parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length >= 2 && ulong.TryParse(parts[1], out var kb))
                return kb;
            return 0;
        }

        /// <summary>
        /// Structure containing memory status information for Windows systems.
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct MEMORYSTATUSEX
        {
            /// <summary>
            /// The size of the structure, in bytes. You must set this member before calling GlobalMemoryStatusEx.
            /// </summary>
            public uint dwLength;
            /// <summary>
            /// A number between 0 and 100 that specifies the approximate percentage of physical memory
            /// that is in use (0 indicates no memory use and 100 indicates full memory use).
            /// </summary>
            public uint dwMemoryLoad;
            /// <summary>
            /// The total size of physical memory, in bytes.
            /// </summary>
            public ulong ullTotalPhys;
            /// <summary>
            /// The amount of physical memory currently available, in bytes.
            /// This is the amount of physical memory that can be immediately reused without having to write its contents to disk first.
            /// </summary>
            public ulong ullAvailPhys;
            /// <summary>
            /// The current committed memory limit for the system or the current process, whichever is smaller, in bytes.
            /// </summary>
            public ulong ullTotalPageFile;
            /// <summary>
            /// The maximum amount of memory the current process can commit, in bytes.
            /// This value is equal to or smaller than the system-wide available commit value.
            /// </summary>
            public ulong ullAvailPageFile;
            /// <summary>
            /// The size of the user-mode portion of the virtual address space of the calling process, in bytes.
            /// </summary>
            public ulong ullTotalVirtual;
            /// <summary>
            /// The amount of unreserved and uncommitted memory currently in the user-mode portion
            /// of the virtual address space of the calling process, in bytes.
            /// </summary>
            public ulong ullAvailVirtual;
            /// <summary>
            /// Reserved. This value is always 0.
            /// </summary>
            public ulong ullAvailExtendedVirtual;
        }

        /// <summary>
        /// Retrieves information about the system's current usage of both physical and virtual memory.
        /// </summary>
        /// <param name="lpBuffer">A pointer to a <see cref="MEMORYSTATUSEX"/> structure that receives the memory status information.</param>
        /// <returns>If the function succeeds, the return value is <c>true</c>. If the function fails, the return value is <c>false</c>.</returns>
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);

    }
}