/*
********************************************************************************
*  Kelli Wiseth
*  kelli[at]alameda-tech-lab[dot]com
*  CIS 255AX
*  Program name: ScreenSaver.java
*  Program Description: Application that simulates a ScreenSaver by 'taking over'
*      the full screensize of the display, and instantiating dynamically created
*      shapes (MyLine, MyTriangle, MySphere) in a variety of randomly generated 
*      sizes and colors. 
*
*  Assignment #6
*  26 April 2005
********************************************************************************
*/

import java.awt.*;
import java.awt.event.*; //ActionEvent, ActionListener
import java.awt.geom.*;
import javax.swing.*;

public class ScreenSaver extends JFrame implements ActionListener {
   private Timer timer;
   private MyShape shape;
   private int width;    
   private int height;    
   private int dpi;            
   private Point deadCenter;                // Midpoint on this display
   private int minY, maxY, minX, maxX;      // Base x, y coordinates on the display
   
   // set window's title bar String and dimensions
   public ScreenSaver() {
      super( "Kelli Wiseth ScreenSaver" );
      Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
      dpi = Toolkit.getDefaultToolkit().getScreenResolution();
      width = screenSize.width;
      height = screenSize.height;
      getContentPane().setBackground(Color.BLACK);
      setSize( width, height);
      setVisible( true );
      deadCenter = new Point (width/2, height/2);
      minX = 2;   // don't start at 0 so we can see it
      maxX = width-2;
      minY = 30; 
      maxY = height-30;
      timer = new Timer (1000, this);
      timer.start();
      
   } //end of the constructor

   // draw shapes with Java2D API
   public void paint( Graphics g )   {
     super.paint( g );  // call superclass's paint method
     Graphics2D g2d = ( Graphics2D ) g;  // cast g to Graphics2D

     for (int ctr=0; ctr < 100; ctr++) {
       // Generate one of three different types of shapes

       int myShapeTypeNum = getRandomInt(1, 3);
  
       /************************************************************************
        * Switch statement that instantiates appropriate shape objects based 
        * on the randomly generated value of myShapeTypeNum.
       *************************************************************************
       */
       
       switch(myShapeTypeNum) { 
       case 1:
       /************************************************************************
        * Since we're using a line of randomly-generated thickness (stroke), I 
        * want to take into account that stroke when placing the x, y coordinate
        * and also when generating the subsequent point for the line. I'm thinking
        * that some kind of recursive call might be good for this, but I can't
        * quite figure it out right now, so I'll use what seems a bit of a kludge
        * to try to accomplish the proper placement of MyLine within the screen size,
        * regardless of stroke value.
        ************************************************************************
        */
        
         float strokeValue = getRandomStroke();
         int strokeValueInt = (int)(strokeValue+1);
         g2d.setStroke(new BasicStroke(strokeValue));
         g2d.setColor(getRandomColor());
         shape = new MyLine(getRandomStrokedX(strokeValueInt), getRandomStrokedY(strokeValueInt), getRandomStrokedX(strokeValueInt), getRandomStrokedY(strokeValueInt));
         shape.draw(g2d);
         for (int delay = 0; delay <= 100000000; delay++);
         break;

       case 2:
         g2d.setColor(getRandomColor());
         shape = new MyTriangle(getRandomX(), getRandomY(), getRandomX(), getRandomY(), getRandomX(), getRandomY());
         shape.draw(g2d);
         for (int delay = 0; delay <= 100000000; delay++);
         break;

       case 3:
         /*******************************************************************************
          * The range of possible radius values depends on where the random X coordinate
          * and the random Y coordinates land on the screen--the relative position of x, y
          * must be taken into account before determing the range of possible radius values,
          * since we don't want the circle to go off the display. Rather than using completely
          * random values for all parameters to the GradientPaint method, I'm generating
          * random values for the second x, y coordinates based on the first, so that
          * we get something like a gradient each time. 
          *******************************************************************************
         */ 
         
         int coordinateX = getRandomX();
         int coordinateY = getRandomY();
         int endGradientX   = coordinateX + getRandomInt(1,90);
         int endGradientY   = coordinateY + getRandomInt(1,90);
         int radius = getRandomRadius(coordinateX, coordinateY);
         g2d.setPaint( new GradientPaint(coordinateX, coordinateY, getRandomColor(), endGradientX, 
                       endGradientY, getRandomColor(), true ) );  
         MyShape shape = new MySphere(coordinateX, coordinateY, radius);
         shape.draw(g2d);
         for (int delay = 0; delay <= 100000000; delay++);
         break;

       default:
         System.err.println("Anomoly...should never see this line.");
         break;

       } // end switch statement

     } //inner for-loop       

 } // end method paint
      
   /**************************************************************************
    * actionPerformed simply calls repaint
    **************************************************************************
   */

   public void actionPerformed(ActionEvent timer) {
      repaint();
     }
   
   public int getRandomX() {
   	  return getRandomInt(minX, maxX);
   }
 
   public int getRandomY() {
      return getRandomInt(minY, maxY);
    }

   public int getRandomStrokedX(int stroke) {
   	int coordinateX = getRandomX();
   	if (coordinateX > deadCenter.getX())
   	   return coordinateX-stroke;
   	else
   	   return coordinateX+stroke;
    }
 
   public int getRandomStrokedY(int stroke) {
   	int coordinateY = getRandomY();
   	if (coordinateY > deadCenter.getY())
   	   return coordinateY-stroke;
   	else
   	   return coordinateY+stroke;
   }
   
   public int getRandomRadius(int xCoordinate, int yCoordinate) {
      int randomMaxX;
      int randomMaxY;
      if (xCoordinate < this.deadCenter.getX()) 
         randomMaxX = this.deadCenter.getX()-xCoordinate;
      else 
         randomMaxX = maxX-xCoordinate;

      if (yCoordinate < this.deadCenter.getY())
         randomMaxY = this.deadCenter.getY()-yCoordinate;
      else
         randomMaxY =  maxY-yCoordinate;

      int smallerValue = Math.min(randomMaxX, randomMaxY);
      smallerValue = smallerValue/2;
      int radius = getRandomInt(2, smallerValue);
      return radius;
    }
  
   /**************************************************************************
    * getRandomInt generates a random number given a shift value and
    * a scaling factor
    **************************************************************************
   */
   
   public int getRandomInt(int shiftValue, int scaleFactor ){
     return shiftValue + (int)(Math.random() * scaleFactor);
    }
   
   /**************************************************************************
    * getRandomStroke generates a random float up to the value of the dots per
    * inch of the screenSize obtained from the Toolkit, so the range of possible
    * values will vary, depending on the system and the number obtained from Toolkit.
    **************************************************************************
   */
   
   public float getRandomStroke() {
   	 return (float)(1.0 + Math.random()*dpi);
   }

   /**************************************************************************
    * getRandomColor calls getRandomInt to create three different random
    * values in the range of 0 to 255, and then returns a Color object to the
    * the caller, initialized with those random RGB values.
    **************************************************************************
   */
 
   public Color getRandomColor() {
   	 int rVal = getRandomInt(0, 255);
   	 int gVal = getRandomInt(0, 255);
   	 int bVal = getRandomInt(0, 255);
 	 return new Color(rVal, gVal, bVal);
    }
   
   // execute application
   public static void main( String args[] ) {
      ScreenSaver application = new ScreenSaver();
      application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
   }

} // end class ScreenSaver