using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using SDL2;

namespace testdrid.SdlWrapper
{
    internal class Surface : IDisposable
    {
        private Surface() {}

        internal Surface(IntPtr pointer)
        {
            IntPointer = pointer;
        }

        public static Surface LoadFromBMP(string filename)
        {
	        FileInfo fi = new FileInfo(filename);
	        if (!fi.Exists)
		        throw new FileNotFoundException(filename);

            Surface result = new Surface();
            result.IntPointer = SDL.SDL_LoadBMP(fi.FullName);
            if (result.IntPointer == IntPtr.Zero)
                throw SdlException.GenerateException();
            return result;
        }

        public static Surface ImageLoad(string filename)
        {
	        FileInfo fi = new FileInfo(filename);
	        if (!fi.Exists)
		        throw new FileNotFoundException(filename);

			Surface result = new Surface();
            result.IntPointer = SDL_image.IMG_Load(fi.FullName);
            if (result.IntPointer == IntPtr.Zero)
                throw SdlException.GenerateException();
            return result;
        }

        public static Surface ImageLoadFromPtr(byte[] buffer)
        {
            IntPtr unmanagedPointer = Marshal.AllocHGlobal(buffer.Length);
            Marshal.Copy(buffer, 0, unmanagedPointer, buffer.Length);

            IntPtr sdlRwFromMem = SDL.SDL_RWFromMem(unmanagedPointer, buffer.Length);

            //IMG_Load_RW
            Surface result = new Surface();
            result.IntPointer = SDL_image.IMG_Load_RW(sdlRwFromMem, 1);
            if (result.IntPointer == IntPtr.Zero)
                throw SdlException.GenerateException();

            Marshal.FreeHGlobal(unmanagedPointer);
            return result;
        }

        public static Surface Create(int width, int height)
        {
            uint rmask, gmask, bmask, amask;
            if (!BitConverter.IsLittleEndian)
            {
                rmask = 0xff000000;
                gmask = 0x00ff0000;
                bmask = 0x0000ff00;
                amask = 0x000000ff;
            }
            else
            {
                rmask = 0x000000ff;
                gmask = 0x0000ff00;
                bmask = 0x00ff0000;
                amask = 0xff000000;
            }
            Surface result = new Surface();
            result.IntPointer = SDL.SDL_CreateRGBSurface(0, width, height, 32, rmask, gmask, bmask, amask);
            if (result.IntPointer == IntPtr.Zero)
                throw SdlException.GenerateException();
            return result;
        }

        internal IntPtr IntPointer { get; set; }

        public void SetColorKey(int flag, uint key)
        {
            SDL.SDL_SetColorKey(IntPointer, flag, key);
        }

        public void Dispose()
        {
            SDL.SDL_FreeSurface(IntPointer);
            IntPointer = IntPtr.Zero;
        }

        public void Fill(uint color)
        {
            int sdlFillRect = SDL.SDL_FillRect(IntPointer, IntPtr.Zero, color);
            if (sdlFillRect != 0)
            {
                throw SdlException.GenerateException();
            }
        }

        public void SetPixel(int x, int y, byte r, byte g, byte b)
        {
            SDL.SDL_Surface sdlSurface = Marshal.PtrToStructure<SDL.SDL_Surface>(IntPointer);
            SDL.SDL_PixelFormat sdlPixelFormat = Marshal.PtrToStructure<SDL.SDL_PixelFormat>(sdlSurface.format);
            
            int offset = (sdlPixelFormat.BytesPerPixel * sdlSurface.w) * y;
            offset += (sdlPixelFormat.BytesPerPixel * x);
            Marshal.WriteByte(sdlSurface.pixels, offset + 0, r);
            Marshal.WriteByte(sdlSurface.pixels, offset + 1, g);
            Marshal.WriteByte(sdlSurface.pixels, offset + 2, b);
            Marshal.WriteByte(sdlSurface.pixels, offset + 3, 0xff);
        }

        public SDL2.SDL.SDL_Surface GetSurfaceData()
        {
            SDL.SDL_Surface ptrToStructure = Marshal.PtrToStructure<SDL.SDL_Surface>(IntPointer);
            return ptrToStructure;
        }
    }
}
