/*
 *  Fraktal.java
 *  Copyright (C) 2002  Frank Buß (fb@frank-buss.de)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You can get the GNU General Public License at
 *  http://www.gnu.org/licenses/gpl.html
 */

import java.awt.*;

public class FraktalCanvas extends Canvas
{
	private Fraktal parent;

	private int primes[];
	private int picWidth = 500;
	private int pic[][] = new int[picWidth][picWidth];
	private int lastUpperLimit;
	private Color colors[][] = new Color[6][768];
        private Image buffer;
        private Graphics gBuffer;
	
	public FraktalCanvas(Fraktal parent)
	{
		this.parent = parent;
		
		// Farben vorausberechnen
		for (int c = 0; c < 768; c++) {
			int r = c;
			int g = 0;
			int b = 0;
			if (r > 255) {
				r = 255;
				g = c - 256;
			}
			if (g > 255) {
				g = 255;
				b = c - 512;
			}
			colors[Fraktal.DRAWING_MODE_RGB][c] = new Color(r, g, b);
			colors[Fraktal.DRAWING_MODE_RBG][c] = new Color(r, b, g);
			colors[Fraktal.DRAWING_MODE_BRG][c] = new Color(b, r, g);
			colors[Fraktal.DRAWING_MODE_GRB][c] = new Color(g, r, b);
			colors[Fraktal.DRAWING_MODE_BGR][c] = new Color(b, g, r);
			colors[Fraktal.DRAWING_MODE_GBR][c] = new Color(g, b, r);
		}

		// Double-Buffering
                buffer = parent.createImage(picWidth, picWidth);
                gBuffer = buffer.getGraphics();
                gBuffer.setColor(new Color(255, 255, 255));
                gBuffer.fillRect(0, 0, picWidth, picWidth);
	}
	
	public Dimension getSize()
	{
		return new Dimension(picWidth, picWidth);
	}
	
	public Dimension getMinimumSize()
	{
		return getSize();
	}
	
	public Dimension getPreferredSize()
	{
		return getSize();
	}
	
	public Dimension getMaximumSize()
	{
		return getSize();
	}

	/**
	 * Bild anzeigen.
	 */	
	public void paint(Graphics g)
	{
		g.drawImage(buffer, 0, 0, this);
	}

	/**
	 * Bild im Speicher berechnen.
	 */
	public void calculatePic()
	{
		int formula = parent.getFormula();
		int upperLimit = parent.getUpperLimit();
		int modulo = parent.getModulo();
		boolean clipping = parent.isClipping();
		int drawingMode = parent.getDrawingMode();
		int intensity = parent.getIntensity();
		boolean normalize = parent.isNormalize();
		int up = parent.getUp();
		int down = parent.getDown();
		int left = parent.getLeft();
		int right = parent.getRight();
		
		if (lastUpperLimit != upperLimit && formula == Fraktal.FORMULA_PRIME)
		{
			// Primzahlen neu berechen
			boolean table[] = new boolean[upperLimit];
			for (int i = 2; i < upperLimit; i++) table[i] = true;
			
			// Sieb des Eratosthenes
			int l = (int) Math.sqrt(upperLimit) + 1;
			for (int i = 2; i < l; i++) {
				if (table[i]) {
					for (int j = 2*i; j < upperLimit; j = j + i) table[j] = false;
				}
			}
	
			// Primzahlen zählen und in primes speichern
			int c = 0;
			for (int i = 2; i < upperLimit; i++) if (table[i]) c++;
			primes = new int[c];
			c = 0;
			for (int i = 2; i < upperLimit; i++) if (table[i]) primes[c++] = i;
		}
		
		// Bild löschen
		for (int xp = 0; xp < picWidth; xp++) {
			for (int yp = 0; yp < picWidth; yp++) {
				pic[xp][yp] = 0;
			}
		}
		
		// Farben berechnen
		int xp = picWidth / 2;
		int yp = picWidth / 2;
		int max = 0;
		int end = formula == Fraktal.FORMULA_PRIME ? primes.length : upperLimit;
		long f0 = 1;
		long f1 = 1;
		long fn = 0;
		for (int i = 0; i < end; i++)
		{
			// Formel auswählen
			switch (formula) {
				case Fraktal.FORMULA_PRIME:
					fn = primes[i];
					break;
				case Fraktal.FORMULA_FIBONACCI:
					fn = f1 + f0;
					f0 = f1;
					f1 = fn;
					break;
				case Fraktal.FORMULA_RANDOM:
					fn = (long) (Math.random() * 65536);
					break;
				case Fraktal.FORMULA_SUM:
					fn = fn + i * i;
					break;
			}
			
			// Modulowert berechnen
			int m = (int)(fn % modulo);
			
			// Cursor bewegen
			if (m == up) {
				yp--;
			} else if (m == down) {
				yp++;
			} else if (m == left) {
				xp--;
			} else if (m == right) {
				xp++;
			}
			
			// Clippen, wenn gewünscht
			if (clipping) {
				if (xp < 0) xp = 0;
				if (xp >= picWidth) xp = picWidth - 1;
				if (yp < 0) yp = 0;
				if (yp >= picWidth) yp = picWidth - 1;
			}
			
			// Farbe erhöhen, wenn gültiger Punkt
			if (xp >= 0 && xp < picWidth && yp >= 0 && yp < picWidth) {
				pic[xp][yp]++;
				if (pic[xp][yp] > max) max = pic[xp][yp];
			}
		}
		
		// normieren, wenn gewünscht
		if (normalize) {
			double mf = (double) max;
			for (xp = 0; xp < picWidth; xp++) {
				for (yp = 0; yp < picWidth; yp++) {
					pic[xp][yp] = (int)((double)pic[xp][yp] / mf * 767);
				}
			}
		}

		// zeichnen
		for (xp = 0; xp < picWidth; xp++) {
			for (yp = 0; yp < picWidth; yp++) {
				int c = pic[xp][yp] * intensity;
				if (c > 767) c = 767;
				gBuffer.setColor(colors[drawingMode][c]);
				gBuffer.drawLine(xp, yp, xp, yp);
			}
		}
	}
}
