
import java.awt.BorderLayout;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/**
 * Viewer for IFS (Iterated Function System) image mapping.
 * 20W CST8132
 * @author Howard
 */
public class IfsViewer extends JPanel {

	private static final long serialVersionUID = 8462584230403421155L;
	private final boolean COLOUR;
	private final int SCALE_TYPE;
	private final int[][] ifs;

	private IfsViewer(int[][] matrix, boolean useColour, int scaleMapping) 
	{
		ifs=matrix;
		COLOUR = useColour;
		SCALE_TYPE = scaleMapping;
		int w = ifs.length;
		int h = ifs[0].length;
		setPreferredSize(new Dimension(w, h));
		setBackground(Color.white);
	}

	private void drawIfs(Graphics2D g) {
		//		int h = getHeight();
		//		int w = getWidth();
		int w = ifs.length;
		int h = ifs[0].length;
		BufferedImage image = new BufferedImage(w, h,
				BufferedImage.TYPE_INT_RGB);

		float max=0;
		for (int y = 0; y < h; y++) 
			for (int x = 0; x < w; x++) 
			{
				if(ifs[x][y]>max) max=ifs[x][y];
			}

		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {
				float i=ifs[x][y];
				float scale;
				scale=i/max;

				if(SCALE_TYPE < 0)
					scale=(float)Math.sqrt(scale);
				else if(SCALE_TYPE > 0)
					scale=scale*scale;
				//scale = (max / i) % 1;

				int c;
				if(COLOUR)
					c = Color.HSBtoRGB(scale, 1, i>0?1:0);
				else
					c = Color.HSBtoRGB(0, 0, i>0?scale:1);
				image.setRGB(x, y, c);
			}
		}
		g.drawImage(image, 0, 0, null);
	}

	/**
	 * Overrides paint in the JComponent
	 */
	@Override
	protected void paintComponent(Graphics gg) {
		super.paintComponent(gg);
		Graphics2D g = (Graphics2D) gg;
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		drawIfs(g);
	}

	/**
	 * View the IFS map on a JPanel. The mapping will be displayed in grey scale with a linear scale
	 * @param matrix The IFS map to be plotted
	 */
	public static void view(int[][] matrix)
	{
		view(matrix,false,0);
	}

	/**
	 * View the IFS map on a JPanel
	 * @param matrix The IFS map to be plotted
	 * @param useColour Should the map be in colour? if not, grey scale is used
	 * @param scaleMapping Mapping from pixel value to display value. 0 - linear scale, <0 square root scale, >0 squared scale 
	 */
	public static void view(int[][] matrix, boolean useColour, int scaleMapping)
	{
		String colourString = useColour?"Colour ":"Grey ";
		String scaleString = scaleMapping>0?"Squared scale":scaleMapping==0?"Linear scale":"Square root scale";
		SwingUtilities.invokeLater(() -> {
			JFrame f = new JFrame();
			f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			f.setTitle("IFS "+colourString+scaleString);
			f.setResizable(false);
			f.add(new IfsViewer(matrix,useColour,scaleMapping), BorderLayout.CENTER);
			f.pack();
			f.setLocationRelativeTo(null);
			f.setVisible(true);
		});
	}
		
	//MAIN METHOD
	/* Lab 01 
	 * 
	  */
	private static int[][] sierpinskiGasket()
	{
		int scale = -500;
		int width = 519;
		int height = 449;
		int x_offset = 439;
		int y_offset = 509;
		int xDisp = 0;
		int yDisp = 0;
			
		double x = 0;
		double y = 0;
		double u = 0;
		double v = 0;
		
		double rand_num = 0;
		
		int[][] array = new int[width][height];
		
		double[][] values = { {0.50, 0.00, 0.00, 0.50, 0.00, 0.00}, 
							  {0.50, 0.00, 0.00, 0.50, 0.00, 0.50}, 
							  {0.50, 0.00, 0.00, 0.50, 0.43, 0.25}};	
		
		for(int i = 0; i < width * height; i++)
		{
			rand_num = Math.random(); 
			
			u = 0.00;
			v = 0.00;
			
			if(rand_num >= 0.00 && rand_num <= 0.33)
			{
				u = values[0][0] * x + values[0][1] * y + values[0][4]; 
				v = values[0][2] * x + values[0][3] * y + values[0][5]; 
			}
			if(rand_num > 0.33 && rand_num <= 0.66)
			{
				u = values[1][0] * x + values[1][1] * y + values[1][4]; 
				v = values[1][2] * x + values[1][3] * y + values[1][5]; 
			}
			if(rand_num > 0.66 && rand_num <= 1.00)
			{
				u = values[2][0] * x + values[2][1] * y + values[2][4]; 
				v = values[2][2] * x + values[2][3] * y + values[2][5]; 
			}
			
			x = u;
			y = v;
			xDisp = (int) (x*scale+x_offset);
			yDisp = (int) (y*scale+y_offset);
			array[yDisp][xDisp]++;
			
		}
		
		return array;
		
	} 
	
	private static int[][] barnsleyFern()
	{
		int scale = 100;
		int width = 1019;
		int height = 503;
		int x_offset = 228;
		int y_offset = 19;
		int xDisp = 0;
		int yDisp = 0;
			
		double x = 0;
		double y = 0;
		double u = 0;
		double v = 0;
		
		double rand_num = 0;
		
		int[][] array = new int[width][height];
		
		double[][] values = { {0.00, 0.00, 0.00, 0.16, 0.00, 0.00}, 
							  {0.85, 0.04,-0.04, 0.85, 0.00, 1.60}, 
							  {0.20,-0.26, 0.23, 0.22, 0.00, 1.60}, 
							 {-0.15, 0.28, 0.26, 0.24, 0.00, 0.44}};
		
		for(int i = 0; i < (width * height); i++)
		{
			rand_num = Math.random(); 
			
			u = 0.00;
			v = 0.00;
			
			if(rand_num >= 0.00 && rand_num <= 0.1)
			{
				u = values[0][0] * x + values[0][1] * y + values[0][4]; 
				v = values[0][2] * x + values[0][3] * y + values[0][5]; 
			}
			if(rand_num > 0.01 && rand_num <= 0.86)
			{
				u = values[1][0] * x + values[1][1] * y + values[1][4]; 
				v = values[1][2] * x + values[1][3] * y + values[1][5]; 
			}
			if(rand_num > 0.86 && rand_num <= 0.93)
			{
				u = values[2][0] * x + values[2][1] * y + values[2][4]; 
				v = values[2][2] * x + values[2][3] * y + values[2][5]; 
			}
			if(rand_num > 0.93 && rand_num <= 1.00)
			{
				u = values[3][0] * x + values[3][1] * y + values[3][4]; 
				v = values[3][2] * x + values[3][3] * y + values[3][5]; 
			}
			x = u;
			y = v;
			xDisp = (int) (x*scale+x_offset);
			yDisp = (int) (y*scale+y_offset);
			array[yDisp][xDisp]++;
		}
		
		return array;
		
	} 	

	
	public static void main(String[] arg)
	{
		//IfsViewer.view(sierpinskiGasket(), true, 0);
		IfsViewer.view(barnsleyFern(), true, 0);
		
	}
}
