package com.zerulus.game.graphics;

import com.zerulus.game.math.Matrix;

import java.awt.Color;
import java.awt.image.BufferedImage;

public class Sprite {

    public BufferedImage image;

	private int[] pixels;
	private int[] ogpixels;

	private int w;
	private int h;	

	public static enum effect {NORMAL, SEPIA, REDISH, GRAYSCALE, NEGATIVE, DECAY};

	private float[][] id = {{1.0f, 0.0f, 0.0f},
							{0.0f, 1.0f, 0.0f},
							{0.0f, 0.0f, 1.0f},
							{0.0f, 0.0f, 0.0f}};

	private float[][] negative = {{1.0f, 0.0f, 0.0f},
								  {0.0f, 1.0f, 0.0f},
								  {0.0f, 0.0f, 1.0f},
								  {0.0f, 0.0f, 0.0f}};

	private float[][] decay = {{0.000f, 0.333f, 0.333f},
							   {0.333f, 0.000f, 0.333f},
							   {0.333f, 0.333f, 0.000f},
							   {0.000f, 0.000f, 0.000f}};

	private float[][] sepia = {{0.393f, 0.349f, 0.272f},
							   {0.769f, 0.686f, 0.534f},
							   {0.189f, 0.168f, 0.131f},
							   {0.000f, 0.000f, 0.000f}};

	private float[][] redish = {{1.0f, 0.0f, 0.0f},
							    {0.0f, 0.3f, 0.0f},
								{0.0f, 0.0f, 0.3f},
							    {0.0f, 0.0f, 0.0f}};
								
	private float[][] grayscale = {{0.333f, 0.333f, 0.333f},
							  	   {0.333f, 0.333f, 0.333f},
								   {0.333f, 0.333f, 0.333f},
								   {0.000f, 0.000f, 0.000f}};

	private float[][] currentEffect = id;

    public Sprite(BufferedImage image) {
		this.image = image;
		this.w = image.getWidth();
		this.h = image.getHeight();
		ogpixels = image.getRGB(0, 0, w, h, ogpixels, 0, w);
		pixels = ogpixels;
	}
	
	public int getWidth() { return w; }
	public int getHeight() { return h; }

    public void saveColors() {
		pixels = image.getRGB(0, 0, w, h, pixels, 0, w);
		currentEffect = id;
	}
	
	public void restoreColors() {
		image.setRGB(0, 0, w, h, pixels, 0, w);
	}

	public void restoreDefault() {
		image.setRGB(0, 0, w, h, ogpixels, 0, w);
	}

	// in #FFFFFF format
	public Color hexToColor(String color) {
		return new Color(
            Integer.valueOf(color.substring(1, 3), 16),
            Integer.valueOf(color.substring(3, 5), 16),
            Integer.valueOf(color.substring(5, 7), 16));
	}

	public void setContrast(float value) {
		float[][] effect = id;
		float contrast = (259 * (value + 255)) / (255 * (259 - value));
		for(int i = 0; i < 3; i++) {
			if(i < 3)
				effect[i][i] = contrast;
			effect[3][i] = 128 * (1 - contrast);
		}
		
		addEffect(effect);
	}

	public void setBrightness(float value) {
		float[][] effect = id;
		for(int i = 0; i < 3; i++)
			effect[3][i] = value;
		
		addEffect(effect);
	}

    public void setEffect(effect e) {
		float[][] effect;
		switch (e) {
			case SEPIA: effect = sepia;
			break;
			case REDISH: effect = redish;
			break;
			case GRAYSCALE: effect = grayscale;
			break;
			case NEGATIVE: effect = negative;
			break;
			case DECAY: effect = decay;
			break;
			default: effect = id;
		}
		
		if(effect != currentEffect) {
			addEffect(effect);
		}
	}

	private void addEffect(float[][] effect) {
		float[][] rgb = new float[1][4];
		float[][] xrgb;
		for(int x = 0; x < w; x++) {
		    for(int y = 0; y < h; y++) {
			    int p = pixels[x + y * w];

				int a = (p >> 24) & 0xff;

				rgb[0][0] = (p >> 16) & 0xff;
				rgb[0][1] = (p >> 8) & 0xff;
				rgb[0][2] = (p) & 0xff;
				rgb[0][3] = 1f;

				xrgb = Matrix.multiply(rgb, effect);

				for(int i = 0; i < 3; i++) {
					if(xrgb[0][i] > 255) rgb[0][i] = 255;
					else if(xrgb[0][i] < 0) rgb[0][i] = 0;
					else rgb[0][i] = xrgb[0][i];
				}
				
				p = (a<<24) | ((int) rgb[0][0]<<16) | ((int) rgb[0][1]<<8) | (int) rgb[0][2];
				image.setRGB(x, y, p);
			}
		}
		currentEffect = effect;
	}

    public Sprite getSubimage(int x, int y, int w, int h) {
        return new Sprite(image.getSubimage(x, y, w, h));
	}
	
	public Sprite getNewSubimage(int x, int y, int w, int h) {
		BufferedImage temp = image.getSubimage(x, y, w, h);
		BufferedImage newImage = new BufferedImage(image.getColorModel(), image.getRaster().createCompatibleWritableRaster(w,h), image.isAlphaPremultiplied(), null);
		temp.copyData(newImage.getRaster());
        return new Sprite(newImage);
	}

	public Sprite getNewSubimage() {
		return getNewSubimage(0, 0, this.w, this.h);
	}
}