package org.hirw.game;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL12.*;
import static org.lwjgl.stb.STBImage.stbi_image_free;
import static org.lwjgl.stb.STBImage.stbi_load;
import static org.lwjgl.stb.STBImage.stbi_set_flip_vertically_on_load;

import java.nio.ByteBuffer;
import lombok.Getter;
import lombok.Setter;

public class Texture {
  @Getter private int width;
  @Getter private int height;
  @Getter private String texturePath;
  @Getter @Setter private int textureID;
  @Getter @Setter private int channelCount;
  @Getter @Setter private int internalFormat;

  public Texture(int width, int height, String texturePath) {
    this.width = width;
    this.height = height;
    this.texturePath = texturePath;
    guessChannelCountAndInternalFormat();
  }

  private void guessChannelCountAndInternalFormat() {
    int fileExtensionIndex = getTexturePath().lastIndexOf(".");
    if (fileExtensionIndex == -1) {
      // No file extension, so just assume 4 channels (.png -> RGBA)
      setChannelCount(4);
      setInternalFormat(GL_RGBA);
      return;
    }

    String fileExtension = getTexturePath().substring(fileExtensionIndex + 1);
    switch (fileExtension) {
      case "png":
        setChannelCount(4);
        setInternalFormat(GL_RGBA);
        break;
      case "jpg":
        setChannelCount(3);
        setInternalFormat(GL_RGB);
        break;
      default:
        // just pray at this point
        setChannelCount(3);
        setInternalFormat(GL_RGB);
    }
  }

  public void init() {
    ByteBuffer imageBytes = loadImageBytes();
    createTexture(imageBytes);
    stbi_image_free(imageBytes);
  }

  private ByteBuffer loadImageBytes() {
    stbi_set_flip_vertically_on_load(true);
    return stbi_load(
        getTexturePath(),
        new int[getWidth()],
        new int[getHeight()],
        new int[getChannelCount()],
        getChannelCount());
  }

  private void createTexture(ByteBuffer imageBytes) {
    setTextureID(glGenTextures());
    glBindTexture(GL_TEXTURE_2D, getTextureID());
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

    setupFilterParameters();
    setupWrapParameters();

    glTexImage2D(
        GL_TEXTURE_2D,
        0,
        getInternalFormat(),
        getWidth(),
        getHeight(),
        0,
        getInternalFormat(),
        GL_UNSIGNED_BYTE,
        imageBytes);
    // glGenerateMipmap(GL_TEXTURE_2D);
  }

  private void setupFilterParameters() {
    // https://github.com/mattdesl/lwjgl-basics/wiki/textures#texture-parameters
    // Set the minification and Magnifiation filters:
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  }

  private void setupWrapParameters() {
    // https://github.com/mattdesl/lwjgl-basics/wiki/textures#texture-parameters
    // Each Vertex has many attributes, including Position (x, y) and Texture Coordinates (s, t).
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  }
}
