/*
 * Teapot4Canvas for Java 3D-enabled mobile phones supporting M3G (JSR-184) API.
 * Created by Mobilefish.com (https://www.mobilefish.com)
 *
 * File: Teapot4Canvas.java
 * Needs: Teapot4MIDlet.java
 * Resources: teapot4_background.m3g
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * If you have any questions, please contact contact@mobilefish.com
 */


package com.mobilefish.m3g;

import javax.microedition.midlet.MIDletStateChangeException;
import javax.microedition.m3g.*;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.Graphics;
import com.mobilefish.m3g.Teapot4MIDlet;

// Teapot4Canvas creates the scenegraph, sets up the object
// and draws the scene.
public class Teapot4Canvas extends GameCanvas implements Runnable {

    // Teapot UserId.
    static final int TEAPOT_ID = 16;
    // Choose Camera ID 25, 26 or 27
    static final int CAMERA_ID = 27;

    // The midlet
    Teapot4MIDlet midlet;

    // Contains the entire scene graph
    World world = null;

    // The camera
    private Camera camera = null;

    // Thread control
    boolean running = false;
    boolean done = true;

    // Global Graphics3D object
    Graphics3D g3d = null;

    // Rendering hints
    public static final int STRONG_RENDERING_HINTS = Graphics3D.ANTIALIAS | Graphics3D.TRUE_COLOR | Graphics3D.DITHER;
    public static final int WEAK_RENDERING_HINTS = 0;
    public static int RENDERING_HINTS = STRONG_RENDERING_HINTS;

    // Viewport dimensions
    int viewport_x;
    int viewport_y;
    int viewport_width;
    int viewport_height;

    // Key array
    boolean[] key = new boolean[5];

    // Key constants
    public static final int FIRE = 0;
    public static final int UP = 1;
    public static final int DOWN = 2;
    public static final int LEFT = 3;
    public static final int RIGHT = 4;

    // Camera rotation
    float camRot = 0.0f;


    /**
     * Construct a new canvas
     */
    Teapot4Canvas(Teapot4MIDlet mid) {
        super(true);

        midlet = mid;

        // Disable fullscreen canvas.
        // The correct aspect ratio must be maintained.
        //setFullScreenMode(true);

        // Load World
        loadWorld();

        // Load Camera
        loadCamera(CAMERA_ID);

        // Disable load light. The scene has the correct light.
        //loadLight();

        // Set viewport
        setupAspectRatio();

    }

    /**
     * Draw the objects
     */
    private void draw(Graphics g) {

        try {

            if (g.getClipWidth() != viewport_width
                    || g.getClipHeight() != viewport_height
                    || g.getClipX() != viewport_x || g.getClipY() != viewport_y) {
                g.setColor(0,0,0);
                g.fillRect(0, 0, getWidth(), getHeight());
            }

            // Move the camera around
            moveCamera();

            // Get the Graphics3D context
            g3d = Graphics3D.getInstance();

            // Bind the graphics object. Use rendering hints.
            g3d.bindTarget(g, true, RENDERING_HINTS);

            g3d.setViewport(viewport_x, viewport_y, viewport_width, viewport_height);

            // Render the world.
            g3d.render(world);
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            // release
            g3d.releaseTarget();
        }
    }


    /**
     * Processes keys
     */
    protected void process() {
        int keys = getKeyStates();

        if((keys & GameCanvas.FIRE_PRESSED) != 0)
            key[FIRE] = true;
        else
            key[FIRE] = false;

        if((keys & GameCanvas.UP_PRESSED) != 0)
            key[UP] = true;
        else
            key[UP] = false;

        if((keys & GameCanvas.DOWN_PRESSED) != 0)
            key[DOWN] = true;
        else
            key[DOWN] = false;

        if((keys & GameCanvas.LEFT_PRESSED) != 0)
            key[LEFT] = true;
        else
            key[LEFT] = false;

        if((keys & GameCanvas.RIGHT_PRESSED) != 0)
            key[RIGHT] = true;
        else
            key[RIGHT] = false;
    }

    /**
     * Init.
     */
    void init() {
    }

    /**
     * Cleanup and destroy.
     */
    void destroy() {
    }


    /**
     * Load World from the m3g file
     */
    private void loadWorld(){
        try {
            String filename = "/teapot4_background.m3g";

            world = (World) Loader.load(filename)[0];

            if (world == null) {
                Object3D[] buffer = Loader.load(filename);

                for (int i = 0; i < buffer.length; i++ ) {
                    if (buffer[i] instanceof World) {
                        world = (World) buffer[i];
                        break;
                    }
                }
                // Clean buffer;
                buffer = null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * Load a camera
     */
    private void loadCamera(int camId){
        try {

            // Load a specific camera in the world
            camera = (Camera) world.find(CAMERA_ID);

            // Add camera to the world
            world.setActiveCamera(camera);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Load a light
     */
    private void loadLight(){
        try {

            Light light = new Light();

            // Set light to Ambient
            light.setMode(Light.AMBIENT);

            // Set light intensity
            light.setIntensity(1.0f);

            // Add light to world
            world.addChild(light);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Start the thread.
     */
    public void start(){
        try {
            // Create thread
            Thread t = new Thread(this);
            running = true;
            done = false;

            // Start the thread
            t.start();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * Infinite loop until user stops.
     */
    public void run() {
        while(running) {
            try {
                // Call the process method.
                process();

                // Draw objects
                draw(getGraphics());
                flushGraphics();

                // Sleep
                try {
                    Thread.sleep(30);
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
            catch(Exception e) {
                e.printStackTrace();
            }
        }

        // Notify completion
        done = true;
    }


    /**
     * Make sure that the content is rendered with the correct aspect ratio.
     */
    void setupAspectRatio() {
        viewport_x = 0;
        viewport_y = 0;
        viewport_width = getWidth();
        viewport_height = getHeight();

        float[] params = new float[4];
        int type = camera.getProjection(params);
        if (type != Camera.GENERIC) {
            // Calculate the window aspect ratio
            float aspectratio = viewport_width / viewport_height;

            if (aspectratio < params[1]) {
                float height = viewport_width / params[1];
                viewport_height = (int) height;
                viewport_y = (getHeight() - viewport_height) / 2;
            } else {
                float width = viewport_height * params[1];
                viewport_width = (int) width;
                viewport_x = (getWidth() - viewport_width) / 2;
            }
        }
    }

    /**
     * Move camera.
     */
    private void moveCamera() {

        if(key[RIGHT]) {
            // Rotate right
            camRot += 5.0f;
        } else if(key[LEFT]) {
            // Rotate left
            camRot -= 5.0f;
        }

        // Set the orientation
        camera.setOrientation(camRot, 0.0f, 0.0f, 1.0f);

        if(key[UP]) {
            // Move forward
            camera.translate(0.0f, 0.0f, -40.0f);
        } else if(key[DOWN]) {
            // Move backward
            camera.translate(0.0f, 0.0f, 40.0f);
        }

        // Quit if Fire key is pressed
        if(key[FIRE]) {
            try {
                midlet.destroyApp(true);
            } catch(MIDletStateChangeException mse) {
                mse.printStackTrace();
            }
        }
    }


    /**
     * Check if the thread is running
     */
    public boolean isRunning() { return running; }

    /**
     * Check if the thread is finished
     */
    public boolean isDone() { return done; }

}
