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

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

        internal IntPtr Pointer { get; set; }
        public static Texture FromSurface(Renderer renderer, Surface surface)
        {
            Texture result = new Texture();
            result.Pointer = SDL.SDL_CreateTextureFromSurface(renderer.Pointer, surface.IntPointer);
            if (result.Pointer == IntPtr.Zero)
            {
                throw SdlException.GenerateException();
            }

            return result;
        }

        public static Texture Create(Renderer renderer, SDL.SDL_TextureAccess access, int w, int h)
        {
            Texture result = new Texture();
            result.Pointer = SDL.SDL_CreateTexture(renderer.Pointer, SDL.SDL_PIXELFORMAT_ARGB8888, (int)access, w, h);
            if (result.Pointer == IntPtr.Zero)
            {
                throw SdlException.GenerateException();
            }
            return result;
        }

        internal static Texture FromIntPointer(IntPtr intPtr)
        {
            Texture result = new Texture();
            result.Pointer = intPtr;
            return result;
        }

        public void Dispose()
        {
            SDL.SDL_DestroyTexture(Pointer);
            Pointer = IntPtr.Zero;
        }

        private bool queryCached;
        private uint queryFormat;
        private int queryAccess, queryW, queryH;
        private void Query()
        {
            if (!queryCached)
            {
                SDL.SDL_QueryTexture(Pointer, out queryFormat, out queryAccess, out queryW, out queryH);
                queryCached = true;
            }
        }

        public int GetWidth()
        {
            Query();
            return queryW;
        }

        public int GetHeight()
        {
            Query();
            return queryH;
        }

        private IntPtr locked;
        private int lockedPitch;
        public void Lock()
        {
            if (locked != IntPtr.Zero)
                throw new InvalidOperationException("Texture already locked");

            int sdlLockTexture = SDL.SDL_LockTexture(Pointer, IntPtr.Zero, out locked, out lockedPitch);
            if (sdlLockTexture != 0)
            {
                throw SdlException.GenerateException();
            }
        }

        public void Fill(byte a, byte r, byte g, byte b)
        {
	        SDL.SDL_Rect rekt = new SDL.SDL_Rect();
	        rekt.h = GetHeight();
	        rekt.w = GetWidth();
	        rekt.x = 0;
	        rekt.y = 0;

	        uint color = (uint)(a << 24);
	        color += (uint)(r << 16);
	        color += (uint)(g << 8);
	        color += b;

	        int sdlFillRect = SDL.SDL_FillRect(Pointer, ref rekt, color);
	        if (sdlFillRect != 0)
	        {
		        throw SdlException.GenerateException();
	        }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void SetPixel(int x, int y, byte a, byte r, byte g, byte b)
        {
            if (y < 0)
                return;

            if (x < 0)
                return;

            if (x >= GetWidth())
                return;

            if (y >= GetHeight())
                return;

            if (locked == IntPtr.Zero)
                throw new InvalidOperationException("Texture not locked");

            int bytesPerPixel = lockedPitch / GetWidth();

            int offset = lockedPitch * y;
            offset += (x * bytesPerPixel);

            Marshal.WriteByte(locked, offset + 0, b);
            Marshal.WriteByte(locked, offset + 1, g);
            Marshal.WriteByte(locked, offset + 2, r);
            Marshal.WriteByte(locked, offset + 3, a);
        }

        public IntPtr GetData0()
        {
	        if (locked == IntPtr.Zero)
		        throw new InvalidOperationException("Texture not locked");

	        return locked;
        }

        public void Unlock()
        {
            if (locked == IntPtr.Zero)
                throw new InvalidOperationException("Texture not locked");
            SDL.SDL_UnlockTexture(Pointer);
            locked = IntPtr.Zero;
            lockedPitch = 0;
        }

        public void SetDrawBlendMode(SDL.SDL_BlendMode mode)
        {
            int i = SDL.SDL_SetTextureBlendMode(Pointer, mode);
            if (i != 0)
                throw SdlException.GenerateException();
        }

        public Surface LockAsSurface()
        {
            if (locked != IntPtr.Zero)
                throw new InvalidOperationException("Texture already locked");

            IntPtr surfacePtr;
            if (SDL.SDL_LockTextureToSurface(Pointer, IntPtr.Zero, out surfacePtr) != 0)
                throw SdlException.GenerateException();
            locked = surfacePtr;
            return new Surface(surfacePtr);
        }
    }
}
