package com.riiablo.codec;

import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.StreamUtils;

import org.apache.commons.io.IOUtils;

import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import com.riiablo.codec.Palette;

public class PL2 {
  private static final int COLORMAPS = 1714;
  private static final int TINTS     = 13;
  private static final int TINT_SIZE = 3 + Palette.COLORS;

  public static final int DEFAULT_INDEX = 31;

  public static final int TINT_WHITE  = COLORMAPS + 0;
  public static final int TINT_RED    = COLORMAPS + 1;
  public static final int TINT_GREEN  = COLORMAPS + 2;
  public static final int TINT_BLUE   = COLORMAPS + 3;
  public static final int TINT_GOLD   = COLORMAPS + 4;
  public static final int TINT_GREY   = COLORMAPS + 5;
  public static final int TINT_BLACK  = COLORMAPS + 6;
  public static final int TINT_7      = COLORMAPS + 7;
  public static final int TINT_ORANGE = COLORMAPS + 8;
  public static final int TINT_YELLOW = COLORMAPS + 9;
  public static final int TINT_10     = COLORMAPS + 10;
  public static final int TINT_11     = COLORMAPS + 11;
  public static final int TINT_12     = COLORMAPS + 12;

  public final byte colormaps[][];
  public final int  tints[];
  public final int  size;

  private PL2(byte[][] colormaps, int[] tints) {
    this.colormaps = colormaps;
    this.tints = tints;
    this.size = COLORMAPS + tints.length;
  }

  public byte[] getColormap(int index) {
    return colormaps[index];
  }

  public int getTint(int index) {
    return tints[index];
  }

  public Texture render() {
    Pixmap pl2Pixmap = new Pixmap(Palette.COLORS, size, Pixmap.Format.RGBA8888);
    ByteBuffer buffer = pl2Pixmap.getPixels();
    for (int i = 0, j = 0; i < size; i++) {
      //buffer.put(colormaps[i]);
      for (int k = 0; k < Palette.COLORS; k++, j += 4) {
        buffer.put(j, colormaps[i][k]);
      }
    }

    buffer.rewind();
    Texture texture = new Texture(pl2Pixmap);
    //texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
    texture.setWrap(Texture.TextureWrap.ClampToEdge, Texture.TextureWrap.ClampToEdge);
    pl2Pixmap.dispose();
    return texture;
  }

  public Texture render(Palette palette) {
    int[] colors = palette.get();
    Pixmap pl2Pixmap = new Pixmap(Palette.COLORS, size, Pixmap.Format.RGBA8888);
    IntBuffer buffer = pl2Pixmap.getPixels().asIntBuffer();
    for (int i = 0, j = 0; i < size; i++) {
      for (int k = 0; k < Palette.COLORS; k++) {
        buffer.put(j++, colors[colormaps[i][k] & 0xFF]);
      }
    }

    Texture texture = new Texture(pl2Pixmap);
    texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
    texture.setWrap(Texture.TextureWrap.ClampToEdge, Texture.TextureWrap.ClampToEdge);
    pl2Pixmap.dispose();
    return texture;
  }

  public static PL2 loadFromFile(FileHandle file) {
    return loadFromStream(file.read());
  }

  public static PL2 loadFromStream(InputStream in) {
    try {
      // Skip the internal palette data which was loaded in the corresponding .dat
      IOUtils.skipFully(in, 0x400);

      // TODO: It may be faster to implement using single dimension using offsets
      byte[][] colormaps = new byte[COLORMAPS + TINTS][Palette.COLORS];
      for (int i = 0; i < COLORMAPS; i++) {
        IOUtils.readFully(in, colormaps[i]);
      }

      int available = in.available();
      if (available % TINT_SIZE > 0) {
        throw new GdxRuntimeException("Remaining bytes does not match an expected tint count.");
      }

      final int tintsUsed = available / TINT_SIZE;
      int[] tints = new int[tintsUsed];
      for (int i = 0, r, g, b; i < tintsUsed; i++) {
        r = in.read();
        g = in.read();
        b = in.read();
        tints[i] = (r << 16) | (g << 8) | b;
      }

      // NOTE: LOADING PL2 is missing a tint, so readFully will not work here :/
      for (int i = 0; i < tintsUsed; i++) {
        IOUtils.readFully(in, colormaps[COLORMAPS + i]);
      }

      return new PL2(colormaps, tints);
    } catch (Throwable t) {
      throw new GdxRuntimeException("Couldn't load PL2 from stream.", t);
    } finally {
      StreamUtils.closeQuietly(in);
    }
  }

}