package gldemo;

import org.lwjgl.opengl.*;
import org.lwjgl.util.glu.*;
import glapp.*;
import mpe.client.*;

/**
 * Draw a simple 3D scene rendered over two windows.  Demonstrate use of the
 * MPE client and server to synchronize rendering and mouse events
 * across multiple applications, possibly running on different
 * computers (see mostpixelsever.com).
 * <P>
 * requires the MPE client library with the UDPClientGL class (for LWJGL) <BR>
 * requires the MPE server.  Run it on the local machine to test: <BR>
 * <PRE>
 *        java -jar mpeServer.jar
 * </PRE>
 * requires an INI file for each client window (defines ID, location and size of window):
 * <PRE>
 * 		id=0;
 * 		server=localhost;
 * 		port=9002;
 * 		localLocation=0,0;
 * 		localScreenSize=640,480;
 * 		masterDimensions=1280,480;
 * </PRE>
 * This demo creates a client and configures it from an INI file.  The client has an
 * ID assigned to it.  To test two windows, run the app with "mpe0.ini", then change
 * to "mpe1.ini" and run a second instance of the app (see UDPClientGL() in main())
 * <P>
 * napier at potatoland dot org
 */
public class GLApp_DemoGiantScreen1 extends GLApp {
    // client to synchronize framerate with MPE server
    UDPClientGL client;

    // Handles for textures
    int cubeTextureHandle = 0;
    int sphereTextureHandle = 0;
    int groundTextureHandle = 0;
    // Light position: if last value is 0, then this describes light direction.  If 1, then light position.
    float lightPosition[]= { -5f, 4f, 3f, 1f };
    // World coordintates at current mouse position
    float[] worldPos = {0f, 0f, 0f};
    // World coordinates of sphere
    float[] spherePos = {0f, 0f, 0f};
    // Rotation of sphere
    float rotation=0f;

    /**
     * Run main loop of application.  Handle mouse and keyboard input.
     */
    public static void main(String args[]) {
        GLApp_DemoGiantScreen1 demo = new GLApp_DemoGiantScreen1();

        //=========================================================
        // Create the MPE client
        //=========================================================

        // Make a new Client from the INI file.
        demo.client = new UDPClientGL("mpe1.ini",demo);

        // The size of window is determined by the client's local width and height
        demo.displayWidth = demo.client.getLWidth();
        demo.displayHeight = demo.client.getLHeight();
        demo.displayFrequency = 60;
        demo.VSyncEnabled = false;
        demo.run();
    }

	/**
	 * frameEvent() is triggered by the client whenever a new frame should be rendered
	 * client.drawFrame() will return true after a frameEvent has been triggered
	 * up till client.done() is called.  frameEvent() will also be called when
	 * the client receives a message broadcast from other screens.
	 */
	public void frameEvent(UDPClient c){
		if (c.messageAvailable()) {
			String[] msg = c.getDataMessage();
			String[] val = msg[0].split(",");
			if (val[0].equals("S")) {  // sphere position
				spherePos[0] = Float.parseFloat(val[1]);
				spherePos[1] = Float.parseFloat(val[2]);
				spherePos[2] = Float.parseFloat(val[3]);
				//System.out.println("GOT MESSAGE: " + msg[0]);
			}
			else {                     // cube position
				worldPos[0] = Float.parseFloat(val[1]);
				worldPos[1] = Float.parseFloat(val[2]);
				worldPos[2] = Float.parseFloat(val[3]);
			}
		}
	}

    /**
     * Initialize the scene.  Called by GLApp.run()
     */
    public void setup()
    {
        // Create textures
        sphereTextureHandle = makeTexture("images/eye.jpg");
        cubeTextureHandle = makeTexture("images/marble.jpg");
        groundTextureHandle = makeTexture("images/mahog_texture.jpg");  //grass_1_512.jpg");

        // Create a point light (white)
        setLight( GL11.GL_LIGHT1,
        		new float[] { 1.0f, 1.0f, 1.0f, 1.0f },   // diffuse color
        		new float[] { .35f, 0.3f, 0.3f, 1.0f },   // ambient
        		new float[] { 1.0f, 1.0f, 1.0f, 1.0f },   // specular
        		lightPosition );                          // position

        // Create a directional light (dark red, to simulate reflection off wood surface)
        setLight( GL11.GL_LIGHT2,
        		new float[] { 0.3f, 0.15f, 0.1f, 1.0f },   // diffuse color
        		new float[] { 0.0f, 0.0f, 0.0f, 1.0f },   // ambient
        		new float[] { 0.0f, 0.0f, 0.0f, 1.0f },   // specular
        		new float[] { 0.0f, -10f, 0.0f, 0f } );   // direction (pointing up)

        // material color
        setMaterial(
        		new float[] { 0.9f, 0.9f, 0.9f, 1.0f },   // diffuse color
        		new float[] { 0.9f, 0.9f, 0.9f, 1.0f },   // ambient
        		new float[] { 1.0f, 1.0f, 1.0f, 1.0f },   // specular
        		new float[] { 0.0f, 0.0f, 0.0f, 0.0f },   // emissive
        		120);        // shininess

        // no overall scene lighting
        setAmbientLight(new float[] { 0.0f, 0.0f, 0.0f, 0.0f });

        // enable lighting and texture rendering
        GL11.glEnable(GL11.GL_LIGHTING);
        GL11.glEnable(GL11.GL_TEXTURE_2D);

        // select model view for subsequent transforms
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glLoadIdentity();

        //=========================================================
        // Setup the MPE client
        //=========================================================

        // set full scene frustum
        // This defines the size and shape of the entire scene that will be split
        // over many screens.  The aspect ratio should match the ratio of the master
        // screen dimensions in the client INI file. (masterDimensions=1280,480;)
        client.setPerspective(30f, 1280f/480f, 1f, 100f);

        // Start the client when we're ready to render!
        client.start();
    }

