
package pibm.patou3d;

import java.io.*;
import java.util.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.awt.event.*;

/**
 * This is the KeyNavigator class.  It accumulates AWT key events (key
 * press and key release) and computes a new transform based on the
 * accumulated events and elapsed time.
 */
public class KeyNavigator {
    
    private     long            aTime;
    private     long            aCount;
    
    private     Vector3d        aPosition;
    private     double          aAngleTheta;
    private     double          aAnglePhi;

    private     byte            aNouveau;
    
    private     TransformGroup  aTG;

    private     double  aVitesseLineaire    =   3.0;
    private     double  aVitesseAngulaire   =   2.0;
    private     double  aVitesseVerticale   =   aVitesseLineaire * 0.7;
    
    private     int     aKeyState = 0;
    private     int     aModifierKeyState = 0;
    
    private     static  final   int     UP_ARROW        = (1<<0);
    private     static  final   int     DOWN_ARROW      = (1<<1);
    private     static  final   int     LEFT_ARROW      = (1<<2);
    private     static  final   int     RIGHT_ARROW     = (1<<3);
    
    private     static  final   int     PLUS_SIGN       = (1<<4);
    private     static  final   int     MINUS_SIGN      = (1<<5);
    private     static  final   int     PAGE_UP         = (1<<6);
    private     static  final   int     PAGE_DOWN       = (1<<7);
    private     static  final   int     HOME_DIR        = (1<<8);
    private     static  final   int     HOME_NOMINAL    = (1<<9);
    
    private     static  final   int     SHIFT           = (1<<10);
    private     static  final   int     ALT             = (1<<11);
    private     static  final   int     META            = (1<<12);
    
    private     static  final   int     KEY_UP          = (1<<13);
    private     static  final   int     KEY_DOWN        = (1<<14);
    
    private     static  final   int     STRAFE_LEFT     = (1<<15);
    private     static  final   int     STRAFE_RIGHT    = (1<<16);
    
    public KeyNavigator(TransformGroup pTG) {
        aTG = pTG;
        aTime = com.sun.j3d.utils.timer.J3DTimer.getValue();
        
        aNouveau = 0;
        aCount = 0;
        
        aPosition = new Vector3d(0,0,2);
        aAngleTheta = 0;
        aAnglePhi = 0;
    }
    
    private long getDeltaTime() {
        long lTime = com.sun.j3d.utils.timer.J3DTimer.getValue();
        long lDelta = lTime - aTime;        
        
        aTime = lTime;
        
        return lDelta;
    }
    
    /**
     * Computes a new transform for the next frame based on
     * the current transform, accumulated keyboard inputs, and
     * elapsed time.  This new transform is written into the target
     * transform group.
     * This method should be called once per frame.
     */
    public void integrateTransformChanges() {
        double lDeltaT = (double) getDeltaTime();
        lDeltaT *= 1e-9;  // en secondes
        
        ++aCount;
        
        // Premiere frame qui bouge. On donne 5ms.
        if (aNouveau == 1) {
            aNouveau = 2;
            
            lDeltaT = 0.005;            
        }
        
        double lDistance = aVitesseLineaire * lDeltaT;
        double lDistanceVerticale = aVitesseVerticale * lDeltaT;
        double lVariationAngulaire = aVitesseAngulaire * lDeltaT;

        double lAnglePerp = aAngleTheta + Math.PI * 0.5;
        
        Vector3d lV3DDeplacementLigne = new Vector3d(lDistance * Math.cos(aAngleTheta), lDistance * Math.sin(aAngleTheta), 0);
        Vector3d lPerpendiculaire = new Vector3d(lDistance * Math.cos(lAnglePerp), lDistance * Math.sin(lAnglePerp), 0);
        Vector3d lCiel = new Vector3d(0, 0, lDistanceVerticale);
        
        if ((aKeyState & UP_ARROW) != 0) {                                      // Avance
            aPosition.add(lV3DDeplacementLigne);
        }
        
        if ((aKeyState & DOWN_ARROW) != 0) {                                    // Recule
            aPosition.sub(lV3DDeplacementLigne);
        }
        
        if ((aKeyState & LEFT_ARROW) != 0) {                                    // Gauche
            aAngleTheta += lVariationAngulaire;
        }
        
        if ((aKeyState & RIGHT_ARROW) != 0) {                                   // Droite
            aAngleTheta -= lVariationAngulaire;
        }
        
        if ((aKeyState & KEY_UP) != 0) {                                        // Haut
            aPosition.add(lCiel);
        }
        
        if ((aKeyState & KEY_DOWN) != 0) {                                      // Bas
            aPosition.sub(lCiel);
        }

        if ((aKeyState & STRAFE_LEFT) != 0) {                                   // Strafe Gauche
            aPosition.add(lPerpendiculaire);
        }
        
        if ((aKeyState & STRAFE_RIGHT) != 0) {                                  // Strafe Droite
            aPosition.sub(lPerpendiculaire);
        }
        
        if ((aKeyState & PAGE_UP) != 0) {                                       // Regarde vers le Haut
            aAnglePhi += lVariationAngulaire;
        }
        
        if ((aKeyState & PAGE_DOWN) != 0) {                                     // Regarde vers le Bas
            aAnglePhi -= lVariationAngulaire;
        }

        // Limites les angles
        if (aAngleTheta > 2 * Math.PI) aAngleTheta -= 2 * Math.PI; else if (aAngleTheta < 0) aAngleTheta += 2 * Math.PI;
        if ((aAnglePhi > Math.PI * 0.6) || (aAngleTheta < -Math.PI * 0.6)) aAngleTheta = Math.PI * 0.6 * (aAngleTheta/Math.abs(aAngleTheta));

        System.out.println("Angle : " + Double.toString(aAngleTheta) + " - " + Long.toString(aCount));
        
        double lLongueurPlan = Math.cos(aAnglePhi);
        
        Vector3d lDirectionVisuelle = new Vector3d(lLongueurPlan * Math.cos(aAngleTheta), lLongueurPlan * Math.sin(aAngleTheta), Math.sin(aAnglePhi));
        
        
        Vector3d lNormale = new Vector3d();
        lNormale.cross(lDirectionVisuelle, lPerpendiculaire);
        
        Point3d lOeuil = new Point3d(aPosition);
        Point3d lCentre = new Point3d();
        lCentre.add(lOeuil, lDirectionVisuelle);
        
        Transform3D lTransformation = new Transform3D();
        lTransformation.lookAt(lOeuil, lCentre, lNormale);
        lTransformation.invert();
        
        aTG.setTransform(lTransformation);
    }
    
