using AC2RE.Definitions;

namespace LandblockExtraction.Tools;
public static class DDSHeader {
    //MagicNumber for DDS file.
    private static readonly uint MAGICNUMBER = 0x20534444;
    //Length of header
    private static readonly int LENGTH = 125;
    public static byte[] Generate(RenderSurface renderSurface) {
        if (renderSurface.pixelFormat == PixelFormat.CUSTOM_RAW_JPEG) return renderSurface.sourceData;

        DDS_HEADER header = genHeadStruct(renderSurface.pixelFormat, renderSurface.height, renderSurface.width);
        using (MemoryStream stream = new MemoryStream(LENGTH + renderSurface.sourceData.Length)) {
            stream.Write(BitConverter.GetBytes(MAGICNUMBER));
            stream.Write(BitConverter.GetBytes(header.dwSize));
            stream.Write(BitConverter.GetBytes((uint)header.dwFlags));
            stream.Write(BitConverter.GetBytes(header.dwHeight));
            stream.Write(BitConverter.GetBytes(header.dwWidth));
            stream.Write(BitConverter.GetBytes(header.dwPitchOrLinearSize));
            stream.Write(BitConverter.GetBytes(header.dwDepth));
            stream.Write(BitConverter.GetBytes(header.dwMipMapCount));
            foreach (var i in header.dwReserved1) stream.Write(BitConverter.GetBytes(i));
            stream.Write(BitConverter.GetBytes(header.ddspf.dwSize));
            stream.Write(BitConverter.GetBytes((uint)header.ddspf.dwFlags));
            stream.Write(BitConverter.GetBytes(header.ddspf.dwFourCC));
            stream.Write(BitConverter.GetBytes(header.ddspf.dwRGBBitCount));
            stream.Write(BitConverter.GetBytes(header.ddspf.dwRBitMask));
            stream.Write(BitConverter.GetBytes(header.ddspf.dwGBitMask));
            stream.Write(BitConverter.GetBytes(header.ddspf.dwBBitMask));
            stream.Write(BitConverter.GetBytes(header.ddspf.dwABitMask));
            stream.Write(BitConverter.GetBytes(header.dwCaps));
            stream.Write(BitConverter.GetBytes(header.dwCaps2));
            stream.Write(BitConverter.GetBytes(header.dwCaps3));
            stream.Write(BitConverter.GetBytes(header.dwCaps4));
            stream.Write(BitConverter.GetBytes(header.dwReserved2));
            stream.Write(renderSurface.sourceData);

            return stream.ToArray();
        }
    }

    private static DDS_HEADER genHeadStruct(PixelFormat format, uint height, uint width) {
        //I think always same each format?
        DDS_HEADER dDS_HEADER = new DDS_HEADER();
        dDS_HEADER.dwFlags |= DDSHeaderFlags.DDSD_CAPS | DDSHeaderFlags.DDSD_HEIGHT | DDSHeaderFlags.DDSD_WIDTH | DDSHeaderFlags.DDSD_PIXELFORMAT;

        if (dDS_HEADER.dwFlags.HasFlag(DDSHeaderFlags.DDSD_HEIGHT)) dDS_HEADER.dwHeight = height;
        if (dDS_HEADER.dwFlags.HasFlag(DDSHeaderFlags.DDSD_WIDTH)) dDS_HEADER.dwWidth = width;

        if (format == PixelFormat.DXT1) {
            dDS_HEADER.dwFlags |= DDSHeaderFlags.DDSD_LINEARSIZE;
            dDS_HEADER.dwPitchOrLinearSize = CalculateCompressedBlockSize(height, width, (uint)new byte[8].Length);
        }
        if (format == PixelFormat.DXT3 || format == PixelFormat.DXT2 || format == PixelFormat.DXT4 || format == PixelFormat.DXT5) {
            dDS_HEADER.dwFlags |= DDSHeaderFlags.DDSD_LINEARSIZE;
            dDS_HEADER.dwPitchOrLinearSize = CalculateCompressedBlockSize(height, width, (uint)new byte[16].Length);
        }

        if (dDS_HEADER.dwFlags.HasFlag(DDSHeaderFlags.DDSD_PIXELFORMAT)) dDS_HEADER.ddspf = genPixelformatStruct(format);

        return dDS_HEADER;
    }

