package de.ucb.mi.jhorde3d.samples;


import de.ucb.mi.jhorde3d.*;
import java.awt.event.*;

import de.ucb.mi.jhorde3d.jinvoke.*;
import de.ucb.mi.jhorde3d.datastructures.*;
import de.ucb.mi.jhorde3d.Horde3DFrame.*;

public class Chicago extends Horde3DFrame
        implements KeyListener,
        MouseListener,
        MouseMotionListener,
        Runnable {

  private int fontMatRes;
  private int logoMatRes;
  private int forwardPipeRes;
  private int deferredPipeRes;
  private int particleSysRes;
  private int panelMatRes;
  private long numFrames = 0;
  float curFPS;
  private float x;
  private int knight;
  private int particleSys;
  private float y;
  private float z;
  private float rx;
  private float ry;
  private float weight;
  private float fps = 30.0f;
  private boolean active = true;
  static float t = 0.0f;

  // standard constructor
  public Chicago(String title) {
    super(title);
    this.addKeyListener(this);
    this.addMouseListener(this);
    this.addMouseMotionListener(this);
    x = 15;
    y = 3;
    z = 20;
    rx = -10;
    ry = 60;
    velocity = 10.0f;
    curFPS = 30;
    weight = 1.0f;
  }

  // KeyListener interface
  public void keyTyped(KeyEvent e) {
    // do nothing
    }

  public void keyReleased(KeyEvent e) {
    // do nothing
    }

  public void keyPressed(KeyEvent e) {
    float curVel = (float) this.velocity * (30.0f / curFPS);
    switch (e.getKeyCode()) {
      case KeyEvent.VK_W:
        // Move forward
        x -= Math.sin(degToRad(ry)) * Math.cos(-degToRad(rx)) * curVel;
        y -= Math.sin(-degToRad(rx)) * curVel;
        z -= Math.cos(degToRad(ry)) * Math.cos(-degToRad(rx)) * curVel;
        break;
      case KeyEvent.VK_S:
        // Move backward
        x += Math.sin(degToRad(ry)) * Math.cos(-degToRad(rx)) * curVel;
        y += Math.sin(-degToRad(rx)) * curVel;
        z += Math.cos(degToRad(ry)) * Math.cos(-degToRad(rx)) * curVel;
        break;
      case KeyEvent.VK_A:
        // Strafe left
        x += Math.sin(degToRad(ry - 90)) * curVel;
        z += Math.cos(degToRad(ry - 90)) * curVel;
        break;
      case KeyEvent.VK_D:
        // Strafe right
        x += Math.sin(degToRad(ry + 90)) * curVel;
        z += Math.cos(degToRad(ry + 90)) * curVel;
        break;
      default:
        break;
    }
  }

  float degToRad(float f) {
    return f * ((float) Math.PI / 180.0f);
  }

  // MouseListener interface
  public void mouseClicked(MouseEvent e) {
  }

  public void mouseEntered(MouseEvent e) {
  }

  public void mouseExited(MouseEvent e) {
  }

  public void mousePressed(MouseEvent e) {
  }

  public void mouseReleased(MouseEvent e) {
  }

  // MouseMotionListener interface
  int previousX = -1;
  int previousY = -1;
  int currentX = -1;
  int currentY = -1;
  float dX = 0.0f;
  float dY = 0.0f;

  public void mouseMoved(MouseEvent e) {
    previousX = currentX;
    previousY = currentY;
    currentX = e.getX();
    currentY = e.getY();
    if (previousX >= 0) {
      dX = (currentX - previousX);
    }
    if (previousY >= 0) {
      dY = -(currentY - previousY);
    }
    // Look left/right
    ry -= dX / 100.0f * 30.0f;
    // Look up/down but only in a limited range
    rx += dY / 100.0f * 30.0f;
    if (rx > 90) {
      rx = 90;
    }
    if (rx < -90) {
      rx = -90;
    }
  }

  public void setCamera(float p_PosX, float p_PosY, float p_PosZ,
          float p_ViewX, float p_ViewY) {
      x = p_PosX;
      y = p_PosY;
      z = p_PosZ;
      rx = p_ViewX;
      ry = p_ViewY;
  }

  public void mouseDragged(MouseEvent e) {
  }

  /// application code
  public boolean init() {
    System.out.println(Horde3D.getVersionString());
    // initialize engine
    if (!Horde3D.init()) {
      System.out.println("not initialized");
      return false;
    }
    // Set (relative) paths for resources
    Horde3D.setResourcePath(ResourceTypes.SceneGraph, "models");
    Horde3D.setResourcePath(ResourceTypes.Geometry, "models");
    Horde3D.setResourcePath(ResourceTypes.Animation, "models");
    Horde3D.setResourcePath(ResourceTypes.Material, "materials");
    Horde3D.setResourcePath(ResourceTypes.Code, "shaders");
    Horde3D.setResourcePath(ResourceTypes.Shader, "shaders");
    Horde3D.setResourcePath(ResourceTypes.Texture2D, "textures");
    Horde3D.setResourcePath(ResourceTypes.TextureCube, "textures");
    Horde3D.setResourcePath(ResourceTypes.Effect, "effects");
    //Horde3D.setResourcePath( ResourceTypes.Pipeline, "pipeline" );
    // Load pipeline configuration
    if (!Horde3D.loadPipelineConfig("content\\pipeline_Knight.xml")) {
      return false; //Welce Pipeline muss hier geladen werden???
    }
    // Set options
    Horde3D.setOption(EngineOptions.LoadTextures, 1);
    Horde3D.setOption(EngineOptions.TexCompression, 0);
    Horde3D.setOption(EngineOptions.AnisotropyFactor, 4);
    Horde3D.setOption(EngineOptions.ShadowMapSize, 2048);
    Horde3D.setOption(EngineOptions.FastAnimation, 1);
    // Add resources
    //Pipelines Diese Pipelines werden im Knight ebenfalls nicht geladen !!!
    //forwardPipeRes = Horde3D.addResource(ResourceTypes.Pipeline, "forward.pipeline.xml", 0 );
    //deferredPipeRes = Horde3D.addResource( ResourceTypes.Pipeline, "deferred.pipeline.xml", 0 );
    //Font
    fontMatRes = Horde3D.addResource( ResourceTypes.Material, "font.material.xml", 0 );
    //Logo
    logoMatRes = Horde3D.addResource( ResourceTypes.Material, "logo.material.xml", 0 );
    panelMatRes = Horde3D.addResource( ResourceTypes.Material, "panel.material.xml", 0 );
    // Shader for deferred shading
    int lightMatRes = Horde3D.addResource( ResourceTypes.Material, "light.material.xml", 0 );
    // Environment
    int envRes = Horde3D.addResource( ResourceTypes.SceneGraph, "platform.scene.xml", 0 );
    int skyBoxRes = Horde3D.addResource(ResourceTypes.SceneGraph, "skybox.scene.xml", 0 );
    particleSysRes = Horde3D.addResource(ResourceTypes.SceneGraph, "particleSys1.scene.xml", 0);
    // Load resources
    Horde3DUtils.loadResourcesFromDisk("content");
    // Add scene nodes
    //Add camera  Wird im Knight ebenfalls nicht verwendet !!!
    //int cam = Horde3D.addCameraNode( RootNode, "Camera");
    //Horde3D.setNodeParam(cam, CameraNodeParams.OcclusionCulling, 1 );
    // Add environment
    int env = Horde3D.addNodes(RootNode, envRes);

    // Bei KNIGHT kommt jetzt: Add Knight und Attach particle system to hand joint

    Horde3D.setNodeTransform( env, 0, 0, 0, 0, 0, 0, 0.23f, 0.23f, 0.23f );
    // Add skybox
    int sky = Horde3D.addNodes(RootNode, skyBoxRes );
    Horde3D.setNodeTransform( sky, 0, 0, 0, 0, 0, 0, 210, 50, 210 );
    // Add light source
    int light = Horde3D.addLightNode(RootNode, "Light1", lightMatRes, "LIGHTING", "SHADOWMAP");
    Horde3D.setNodeTransform(light, 0, 20, 50, -30, 0, 0, 1, 1, 1);
    Horde3D.setLightParam(light, LightNodeParams.Radius, 30);
    Horde3D.setLightParam(light, LightNodeParams.FOV, 90);
    Horde3D.setLightParam( light, LightNodeParams.ShadowMapCount, 3 );
    Horde3D.setLightParam( light, LightNodeParams.ShadowSplitLambda, 0.9f );
    Horde3D.setLightParam(light, LightNodeParams.ShadowMapBias, 2.97f);
    //Horde3D.setLightParam(light, LightNodeParams.ShadowMapEnabled, 1);
    Horde3D.setLightParam(light, LightNodeParams.Col_R, 2.0f);
    Horde3D.setLightParam(light, LightNodeParams.Col_G, 1.7f);
    Horde3D.setLightParam(light, LightNodeParams.Col_B, 1.7f);

    // Customize post processing effects BEI KNIGHT vorhanden
    int matRes = Horde3D.findResource(ResourceTypes.Material, "postprocessing.material.xml");
    Horde3D.setMaterialUniform(matRes, "radblurParams", 0.007f, 0, 0, 0);

    crowdSim crowd_Sim = new crowdSim();
    //crowd_Sim = new crowdSim(contentDir);
    crowd_Sim.init();

    return true;
  }

  public void mainLoop(float fps, boolean freeze, boolean showFPS) {
    curFPS = fps;
    if (!freeze) {
      t += 1.0f / curFPS;
      // Do animation blending
      Horde3D.setModelAnimParams(knight, 0, t * 24.0f, weight);
      Horde3D.setModelAnimParams(knight, 1, t * 24.0f, 1.0f - weight);
      // Animate particle system
      Horde3D.advanceEmitterTime(particleSys, 1.0f / curFPS);
      //crowdSim.update(curFPS);
    }
    // Set camera parameters
    Horde3D.setNodeTransform(PrimeTimeCam,  x, y, z, rx, ry, 0, 1, 1, 1);
    if (showFPS) {
      String text = "FPS: " + fps;
      Horde3DUtils.showText(text, 0, 0.95f, 0.03f, 0, fontMatRes);
      String text2 = "Weight: " + weight;
      Horde3DUtils.showText(text2, 0, 0.91f, 0.03f, 0, fontMatRes);
    }

    // Show logo
    Horde3D.showOverlay(0.75f, 0, 0, 0, 1, 0, 1, 0,
            1, 0.2f, 1, 1, 0.75f, 0.2f, 0, 1,
            7, logoMatRes);
    // Render scene
    Horde3D.render();
  }

  public void release() {
    //delete crowdSim;
    //crowdSim = 0x0;
    Horde3D.release();
  }

  // Runnable interface
  public void run() {
    initOpenGL();
    init();
    initPerformanceCounter();
    float previousCounterTime = 0;
    while (!exitApp) {
      if (active & !exitApp) {
        this.mainLoop(fps, freeze, showFPS);
        Horde3DUtils.swapBuffers();
        numFrames += 1;
        if (numFrames >= 3) {
          float counterTime = (float) getCounterTime();
          if (previousCounterTime > 0) {
            fps = ((float) numFrames * 1000.0f) / (counterTime - previousCounterTime); // hier in millis, im orig in sec
          }
          numFrames = 0;
          previousCounterTime = counterTime;
        // initPerformanceCounter();
        }
      }
    }
    releaseOpenGL();
  }

  public static void main(String[] args) {
    Chicago chicago = new Chicago("JHorde3D Example: Chicago");
    Thread thread = new Thread(chicago);
    thread.start();
  }
}






