package de.ucb.mi.jhorde3d.samples;

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

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

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

  private int fontMatRes;
  private int logoMatRes;
  private int knight;
  private int particleSys;
  private long numFrames = 0;
  private float curFPS;
  private float x;
  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;

  private CameraControlFrame cameraControlFrame;
  private boolean cameraControlActive = false;


  // getter
    public float get_X() {
        return x;
    } 

    public float get_Y() {
        return y;
    }
 
    public float get_Z() {
        return z;
    }

     public float getRx() {
        return rx;
    }

    public float getRy() {
        return ry;
    }

  // standard constructor
  public Knight(String title) {
    super(title);
    this.addKeyListener(this);
    this.addMouseListener(this);
    this.addMouseMotionListener(this);
    x = 0;
    y = 3;
    z = -18;
    rx = 7;
    ry = 180;
    velocity = 0.3f;
    curFPS = 30.0f;
    weight = 1.0f;
    cameraControlFrame = new CameraControlFrame(this);
  }

  // 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_C:
        cameraControlActive = true;        
        cameraControlFrame.requestFocus();
        cameraControlFrame.setVisible(true);
        break;
      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;
      case KeyEvent.VK_1:
        weight += 2 / curFPS;
        if (weight > 1) {
          weight = 1;
        }
        break;
      case KeyEvent.VK_2:
        weight -= 2 / curFPS;
        if (weight < 0) {
          weight = 0;
        }
        break;
      default:
        break;
    }
  }

  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 setCameraControlActive(boolean p_Active) {
      cameraControlActive = p_Active;
  }

  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) {
      if (!cameraControlActive) {
        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 mouseDragged(MouseEvent e) {
  }

  // 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();
        }
      }
    }
    // Exit JFrame for Camera-Control
    cameraControlFrame.dispose();
    releaseOpenGL();
  }


  /// application code
  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");
    // Load pipeline configuration
    if (!Horde3D.loadPipelineConfig("content\\pipeline_Knight.xml")) {
      return false;
    }
    // Set options
    Horde3D.setOption(EngineOptions.LoadTextures, 1);
    Horde3D.setOption(EngineOptions.TexCompression, 0);
    Horde3D.setOption(EngineOptions.FastAnimation, 0);
    Horde3D.setOption(EngineOptions.AnisotropyFactor, 8);
    Horde3D.setOption(EngineOptions.ShadowMapSize, 2048);
    // Add Resources
    // Font
    fontMatRes = Horde3D.addResource(ResourceTypes.Material, "font.material.xml", 0);
    logoMatRes = Horde3D.addResource(ResourceTypes.Material, "logo.material.xml", 0);
    // Environment
    int envRes = Horde3D.addResource(ResourceTypes.SceneGraph, "scene.scene.xml", 0);
    int knightRes = Horde3D.addResource(ResourceTypes.SceneGraph, "knight.scene.xml", 0);
    int knightAnim1Res = Horde3D.addResource(ResourceTypes.Animation, "knight_order.anim", 0);
    int knightAnim2Res = Horde3D.addResource(ResourceTypes.Animation, "knight_attack.anim", 0);
    // Particle system
    int particleSysRes = Horde3D.addResource(ResourceTypes.SceneGraph, "particleSys1.scene.xml", 0);
    // Load resources
    Horde3DUtils.loadResourcesFromDisk("content");
    // Add scene nodes
    // Add environment
    Horde3D.addNodes(RootNode, envRes);
    // Add knight
    knight = Horde3D.addNodes(RootNode, knightRes);
    Horde3D.setNodeTransform(knight, 0, 0, 0, 0, 0, 0, 0.1f, 0.1f, 0.1f);
    Horde3D.setupModelAnimStage(knight, 0, knightAnim1Res, "", false);
    Horde3D.setupModelAnimStage(knight, 1, knightAnim2Res, "", false);

    // Attach particle system to hand joint
    int hand = Horde3D.getNodeChild(knight, "Bip01_R_Hand", 0, true);
    particleSys = Horde3D.addNodes(hand, particleSysRes);
    Horde3D.setNodeTransform(particleSys, 0, 40, 0, 90, 0, 0, 1, 1, 1);

    // Add light source
    int light = Horde3D.addLightNode(RootNode, "Light1", 0, "LIGHTING", "SHADOWMAP");
    Horde3D.setNodeTransform(light, 0, 1, -15, 30, 180, 0, 1, 1, 1);
    Horde3D.setLightParam(light, LightNodeParams.Radius, 30);
    Horde3D.setLightParam(light, LightNodeParams.FOV, 90);
    // Horde3D.setLightParam(light, LightNodeParams.ShadowMapEnabled, 1);
    Horde3D.setLightParam(light, LightNodeParams.ShadowMapBias, 0.97f);
    Horde3D.setLightParam(light, LightNodeParams.Col_R, 1.0f);
    Horde3D.setLightParam(light, LightNodeParams.Col_G, 0.7f);
    Horde3D.setLightParam(light, LightNodeParams.Col_B, 0.7f);

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

  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);
    }
    // Set camera parameters
    Horde3D.setNodeTransform(PrimeTimeCam,  x, y, z, rx, ry, 0, 1, 1, 1);
    if (showFPS) {
      // Show text
      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();
  }

  void release() {
    Horde3D.release();
  }

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