from typing import Optional
from OpenGL import GL
import logging

logger = logging.getLogger(__name__)


class Texture:
    def __init__(
        self,
        width: int,
        height: int,
        data: Optional[bytes] = None,
        *,
        pixel_type=GL.GL_RGBA,
    ) -> None:
        self.width = width
        self.height = height
        # GL.GL_RGBA(32bit) or GL.GL_RED(8bit graysclale)
        self.pixel_type = pixel_type
        self.handle = GL.glGenTextures(1)
        logger.debug(f"Texture: {self.handle}")

        self.bind()

        GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1)
        # GL.glPixelStorei(GL.GL_UNPACK_ROW_LENGTH, width)
        GL.glPixelStorei(GL.GL_UNPACK_SKIP_PIXELS, 0)
        GL.glPixelStorei(GL.GL_UNPACK_SKIP_ROWS, 0)

        GL.glTexImage2D(
            GL.GL_TEXTURE_2D,
            0,
            self.pixel_type,
            width,
            height,
            0,
            self.pixel_type,
            GL.GL_UNSIGNED_BYTE,
            data,
        )
        GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR)
        GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR)
        GL.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT)
        GL.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT)

        self.unbind()

    def __del__(self):
        logger.debug(f"{self.handle}")
        GL.glDeleteTextures([self.handle])

    def update(self, x, y, w, h, data):
        self.bind()

        GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1)
        GL.glPixelStorei(GL.GL_UNPACK_ROW_LENGTH, self.width)
        GL.glPixelStorei(GL.GL_UNPACK_SKIP_PIXELS, x)
        GL.glPixelStorei(GL.GL_UNPACK_SKIP_ROWS, y)

        GL.glTexSubImage2D(
            GL.GL_TEXTURE_2D, 0, x, y, w, h, self.pixel_type, GL.GL_UNSIGNED_BYTE, data
        )

        GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 4)
        GL.glPixelStorei(GL.GL_UNPACK_ROW_LENGTH, 0)
        GL.glPixelStorei(GL.GL_UNPACK_SKIP_PIXELS, 0)
        GL.glPixelStorei(GL.GL_UNPACK_SKIP_ROWS, 0)

        self.unbind()

    def bind(self):
        GL.glBindTexture(GL.GL_TEXTURE_2D, self.handle)

    def unbind(self):
        GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