    /**
     * Render one frame, only when client says so.
     */
    public void draw() {
        // don't draw anything until client says we can
        if (client.drawFrame()) {

    		// adjust the projection to show one portion of entire scene
    		client.placeScreen();

    		rotation += .5f;

    		// clear depth buffer and color
    		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

    		// select model view for subsequent transforms
    		GL11.glMatrixMode(GL11.GL_MODELVIEW);
    		GL11.glLoadIdentity();

    		// set the viewpoint
    		GLU.gluLookAt(5f, 0f, 15f, // where is the eye
    				0f, 0f, 0f,    // what point are we looking at
    				0f, 1f, 0f);   // which way is up

    		setLightPosition( GL11.GL_LIGHT1, lightPosition );

    		// draw the ground plane
    		GL11.glPushMatrix();
    		{
    			GL11.glTranslatef(0f, -2f, 0f); // down a bit
    			GL11.glScalef(7f, .1f, 7f);
    			GL11.glBindTexture(GL11.GL_TEXTURE_2D, groundTextureHandle);
    			renderCube();
    		}
    		GL11.glPopMatrix();

    		// draw sphere at center
    		GL11.glPushMatrix();
    		{
    			GL11.glTranslatef(spherePos[0], spherePos[1], spherePos[2]);
    			GL11.glRotatef(-90, 1, 0, 0);       // rotate around X axis
    			GL11.glRotatef(rotation, 0, 0, 1);  // rotate around Y axis
    			GL11.glBindTexture(GL11.GL_TEXTURE_2D, sphereTextureHandle); // activate texture
    			renderSphere();                     // draw the sphere
    		}
    		GL11.glPopMatrix();

    		// Draw cube at cursor position
    		GL11.glPushMatrix();
    		{
    			GL11.glTranslatef(worldPos[0], worldPos[1], worldPos[2]); // move to mouse pos
    			GL11.glScalef(.5f, .5f, .5f);                             // make it smaller
    			GL11.glBindTexture(GL11.GL_TEXTURE_2D, cubeTextureHandle); // activate texture
    			renderCube();
    		}
    		GL11.glPopMatrix();

    		// Draw sphere at light position
    		GL11.glPushMatrix();
    		{
    			GL11.glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
    			GL11.glScalef(.2f, .2f, .2f);                       // make it tiny
    			GL11.glBindTexture(GL11.GL_TEXTURE_2D, cubeTextureHandle);
    			renderSphere();
    		}
    		GL11.glPopMatrix();

    		// Alert the server when frame is done
    		client.done();
        }

        // If we're waiting, show a message
        if (client.allConnected == false) {
            print(10, 40, 0, "Waiting for other clients to connect.  My ID=" + client.id);
        }
    }

    /**
     * Convert the mouse position into world coordinates.
     */
    public void mouseMove(int x, int y) {
        worldPos = getWorldCoordsAtScreen(x,y);
    	// Broadcast cube position to other screens
    	// Do not include a ":" in your message
       	client.broadcast("C," + worldPos[0] + "," + worldPos[1] + "," + worldPos[2]);
    }

    /**
     * Set sphere position to current mouse x,y
     */
    public void mouseDown(int x, int y) {
        spherePos = getWorldCoordsAtScreen(x,y);
    	// Broadcast sphere position to other screens
    	// Do not include a ":" in your message
       	client.broadcast("S," + spherePos[0] + "," + spherePos[1] + "," + spherePos[2]);
    }


    public void mouseUp(int x, int y) {
    }


    public void keyDown(int keycode) {
    }


    public void keyUp(int keycode) {
    }

}