package graphics.userInterface;

import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JLabel;

/**
 * Utiliza um arquivo .png para renderizar uma janela de um tamanho determinado na posição determinada
 * */
public class Window extends JLabel implements Serializable {
	/**
	 * Serve pra não dar merda na serialização de objetos.
	 */
	private static final long serialVersionUID = -4810258849360245600L;
	
	/**
	 * O caminho da imagem
	 * */
	private static final String path = "resources/graphics/system/Windows";
	/**
	 * A extensão da imagem
	 * */
	private static final String extension = ".png";
	
	private BufferedImage image;
	private ImageIcon window;
	private BufferedImage[] bordersAndBackground;
	private int width, height;
	private Runnable method;
	
	/**
	 * Inicializa uma janela na posição (x,y) de tamanho width x height
	 * */
	public Window(int x, int y, int width, int height) {
		this.width = width;
		this.height = height;
		
		// Carrega o sprite em image
		image = loadSprite(path + extension);
		
		// Pega a parte relevante do sprite e coloca em iimage
		image = image.getSubimage(0, 768, 128, 128);
		
		// Cria a parte de background
		BufferedImage background = image.getSubimage(0, 0, 64, 64);
		// Obtém-se as bordas
		BufferedImage borders = image.getSubimage(64, 0, 64, 64);
		
		// Pra dar MENOS problemas, foi criado um vetor de Buffered images com todas as subImages cortadas corretamente
		bordersAndBackground = new BufferedImage[] {
				borders.getSubimage(0, 0, 16, 16),   // 0 Canto Superior Esquerdo
				borders.getSubimage(48, 0, 16, 16),  // 1 Canto Superior Direito
				borders.getSubimage(0, 48, 16, 16),  // 2 Canto Inferior Esquerdo
				borders.getSubimage(48, 48, 16, 16), // 3 Canto Inferior Direito
				borders.getSubimage(16, 0, 32, 16),  // 4 Barra Superior 
				borders.getSubimage(16, 48, 32, 16), // 5 Barra Inferior
				borders.getSubimage(0, 16, 16, 32),  // 6 Barra Esquerda
				borders.getSubimage(48, 16, 16, 32), // 7 Barra Direita
				background							 // 8 Background da Janela
		};
		
		// Cria-se a janela em generatedWindow 
		BufferedImage generatedWindow = createWindow(bordersAndBackground, width, height);
		
		// Alinhamentos pra essa bagaça não sair Chaotic Evil
		setHorizontalAlignment(JLabel.LEFT);
		setVerticalAlignment(JLabel.CENTER);
		
		// Seta a janela como o que foigerada e GG WP
		window = new ImageIcon(generatedWindow);
		setIcon(window);
		setSize(window.getIconWidth(), window.getIconHeight());
		setLocation(x, y);
	}
	
	/**
	 * Inicializa uma janela na posição (x,y) de tamanho width x height
	 * */
	public Window(int x, int y, int width, int height, String text) {
		this.width = width;
		this.height = height;
		
		// Carrega o sprite em image
		image = loadSprite(path + extension);
		
		// Pega a parte relevante do sprite e coloca em iimage
		image = image.getSubimage(0, 768, 128, 128);
		
		// Cria a parte de background
		BufferedImage background = image.getSubimage(0, 0, 64, 64);
		// Obtém-se as bordas
		BufferedImage borders = image.getSubimage(64, 0, 64, 64);
		
		// Pra dar MENOS problemas, foi criado um vetor de Buffered images com todas as subImages cortadas corretamente
		bordersAndBackground = new BufferedImage[] {
				borders.getSubimage(0, 0, 16, 16),   // 0 Canto Superior Esquerdo
				borders.getSubimage(48, 0, 16, 16),  // 1 Canto Superior Direito
				borders.getSubimage(0, 48, 16, 16),  // 2 Canto Inferior Esquerdo
				borders.getSubimage(48, 48, 16, 16), // 3 Canto Inferior Direito
				borders.getSubimage(16, 0, 32, 16),  // 4 Barra Superior 
				borders.getSubimage(16, 48, 32, 16), // 5 Barra Inferior
				borders.getSubimage(0, 16, 16, 32),  // 6 Barra Esquerda
				borders.getSubimage(48, 16, 16, 32), // 7 Barra Direita
				background							 // 8 Background da Janela
		};
		
		// Cria-se a janela em generatedWindow 
		BufferedImage generatedWindow = createWindow(bordersAndBackground, width, height, text);
		
		// Alinhamentos pra essa bagaça não sair Chaotic Evil
		setHorizontalAlignment(JLabel.LEFT);
		setVerticalAlignment(JLabel.CENTER);
		
		// Seta a janela como o que foigerada e GG WP
		window = new ImageIcon(generatedWindow);
		setIcon(window);
		setSize(window.getIconWidth(), window.getIconHeight());
		setLocation(x, y);
	}
	