    /**
     * Processed a keyboard event.  This routine should be called
     * every time a KEY_PRESSED or KEY_RELEASED event is received.
     * @param keyEvent the AWT key event
     */
    public int processKeyEvent(KeyEvent keyEvent) {
        int keyCode = keyEvent.getKeyCode();
        int keyChar = keyEvent.getKeyChar();
        
        //System.err.println("keyCode " + keyCode + "  keyChar " + keyChar);
        
        if (keyEvent.getID() == KeyEvent.KEY_RELEASED) {
            switch (keyCode) {
                case KeyEvent.VK_UP:        aKeyState &= ~UP_ARROW;   break;
                case KeyEvent.VK_DOWN:      aKeyState &= ~DOWN_ARROW;   break;
                case KeyEvent.VK_LEFT:      aKeyState &= ~LEFT_ARROW;   break;
                case KeyEvent.VK_RIGHT:     aKeyState &= ~RIGHT_ARROW;  break;
                case KeyEvent.VK_PAGE_UP:   aKeyState &= ~PAGE_UP;    break;
                case KeyEvent.VK_PAGE_DOWN: aKeyState &= ~PAGE_DOWN;    break;
                case KeyEvent.VK_EQUALS:    aKeyState &= ~HOME_NOMINAL; break;
                default: switch(keyChar) {
                    case '-':               aKeyState &= ~MINUS_SIGN;   break;
                    case '+':               aKeyState &= ~PLUS_SIGN;    break;

                    case 'A': case 'a':     aKeyState &= ~KEY_UP;       break;
                    case 'Z': case 'z':     aKeyState &= ~KEY_DOWN;     break;

                    case 'S': case 's':     aKeyState &= ~STRAFE_LEFT;  break;
                    case 'D': case 'd':     aKeyState &= ~STRAFE_RIGHT; break;

                }
            }
        } else if (keyEvent.getID() == KeyEvent.KEY_PRESSED) {
            switch (keyCode) {
                case KeyEvent.VK_UP:        aKeyState |=  UP_ARROW;   break;
                case KeyEvent.VK_DOWN:      aKeyState |=  DOWN_ARROW;   break;
                case KeyEvent.VK_LEFT:      aKeyState |=  LEFT_ARROW;   break;
                case KeyEvent.VK_RIGHT:     aKeyState |=  RIGHT_ARROW;  break;
                case KeyEvent.VK_PAGE_UP:   aKeyState |=  PAGE_UP;      break;
                case KeyEvent.VK_PAGE_DOWN: aKeyState |=  PAGE_DOWN;    break;
                case KeyEvent.VK_EQUALS:    aKeyState |=  HOME_NOMINAL; break;
                default: switch(keyChar) {
                    case '-':               aKeyState |=  MINUS_SIGN;   break;
                    case '+':               aKeyState |=  PLUS_SIGN;    break;

                    case 'A': case 'a':     aKeyState |=  KEY_UP;       break;
                    case 'Z': case 'z':     aKeyState |=  KEY_DOWN;     break;
                    
                    case 'S': case 's':     aKeyState |=  STRAFE_LEFT;  break;
                    case 'D': case 'd':     aKeyState |=  STRAFE_RIGHT; break;
                }
            }
        }
        
        if (keyEvent.isShiftDown())
            aModifierKeyState |=  SHIFT;
        else
            aModifierKeyState &= ~SHIFT;
        
        if (keyEvent.isMetaDown())
            aModifierKeyState |=  META;
        else
            aModifierKeyState &= ~META;
        
        if (keyEvent.isAltDown())
            aModifierKeyState |=  ALT;
        else
            aModifierKeyState &= ~ALT;

        if (aKeyState == 0) {
            aNouveau = 0;
        } else {
            if (aNouveau == 0) {
                aNouveau = 1;
            }
        }
            
        
        return aKeyState;
    }
}