    private static DDS_PIXELFORMAT genPixelformatStruct(PixelFormat surface) {
        DDS_PIXELFORMAT dDS_PIXELFORMAT = new DDS_PIXELFORMAT();

        switch (surface) {
            case PixelFormat.DXT1:
            case PixelFormat.DXT3:
            case PixelFormat.DXT4:
            case PixelFormat.DXT5:
                dDS_PIXELFORMAT.dwFlags |= DDSPixelformatFlags.DDPF_FOURCC;
                dDS_PIXELFORMAT.dwFourCC = (uint)surface;
                break;
            case PixelFormat.A8R8G8B8:
                dDS_PIXELFORMAT.dwFlags |= DDSPixelformatFlags.DDPF_ALPHAPIXELS | DDSPixelformatFlags.DDPF_RGB;
                dDS_PIXELFORMAT.dwRGBBitCount = 0x20;
                dDS_PIXELFORMAT.dwRBitMask = 0x00FF0000;
                dDS_PIXELFORMAT.dwGBitMask = 0x0000FF00;
                dDS_PIXELFORMAT.dwBBitMask = 0x000000FF;
                dDS_PIXELFORMAT.dwABitMask = 0xFF000000;
                break;
            case PixelFormat.R8G8B8:
                dDS_PIXELFORMAT.dwFlags |= DDSPixelformatFlags.DDPF_RGB;
                dDS_PIXELFORMAT.dwRGBBitCount = 0x18;
                dDS_PIXELFORMAT.dwRBitMask = 0x00FF0000;
                dDS_PIXELFORMAT.dwGBitMask = 0x0000FF00;
                dDS_PIXELFORMAT.dwBBitMask = 0x000000FF;
                break;
            case PixelFormat.A8:
                dDS_PIXELFORMAT.dwFlags |= DDSPixelformatFlags.DDPF_ALPHAPIXELS;
                dDS_PIXELFORMAT.dwRGBBitCount = 0x8;
                dDS_PIXELFORMAT.dwABitMask = 0xFF;
                break;
            default:
                dDS_PIXELFORMAT.dwFlags |= DDSPixelformatFlags.DDPF_FOURCC;
                break;
        }

        return dDS_PIXELFORMAT;
    }
    public static uint CalculateCompressedBlockSize(uint height, uint width, uint blockSize) {
        return ((width + 3) / 4) * ((height + 3) / 4) * blockSize;
    }
}

public struct DDS_HEADER {
    public uint dwSize;
    public DDSHeaderFlags dwFlags;
    public uint dwHeight;
    public uint dwWidth;
    public uint dwPitchOrLinearSize;
    public uint dwDepth;
    public uint dwMipMapCount;
    public uint[] dwReserved1;
    public DDS_PIXELFORMAT ddspf;
    public uint dwCaps;
    public uint dwCaps2;
    public uint dwCaps3;
    public uint dwCaps4;
    public uint dwReserved2;

    public DDS_HEADER() {
        dwSize = 124; //Fixed on 124 octets
        dwFlags = 0;
        dwHeight = 0;
        dwWidth = 0;
        dwPitchOrLinearSize = 0;
        dwDepth = 0;
        dwMipMapCount = 0;
        dwReserved1 = new uint[11];
        foreach (var i in dwReserved1) dwReserved1[i] = 0x00000000;
        ddspf = new DDS_PIXELFORMAT();
        dwCaps = 0x1000;
        dwCaps2 = 0;
        dwCaps3 = 0;
        dwCaps4 = 0;
        dwReserved2 = 0;


    }
}
public struct DDS_PIXELFORMAT {
    public uint dwSize;
    public DDSPixelformatFlags dwFlags;
    public uint dwFourCC;
    public uint dwRGBBitCount;
    public uint dwRBitMask;
    public uint dwGBitMask;
    public uint dwBBitMask;
    public uint dwABitMask;

    public DDS_PIXELFORMAT() {
        dwSize = 32; //Fixed on 32 octets
        dwFlags = 0;
        dwFourCC = 0;
        dwRGBBitCount = 0;
        dwRBitMask = 0;
        dwGBitMask = 0;
        dwBBitMask = 0;
        dwABitMask = 0;
    }
}

[Flags]
public enum DDSHeaderFlags : uint {
    NONE = 0,
    DDSD_CAPS = 1 << 0, // 0x00000001
    DDSD_HEIGHT = 1 << 1, // 0x00000002
    DDSD_WIDTH = 1 << 2, // 0x00000004
    DDSD_PITCH = 1 << 3, // 0x00000008
    DDSD_PIXELFORMAT = 1 << 12, // 0x00001000
    DDSD_MIPMAPCOUNT = 1 << 17, // 0x00020000
    DDSD_LINEARSIZE = 1 << 19, // 0x00080000
    DDSD_DEPTH = 1 << 23, // 0x00800000
}

[Flags]
public enum DDSPixelformatFlags : uint {
    NONE = 0,
    DDPF_ALPHAPIXELS = 1 << 0, // 0x00000001
    DDPF_ALPHA = 1 << 1, // 0x00000002
    DDPF_FOURCC = 1 << 2, // 0x00000004
    DDPF_RGB = 1 << 6, // 0x00000040
    DDPF_YUV = 1 << 9, // 0x00000200
    DDPF_LUMINANCE = 1 << 17, // 0x00020000
}