
import java.awt.Frame;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.FileNotFoundException;

import com.hermetica.magician.GL;
import com.hermetica.magician.CoreGL;
import com.hermetica.magician.CoreGLU;
import com.hermetica.magician.GLDrawable;
import com.hermetica.magician.GLEventListener;
import com.hermetica.magician.GLU;
import com.hermetica.magician.GLComponent;
import com.hermetica.magician.GLComponentFactory;
import com.hermetica.magician.GLCapabilities;
import com.hermetica.magician.GLUQuadric;

import com.hermetica.vecmath.Vec3f;

import bsh.Interpreter;
import bsh.EvalError;

public class StartBSHClass extends Frame implements GLEventListener
{
  private final double sqrt2 = Math.sqrt( 2.0 );
  private final double sqrt2_div2 = sqrt2/2;
  private final float sqrt2_div2f = (float)sqrt2_div2;
  
  // The OpenGL state machine
  private GL gl;
  private GLU glu;
  
  // The OpenGL component to render onto
  private static GLComponent glc;
  private int width = 900;
  private int height = 500;
  
  private MouseHandler mouseHandler;
  
//these fields are public so the Interpreter may get access
  
  //mouse move variables
  private int lastx;
  private int lasty;
  public float rotateX;
  public float rotateY;
  private boolean mouseMoving;
  
  //key move variables
  public float transX;
  public float transY;
  public float transZ;
  
  public float eyeX;
  public float eyeY = 0.5f;
  public float eyeZ = 1.5f;
  
  public float centerX = 0.0f;
  public float centerY = 0.0f;
  public float centerZ = 0.0f;
  
  //these fields are public so the Interpreter may get access
  public float rotX;
  public float rotY;
  public float rotZ;
  
  private Interpreter inter;


/******************************************************/
  public static void main( String argv[] )
 {
   StartBSHClass k = new StartBSHClass();
   //k.startDemo();
  }
/******************************************************/

  public StartBSHClass()
  {
  
    gl = new CoreGL(); 
    glu = new CoreGLU();
    
    // Prepare drawing environment
    setLayout( new BorderLayout() );
    glc = GLComponentFactory.createGLComponent( getWidth(), getHeight() );
    add( "Center", glc );
    pack();
    show();
    
    //Set up the Bean Shell Interpreter
    inter = new Interpreter();
    inter.setVariable( "gl", gl );
    inter.setVariable( "glu", glu );
    inter.setVariable( "my", this );
    
    
    // Setup the context capabilities */
    GLCapabilities cap = glc.getContext().getCapabilities();
    cap.setDepthBits( 12 );
    cap.setPixelType( GLCapabilities.RGBA );
    cap.setColourBits( 24 );
    cap.setRedBits( 1 );
    cap.setDoubleBuffered( GLCapabilities.DOUBLEBUFFER );
    
    // Register the GLComponentListener with the GLComponent
    glc.addGLEventListener( this );

    mouseHandler = new MouseHandler();
    glc.addMouseListener(mouseHandler);
    glc.addMouseMotionListener(mouseHandler);
    
    glc.addKeyListener( new KeyHandler() );
    
    //vc = new ViewController( this, glc );
    addWindowListener( new CloseWindowAndExit() );
    

    // Initialize the component
    glc.initialize();
    
    //glc.setSleepDuration( 450 );
    //glc.start();
    
  }
  
  // Returns the width and height of the demo */
  public int getWidth()
  {
    return width;
  }
  public int getHeight()
  {
    return height;
  }
  