	/**
	 * Inicializa um cursor na posição (x,y) de tamanho 48 x 48
	 * */
	public Window(int x, int y, int width, int height, boolean isCursor) {
		this.width = width;
		this.height = height;
		
		// Carrega o sprite em image
		image = loadSprite("resources/images/tiles/Tiles.png");
		
		// Pega a parte relevante do sprite e coloca em iimage
		image = image.getSubimage(0 * 96, 2* 96, 96, 96);

		// Cria-se a janela em generatedWindow 
		BufferedImage generatedWindow = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		rescale(image, generatedWindow, 0.5, 0.5);
		
		// Alinhamentos pra essa bagaça não sair Chaotic Evil
		setHorizontalAlignment(JLabel.LEFT);
		setVerticalAlignment(JLabel.CENTER);
		
		// Seta a janela como o que foigerada e GG WP
		window = new ImageIcon(generatedWindow);
		setIcon(window);
		setSize(window.getIconWidth(), window.getIconHeight());
		setLocation(x, y);
	}
	
	/**
	 * Cria uma janela sem texto.
	 * @param window o vetor de subImages pra bagaça funcionar
	 * @param width a largura desejada, não coloque valores negativos, porque pode dar merda
	 * @param height a altura desejada, não coloque valores negativos, porque pode dar merda
	 * @return uma BufferedImage de largura width e altura height
	 */
	public BufferedImage createWindow(BufferedImage[] window, int width, int height) {
		// Cria-se uma nova imagem do tamanho explicitado
		BufferedImage newImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		// Abre as portas da criação de um novo desenho
		Graphics2D graphics = newImage.createGraphics();
		
		// Proporções para o background da janela
		double horizontalRatioBackground = ((double) width)/64.0;
		double verticalRatioBackground = ((double) height)/64.0;
		
		// Proporções para os cantos da janela
		double horizontalRatio = ((double) width)/16.0;
		double verticalRatio = ((double) height)/16.0;
				
		// Não me julgue
		BufferedImage[] auxHorizontal = new BufferedImage[2];
		auxHorizontal[0] = rescale(window[4], auxHorizontal[0], horizontalRatio, 1.0);
		auxHorizontal[1] = rescale(window[5], auxHorizontal[1], horizontalRatio, 1.0);
		
		// Esse foi o jeito mais fácil de não dar erro
		BufferedImage[] auxVertical = new BufferedImage[2];
		auxVertical[0] = rescale(window[6], auxVertical[0], 1.0, verticalRatio);
		auxVertical[1] = rescale(window[7], auxVertical[1], 1.0, verticalRatio);
		
		// Cria o background da janela
		BufferedImage auxBackground = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		auxBackground = rescale(window[8], auxBackground, horizontalRatioBackground, verticalRatioBackground);
		
		// O offset conforme o tamanho dos cantos
		int widthOffset = auxBackground.getWidth() - window[0].getWidth();
		int heightOffset = auxBackground.getHeight() - window[0].getHeight();
				
		// Coloca o background pra preencher o fundo
		graphics.drawImage(auxBackground, null, 0, 0);
		
		// Primeiro desenha-se as barras laterais
		graphics.drawImage(auxHorizontal[0], null, window[0].getWidth(), 0);
		graphics.drawImage(auxHorizontal[1], null, window[0].getWidth(), heightOffset);
		graphics.drawImage(auxVertical[0], null, 0, window[0].getHeight());
		graphics.drawImage(auxVertical[1], null, widthOffset, window[0].getHeight());
		
		// Depois desenha-se os cantos
		graphics.drawImage(window[0], null, 0, 0);
		graphics.drawImage(window[1], null, widthOffset, 0);
		graphics.drawImage(window[2], null, 0, heightOffset);
		graphics.drawImage(window[3], null, widthOffset, heightOffset);
		
		// Fecha o uso dos Graphics2D
		graphics.dispose();
		
		// Retorna a imagem desenhada
		return newImage;
	}
	
