using System.Runtime.InteropServices;

namespace Compression.BSA
{
    /*
     * Copied from https://raw.githubusercontent.com/AlexxEG/BSA_Browser/master/Sharp.BSA.BA2/BA2Util/DDS.cs
     * which is also GPL3 code. Modified slightly for Wabbajack
     * 
     */

    /* 
     * Copied from dds.h. Includes (almost) only stuff I need in this project.
     * 
     * Link: https://github.com/digitalutopia1/BA2Lib/blob/master/BA2Lib/dds.h
     * 
     */

    public class DDS
    {
        public static uint HeaderSizeForFormat(DXGI_FORMAT fmt)
        {
            switch (fmt)
            {
                case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB:
                case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB:
                case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM:
                case DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM:
                case DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16:
                case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM:
                case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB:
                    return DDS_HEADER_DXT10.Size + DDS_HEADER.Size;
                default:
                    return DDS_HEADER.Size;
            }
        }

        public const int DDS_MAGIC = 0x20534444; // "DDS "

        public static uint MAKEFOURCC(char ch0, char ch1, char ch2, char ch3)
        {
            // This is alien to me...
            return ((uint)(byte)(ch0) | ((uint)(byte)(ch1) << 8) | ((uint)(byte)(ch2) << 16 | ((uint)(byte)(ch3) << 24)));
        }

        public const int DDS_FOURCC = 0x00000004; // DDPF_FOURCC
        public const int DDS_RGB = 0x00000040; // DDPF_RGB
        public const int DDS_RGBA = 0x00000041; // DDPF_RGB | DDPF_ALPHAPIXELS

        public const int DDS_HEADER_FLAGS_TEXTURE = 0x00001007; // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT
        public const int DDS_HEADER_FLAGS_MIPMAP = 0x00020000; // DDSD_MIPMAPCOUNT
        public const int DDS_HEADER_FLAGS_LINEARSIZE = 0x00080000; // DDSD_LINEARSIZE

        public const int DDS_SURFACE_FLAGS_TEXTURE = 0x00001000; // DDSCAPS_TEXTURE
        public const int DDS_SURFACE_FLAGS_MIPMAP = 0x00400008; // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP

        public const int DDS_ALPHA_MODE_UNKNOWN = 0x0;
    }

    #region dxgiformat.h

    public enum DXGI_FORMAT
    {
        DXGI_FORMAT_R8G8B8A8_UNORM = 28,
        DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29,
        DXGI_FORMAT_R8_UNORM = 61,
        DXGI_FORMAT_BC1_UNORM = 71,
        DXGI_FORMAT_BC1_UNORM_SRGB = 72,
        DXGI_FORMAT_BC2_UNORM = 74,
        DXGI_FORMAT_BC3_UNORM = 77,
        DXGI_FORMAT_BC3_UNORM_SRGB = 78,
        DXGI_FORMAT_BC4_UNORM = 80,
        DXGI_FORMAT_BC5_UNORM = 83,
        DXGI_FORMAT_BC5_SNORM = 84,
        DXGI_FORMAT_B8G8R8A8_UNORM = 87,
        DXGI_FORMAT_B8G8R8X8_UNORM = 88,
        DXGI_FORMAT_BC6H_UF16 = 95,
        DXGI_FORMAT_BC7_UNORM = 98,
        DXGI_FORMAT_BC7_UNORM_SRGB = 99
    }

    #endregion

    public enum DXT10_RESOURCE_DIMENSION
    {
        DIMENSION_TEXTURE1D = 2,
        DIMENSION_TEXTURE2D = 3,
        DIMENSION_TEXTURE3D = 4,
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct DDS_HEADER
    {
        public uint dwSize;
        public uint dwHeaderFlags;
        public uint dwHeight;
        public uint dwWidth;
        public uint dwPitchOrLinearSize;
        public uint dwDepth; // only if DDS_HEADER_FLAGS_VOLUME is set in dwHeaderFlags
        public uint dwMipMapCount;
        public uint dwReserved1; // [11]
        public DDS_PIXELFORMAT PixelFormat; // ddspf
        public uint dwSurfaceFlags;
        public uint dwCubemapFlags;
        public uint dwReserved2; // [3]

        public uint GetSize()
        {
            // 9 uint + DDS_PIXELFORMAT uints + 2 uint arrays with 14 uints total
            // each uint 4 bytes each
            return (9 * 4) + PixelFormat.GetSize() + (14 * 4);
        }

        public void Write(System.IO.BinaryWriter bw)
        {
            bw.Write(dwSize);
            bw.Write(dwHeaderFlags);
            bw.Write(dwHeight);
            bw.Write(dwWidth);
            bw.Write(dwPitchOrLinearSize);
            bw.Write(dwDepth);
            bw.Write(dwMipMapCount);

            // Just write it multiple times, since it's never assigned a value anyway
            for (int i = 0; i < 11; i++)
                bw.Write(dwReserved1);

            // DDS_PIXELFORMAT
            bw.Write(PixelFormat.dwSize);
            bw.Write(PixelFormat.dwFlags);
            bw.Write(PixelFormat.dwFourCC);
            bw.Write(PixelFormat.dwRGBBitCount);
            bw.Write(PixelFormat.dwRBitMask);
            bw.Write(PixelFormat.dwGBitMask);
            bw.Write(PixelFormat.dwBBitMask);
            bw.Write(PixelFormat.dwABitMask);

            bw.Write(dwSurfaceFlags);
            bw.Write(dwCubemapFlags);

            // Just write it multiple times, since it's never assigned a value anyway
            for (int i = 0; i < 3; i++)
                bw.Write(dwReserved2);
        }

        public static uint Size
        {
            get
            {
                unsafe
                {
                    return (uint)(sizeof(DDS_HEADER) + (sizeof(int) * 10) + (sizeof(int) * 2));
                };
            }
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct DDS_HEADER_DXT10
    {
        public uint dxgiFormat;
        public uint resourceDimension;
        public uint miscFlag;
        public uint arraySize;
        public uint miscFlags2;

        public void Write(System.IO.BinaryWriter bw)
        {
            bw.Write(dxgiFormat);
            bw.Write(resourceDimension);
            bw.Write(miscFlag);
            bw.Write(arraySize);
            bw.Write(miscFlags2);
        }

        public static uint Size
        {
            get
            {
                unsafe
                {
                    return (uint)sizeof(DDS_HEADER_DXT10);
                };
            }
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public unsafe struct DDS_PIXELFORMAT
    {
        public uint dwSize;
        public uint dwFlags;
        public uint dwFourCC;
        public uint dwRGBBitCount;
        public uint dwRBitMask;
        public uint dwGBitMask;
        public uint dwBBitMask;
        public uint dwABitMask;

        public DDS_PIXELFORMAT(uint size, uint flags, uint fourCC, uint rgbBitCount, uint rBitMask, uint gBitMask, uint bBitMask, uint aBitMask)
        {
            dwSize = size;
            dwFlags = flags;
            dwFourCC = fourCC;
            dwRGBBitCount = rgbBitCount;
            dwRBitMask = rBitMask;
            dwGBitMask = gBitMask;
            dwBBitMask = bBitMask;
            dwABitMask = aBitMask;
        }

        public uint GetSize()
        {
            // 8 uints, each 4 bytes each
            return 8 * 4;
        }
    }
}