  public void initialize( GLDrawable component )
  {
  
    // Here we set the attributes that will be constant
    // during the rendering contexts lifetime
    gl.glEnable(GL.GL_DEPTH_TEST);
    gl.glDepthFunc(GL.GL_LESS);
    
    // prepare ligthsource
    float ambient[] = {0.2f,0.2f,0.3f,1.0f };
    float diffuse[] = {1.0f,1.0f,1.0f,1.0f };
    float position[] = {1.0f,1.0f,1.0f,0.0f };
    float lmodel_ambient[] = {0.4f,0.4f,0.4f,1.0f };
    
    gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, ambient);
    gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, diffuse);
    gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, position);
    
    gl.glEnable(GL.GL_LIGHT0);
    gl.glEnable(GL.GL_LIGHTING);
    gl.glEnable(GL.GL_COLOR_MATERIAL);
    
    // smooth the drawing
    gl.glShadeModel(GL.GL_SMOOTH);
    gl.glEnable(GL.GL_NORMALIZE);
    
    // set background to white
    gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    
  }

  // Handles viewport resizing
  public void reshape( GLDrawable component, int x, int y, int width, int height )
  {
    // Setup the viewport
    
    gl.glViewport( component, 0, 0, width, height );
    gl.glMatrixMode( GL.GL_PROJECTION );
    gl.glLoadIdentity();
    glu.gluPerspective( 45.0, (float) width/height, 0.015, 50.0 );
    gl.glMatrixMode( GL.GL_MODELVIEW );
  
  }

  // produce the image
  public void display( GLDrawable component )
  {
    System.out.println("StartBSHClass.display - start");
  
    // Clear the color and depth buffers.
     gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
      
    // Set up for scene
    gl.glMatrixMode(GL.GL_MODELVIEW);
    gl.glLoadIdentity();
    
    gl.glRotatef( rotY, 0.0f, 1.0f, 0.0f );
    
    // position the eye relative to scene
    glu.gluLookAt(	eyeX, eyeY, eyeZ,  // eye
              centerX, centerY, centerZ,   // looking at
              0.0f, 1.0f, 0.0f);   // is up

    //rotate for mouse move
    gl.glRotatef( rotateX, 1.0f, 0.0f, 0.0f );
    gl.glRotatef( rotateY, 0.0f, 1.0f, 0.0f );
    
    //transelate for key moves
    gl.glTranslatef( transX, transY, transZ );
    
    
    try
    {
      inter.source("StartBSHClass.txt");
      
    }
    catch(FileNotFoundException e)
    {
      System.out.println("StartBSHClass.display - file not found: "+e);
      System.exit(1);
    }
    catch( EvalError e2 )
    {
      System.out.println("StartBSHClass.display - inter.source: "+e2);
    }

    
  }
  
  // Returns a GL pipeline from the listener
  public GL getGL()
  {
    return gl;
  }
  
  //inner class to handle mouse events
  class MouseHandler implements MouseListener, MouseMotionListener
  {
    /** methods required for MouseListener and MouseMotionListener interfaces */
    public void mouseReleased(MouseEvent e)
    {
      /** object is not moving anymore */
      mouseMoving=false;
      /** repainting the complete tree */
      glc.repaint();
    }
    
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}
    public void mouseMoved(MouseEvent e) {}
    public void mouseClicked(MouseEvent e) {}
    
    public void mousePressed(MouseEvent e)
    {
      /** record mouse down coordinates */
      lastx = e.getX();
      lasty = e.getY();
      mouseMoving=true;
    }
    
  
    public void mouseDragged(MouseEvent e)
    {
      /** create a rotation quaternion that tracks the mouse movement */	
      int x = e.getX();
      int y = e.getY();
      //Dimension dim = e.getComponent().getSize();
      int width = getWidth();
      int height = getHeight();
  
      
      //find angles to rotate
      //when dragging mouse along the X-axis of the screen,
      //it makes sense to rotate around the Y-axis of the module (at least in this example)
      rotateY += (180.0f*((float)(x-lastx)/(float)(width)));
      rotateX += (-180.0f*((float)(y-lasty)/(float)(height)));
      
      lastx = x;
      lasty = y;
      
      glc.repaint();
    }
  }

  //inner class to handle keyevents
  class KeyHandler extends KeyAdapter
  {
  
    private boolean shift;

    public void keyPressed(KeyEvent e)
    {
      int code = e.getKeyCode();
      String str = KeyEvent.getKeyText( code );
      String mod = KeyEvent.getKeyModifiersText( code );
      
      if( str.indexOf( "Shift" ) != -1 )
      {
        shift = true;
      }
      
      System.out.println("StartBSHClass.keyPressed - code="+code+" - getKeyText:" + str+ "mod:"+mod);

      if( str.compareTo("Up") == 0  && shift)
      {
        eyeY += 0.01f;
        centerY += 0.01f;
      }
      else if( str.compareTo("Down") == 0  && shift)
      {
        eyeY -= 0.01f;
        centerY -= 0.01f;
      }
      else if( str.compareTo("Up") == 0 )
      {
        eyeZ -= 0.01f;
        centerZ += 0.01f;
      }
      else if( str.compareTo("Down") == 0 )
      {
        eyeZ += 0.01f;
        centerZ -= 0.01f;
      }
      else if( str.compareTo("Page Up") == 0 )
      {
        eyeZ -= 0.01f;
        //centerZ += 0.01f;
      }
      else if( str.compareTo("Page Down") == 0 )
      {
        eyeZ += 0.01f;
        //centerZ -= 0.01f;
      }
      else if( str.compareTo("Left") == 0 && shift)
      {
        eyeX -= 0.01f;
        centerX -= 0.01f;
      }
      else if( str.compareTo("Right") == 0  && shift)
      {
        eyeX += 0.01f;
        centerX += 0.01f;
      }
      else if( str.compareTo("Left") == 0 )
      {
        double angle = -2.0*Math.PI/180.0;
      
        //rotY -= 5;
        float x1 = centerX - eyeX;
        float y1 = centerZ - eyeZ;
        
        float x2 = x1 * (float)Math.cos( angle ) - y1 * (float)Math.sin( angle );
        float y2 = y1 * (float)Math.cos( angle ) - x1 * (float)Math.sin( angle );
        
        centerX = x2 + eyeX;
        centerZ = y2 + eyeZ;
      }
      else if( str.compareTo("Right") == 0 )
      {
        double angle = 2.0*Math.PI/180.0;
      
        System.out.println("Right: cX="+centerX+" cZ="+centerZ);
        
        //rotY -= 5;
        float x1 = centerX - eyeX;
        float y1 = centerZ - eyeZ;
        
        float x2 = x1 * (float)Math.cos( angle ) - y1 * (float)Math.sin( angle );
        float y2 = y1 * (float)Math.cos( angle ) - x1 * (float)Math.sin( angle );
        
        centerX = x2 + eyeX;
        centerZ = y2 + eyeZ;
        
        System.out.println("Right: cX="+centerX+" cZ="+centerZ);

      }
      
      glc.repaint();
    }
    
    public void keyReleased(KeyEvent e)
    {
      int code = e.getKeyCode();
      String str = KeyEvent.getKeyText( code );
      String mod = KeyEvent.getKeyModifiersText( code );
      
      if( str.indexOf( "Shift" ) != -1 )
      {
        shift = false;
      }
      
      
      System.out.println("StartBSHClass.keyReleased - code="+code+" - getKeyText:" + str+ "mod:"+mod);

    }


      

  }

  //inner class to handle closeing window
  
  class CloseWindowAndExit extends WindowAdapter
  {
    public void windowDeiconified( WindowEvent evt )
    {
      if ( glc.isRunning() )
      {
        glc.resume();
      }
    }
    
    public void windowIconified( WindowEvent evt )
    {
      if ( glc.isRunning() )
      {
        glc.suspend();
      }
    }
  
    public void windowClosing( WindowEvent e )
    {
      glc.destroy();
      dispose();
      System.exit( 0 );
    }
  }
}