	/**
	 * Cria uma janela com texto.
	 * @param window o vetor de subImages pra bagaça funcionar
	 * @param width a largura desejada, não coloque valores negativos, porque pode dar merda
	 * @param height a altura desejada, não coloque valores negativos, porque pode dar merda
	 * @param text o texto a ser exibido. Só \n funcionam e agradeça por isso.
	 * @return uma BufferedImage de largura width e altura height
	 */
	public BufferedImage createWindow(BufferedImage[] window, int width, int height, String text) {
		// Cria-se uma nova imagem do tamanho explicitado
		BufferedImage newImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		// Abre as portas da criação de um novo desenho
		Graphics2D graphics = newImage.createGraphics();
		
		// Proporções para o background da janela
		double horizontalRatioBackground = ((double) width)/64.0;
		double verticalRatioBackground = ((double) height)/64.0;
		
		// Proporções para os cantos da janela
		double horizontalRatio = ((double) width)/16.0;
		double verticalRatio = ((double) height)/16.0;
				
		// Não me julgue
		BufferedImage[] auxHorizontal = new BufferedImage[2];
		auxHorizontal[0] = rescale(window[4], auxHorizontal[0], horizontalRatio, 1.0);
		auxHorizontal[1] = rescale(window[5], auxHorizontal[1], horizontalRatio, 1.0);
		
		// Esse foi o jeito mais fácil de não dar erro
		BufferedImage[] auxVertical = new BufferedImage[2];
		auxVertical[0] = rescale(window[6], auxVertical[0], 1.0, verticalRatio);
		auxVertical[1] = rescale(window[7], auxVertical[1], 1.0, verticalRatio);
		
		// Cria o background da janela
		BufferedImage auxBackground = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		auxBackground = rescale(window[8], auxBackground, horizontalRatioBackground, verticalRatioBackground);
		
		// O offset conforme o tamanho dos cantos
		int widthOffset = auxBackground.getWidth() - window[0].getWidth();
		int heightOffset = auxBackground.getHeight() - window[0].getHeight();
				
		// Coloca o background pra preencher o fundo
		graphics.drawImage(auxBackground, null, 0, 0);
		
		// Primeiro desenha-se as barras laterais
		graphics.drawImage(auxHorizontal[0], null, window[0].getWidth(), 0);
		graphics.drawImage(auxHorizontal[1], null, window[0].getWidth(), heightOffset);
		graphics.drawImage(auxVertical[0], null, 0, window[0].getHeight());
		graphics.drawImage(auxVertical[1], null, widthOffset, window[0].getHeight());
		
		// Depois desenha-se os cantos
		graphics.drawImage(window[0], null, 0, 0);
		graphics.drawImage(window[1], null, widthOffset, 0);
		graphics.drawImage(window[2], null, 0, heightOffset);
		graphics.drawImage(window[3], null, widthOffset, heightOffset);
		
		// Depois desenha-se a porra do texto (se houver) que me deu uma dor de cabeça fudida
		if (text != null) {
			// Splita o texto em newlines porque essa merda não é mais automática
			String[] line = text.split("\n");
			
			// Desenha o que foi splitado direitinho
			for (int i = 0; i < line.length; i++) {
				graphics.drawString(line[i], 8, window[0].getHeight() + 2 + graphics.getFontMetrics().getHeight() * i);
			}			
		}
		
		// Fecha o uso dos Graphics2D
		graphics.dispose();
		
		// Retorna a imagem desenhada
		return newImage;
	}

	/**
	 * Dá um redraw todo cagado
	 * @param text O texto dessa janela.
	 */
	public void updateText(String text) {
		BufferedImage generatedWindow = createWindow(bordersAndBackground, width, height, text);
		window = new ImageIcon(generatedWindow);
		setIcon(window);
	}
	
	/**
	 * Reescala uma imagem com o AffineTransform pelas proporções horizontalRatio e verticalRatio
	 * @param source a imagem que será reescalada
	 * @param destination a imagem que servirá como recipiente da gerada (DICA: ESSA PORRA NÃO PODE SER IGUAL A source DE JEITO MANEIRA)
	 * @param horizontalRatio a proporção horizontal da escala
	 * @param verticalRatio a proporção vertical da escala
	 * @return source escalada conforme as ratios
	 */
	public BufferedImage rescale(BufferedImage source, BufferedImage destination, double horizontalRatio, double verticalRatio) {
		AffineTransform affine = new AffineTransform();
		affine.scale(horizontalRatio, verticalRatio);
		
		AffineTransformOp scale = new AffineTransformOp(affine, AffineTransformOp.TYPE_BILINEAR);
		return scale.filter(source, destination);
	}
	
	public void hide() {
		if (getIcon() != null) {
			setIcon(null);
		}
	}
	
	public void show() {
		if (getIcon() == null) {
			setIcon(window);
		}
	}
	
	/**
	 * AEEE PORRRAAAA FILHO DUMA PULTA FUNCIONA
	 * @param method - Funcionator
	 */
	public void setExecuteMethod(Runnable method) {
		this.method = method;
	}
	
	/**
	 * EXECUTA MEU FILHO, EXECUTA SEM MEDO
	 */
	public void executeMethod() {
		method.run();
	}
	
	public void inputManager(KeyEvent e) {
		// Confirma
		if (e.getKeyCode() == KeyEvent.VK_K) {
			executeMethod();
		}
		
		// Cancela ?
		if (e.getKeyCode() == KeyEvent.VK_L) {
			
		}
	}
	
	/**
	 * Carrega um sprite do arquivo file
	 * @param file o nome do arquivo
	 * @return uma BufferedImage contendo a imagem que se deseja
	 */
    public BufferedImage loadSprite(String file) {

        BufferedImage sprite = null;

        try {
            sprite = ImageIO.read(new File( file ));
        } catch (IOException e) {
            e.printStackTrace();
        }

        return sprite;
    }
}

