//Anton Dubrau
//comp202 summer 2010 - assignment3 solutions

import java.util.Scanner;


class Fractal{
  //define constants
  static int COLUMNS = 120;
  static int ROWS = 40;
  static int MAX_ITERATIONS = 1000;


  //the main method prints the fractal and prompts for actions
  public static void main(String[] args){
    double x=-.5, dx=6, y=0, dy=4;
    Scanner scanner = new Scanner(System.in);

    char c = ' ';
    while (c != 'q'){ //loop while we don't input q
      //print the fractal
      System.out.println(computeFractal(x,dx,y,dy));

      //get next char
      System.out.print("what next? (i)up, (k)down, (j)left, (l)right, (e)zoom in, (d)zoom out, (v)video, (q)uit: ");
      c = scanner.next().charAt(0); //first char of next token
      
      //the options
      switch (c){
      case'i': y += (dy*.3); break; //up
      case'k': y -= (dy*.3); break; //down
      case'j': x -= (dx*.3); break; //left
      case'l': x += (dx*.3); break; //right
      case'e': dx *= .6; //zoom in
               dy *= .6;
               break;
      case'd': dx /= .6; //zoom out 
               dy /= .6;
               break;
      case'v': printVideo(); break; //print video
      }
    }
  }

  //computes the Fractal in given area as ascii art which is returned as a string
  //input: all doubles: the center x coordinate, the x-range, the center y coordinate, the y-range
  //output: a string representing the ascii fractal at the input position, of size ROWS*COLUMNS
  public static String computeFractal(double x1,double dx,double y1,double dy){
    String s = ""; //we will collect the fractal in a string
    //go through the screen from top left to bottom right, one row at a time
    for (int yi=0; yi < ROWS; yi++){
      for (int xi=0; xi < COLUMNS; xi++){
        double x = x1 -dx/2 +xi*dx/(COLUMNS-1); //find x coordinate
        double y = y1 +dy/2 -yi*dy/(ROWS-1); //find y coordinate
        s += iterationCountToChar(getIterationCount(x,y)); //add character for that coordinate to string
      }
      s += '\n'; //start on next row
    }
    return s;
  }


  //applies the mandelbrot formula to the arguments for at most MAX_TERATIONS
  //input: two doubles denoting an x- and y-coordinate
  //output: the number of iterations it took to get an absolute value larger than 2, or MAX_ITERATIONS
  public static int getIterationCount(double x0,double y0){
    int i = -1;
    double x = 0, y = 0;
    while(((x*x + y*y) <= (2*2)) && (i < MAX_ITERATIONS)){
      double xtemp = x*x - y*y + x0; //we use a temporary because we need the old x
      y = 2*x*y + y0;
      x = xtemp;
      i++;
    }
    return i;
  }


  //given an iteration count, returns the char to be printed
  //input: an integer i denoting the iteration count (see getIterationCount)
  //output: ' ' if the input is equal to MAX_ITERATIONS, otherwise one of
  //        ".,:;?&8SH@" depending on the last digit of floor(i/(ln(i)+1))
  public static char iterationCountToChar(int iterationCount){
    //case iterationCount is maxal - return space
    if (iterationCount == MAX_ITERATIONS) return ' ';

    //iteration count is not maximal - return character based on the count
    switch(((int)(iterationCount/(Math.log(iterationCount)+1)))%10){
    case 0:  return '.';
    case 1:  return ',';
    case 2:  return ':';
    case 3:  return ';';
    case 4:  return '?';
    case 5:  return '&';
    case 6:  return '8';
    case 7:  return 'S';
    case 8:  return 'H';
    default: return '@';
    }
  }

  //prints a video zooming in at some specific location in the mandelbrot set
  public static void printVideo(){
    //set starting position
    double x = 0.001643721971153;
    double y = 0.822467633298876;
    double dx = 6;
    double dy = 4;

    //print and zoom in a loop until frame is very small
    while(dx > 1e-11){
      System.out.println(computeFractal(x,dx,y,dy)); //print
      dx*=.9; //zoom
      dy*=.9;
    }

    //exit
    System.exit(0);
  }
}



