/*-
 * Copyright (c) 1999 Qube Software, Ltd.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of the source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Any redistribution solely in binary form must conspicuously
 *    reproduce the following disclaimer in documentation provided with the
 *    binary redistribution.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'', WITHOUT ANY WARRANTIES, EXPRESS
 * OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  LICENSOR SHALL
 * NOT BE LIABLE FOR ANY LOSS OR DAMAGES RESULTING FROM THE USE OF THIS
 * SOFTWARE, EITHER ALONE OR IN COMBINATION WITH ANY OTHER SOFTWARE.
 *
 *	$Id: teapot.cpp,v 1.40 2000/05/30 19:37:15 n_hibma Exp $
 */

#include <math.h>		// Define for sine and cosine
#include <stdio.h>

#include "patch.h"
#include "event.h"
#include "image.h"

//unsigned long texture;

using namespace Draw;
using namespace Image;

Patch::World* myWorld;
Patch::Model* TeaPot1;

Patch::World* testw;
Patch::Instance* testi;
Patch::Model* testm;
Patch::Patch* testp;

void GenModelPov(Patch::Model* model, int noShaders);

void makePatch(Patch::Model* model, Shader* shader);

Factory* factory;
Renderer* renderer;
Renderer* renderer2;
Buffer* buffer;
Buffer* buffer2;
LightManager* lights;
Material mat;
Texture* text;
Texture* text2;
Texture* text3;
Shader* shader[3];
Shader* shader2[3];
Maths::Matrix4f rotation;

float start = -200.0f;
float rot = 3.0f;

static void setViewport(Buffer* buffer)
{
    buffer->setFrustum(Maths::Box2f(-0.5f, -0.5f, 0.5f, 0.5f), 1.0f, 1000.0f);
}

static void setCamera(Buffer* buffer)
{
    Maths::Matrix4f m;
    m.setIdentity();
    m.position(Maths::Vec3f(0.0f, 0.0f, -500.0f));
    buffer->setMatrix(Buffer::CameraMatrix, m);
}

static void setViewport2(Buffer* buffer)
{
    buffer->setFrustum(Maths::Box2f(-0.5f, -0.5f, 0.5f, 0.5f), 1.0f, 1000.0f);
}

static void setCamera2(Buffer* buffer)
{
    Maths::Matrix4f m;
    m.setIdentity();
    m.position(Maths::Vec3f(0.0f, 0.0f, -500.0f));
    buffer->setMatrix(Buffer::CameraMatrix, m);
}

static Texture*
loadBitmap(Renderer* renderer, const char* name)
{
    bool imageOk = true;
    Texture* texture;

    unsigned int width;
    unsigned int height;
    unsigned int maxdim;
    Format::Type format = Format::RGB888;
    
    Pixmap* image = Pixmap::loadBMP(name, format, 8);
    if (!image)
	return 0;

    width = image->extent().x;
    height = image->extent().y;

    maxdim = width;
    if (height > width)
	maxdim = height;

    if (width != height)
	imageOk = false;

    unsigned int levels = 0;
    for (levels = 0; (1U << levels) < maxdim; levels++);

    if (maxdim != (1U << levels))
	imageOk = false;

    if (!imageOk) {
	/*
	 * Reformat the image to a square power of two image.
	 */
	if (levels > 8) levels = 8;
	maxdim = 1U << levels;
	Pixmap* scale = new Pixmap(maxdim, maxdim, image->extent().z,
				   format, 8);
	
	copy(*image, Box2ui(0, 0, width, height),
	     *scale, Box2ui(0, 0, maxdim, maxdim));
	width = height = maxdim;
	delete image;
	image = scale;
    }

    texture = renderer->createTexture(levels + 1, image->extent().z);
	
    for (unsigned int i = levels; i != 0; i--) {
	texture->loadLevel(i, (unsigned char*) image->address(0, 0));
	    
	if (i > 0) {
	    Pixmap* scale = new Pixmap(width / 2,
				       height / 2,
				       image->extent().z,
				       format,
				       8);
	    copy(*image, Box2ui(0, 0, width, height),
		 *scale, Box2ui(0, 0, width/2, height/2));
	    width /= 2;
	    height /= 2;
	    delete image;
	    image = scale;
	}
    }
    texture->loadLevel(0, (unsigned char*) image->address(0, 0));
    delete image;
    
    return texture;
}

#ifdef STATIC

/* Make sure the drivers we want are linked in. */

#ifdef q_windows
extern int QDriver_OS_Win32;
int* osref = &QDriver_OS_Win32;

extern int QDriver_Input_Win32;
int* inpref = &QDriver_Input_Win32;

extern int QDriver_Display_DX7;
int* displayref = &QDriver_Display_DX7;
#else    
extern int QDriver_OS_X11;
int* osref = &QDriver_OS_X11;
extern int QDriver_Display_OpenGL;
int* displayref = &QDriver_Display_OpenGL;
#endif


#endif // STATIC


void InitWorld()
{
    factory = Factory::create();
    renderer = factory->createRenderer(NULL, "Teapot", 0, 0, 640, 480);


    lights = renderer->createLightManager();
    Light light;
    light.point(Maths::Vec3f(100, 0, -500));
    light.endRange = 500;
    lights->set(&light, 1);
    lights->setAmbient(Maths::Vec3f(0.3f, 0.3f, 0.3f));
    lights->setOptions(LightManager::ModulateAmbient |
		       LightManager::ModulateDiffuse |
		       LightManager::CopySharedFields);
    
    buffer = renderer->backBuffer();

    mat.emissive = Maths::Vec3f(0, 0, 0);
    mat.ambient = Maths::Vec3f(1, 1, 1);
    mat.diffuse = Maths::Vec3f(1, 1, 1);
    mat.specular = Maths::Vec3f(1, 1, 1);
    mat.shininess = 15;
    mat.brilliance = 1;

    text = loadBitmap(renderer, "teapot.bmp");
    text2 = loadBitmap(renderer, "teapot2.bmp");
    text3 = loadBitmap(renderer, "teapot3.bmp");

    if (!text || !text2 || !text3) {
    	fprintf(stderr, "teapot{,1,2}.bmp not found in the current directory\n");
	exit(1);
    }

    shader[0] = renderer->createShader(text);
    shader[1] = renderer->createShader(text2);
    shader[2] = renderer->createShader(text3);

   
    setViewport(buffer);
    setCamera(buffer);

    rotation.setIdentity();
    
    myWorld = Patch::World::create(factory);
    
    testm = Patch::Model::create(factory);
    GenModelPov(testm,3);
    //makePatch(testm,shader);


    testm->minPatchSize(10);
    
    testm->maxSplitLevel(2);
    testm->minSplitLevel(1);
    
    
    //testm->add(testp);
    testi = Patch::Instance::create(testm);
    
    myWorld->add(testm);

    myWorld->add(testi);
    
}

void DeleteWorld()
{
    if(myWorld)
        delete myWorld;
    if(renderer)
        delete renderer;
    if(renderer2)
        delete renderer2;
}

// Called to draw scene
void RenderScene(void)
{
    InitWorld();

    Maths::Matrix4f rotation;
    rotation.setIdentity();
    
    for (;;) {
	rotation.setIdentity();
	rotation.position(Maths::Vec3f(0.0f, 0.0f, start));
	rotation.setRotation(Maths::Vec3f(1.0f,0.0f,0.0f), rot);
	buffer->setMatrix(Buffer::ModelMatrix, rotation);
	start -= 0.0f;
	rot += 0.03f;

	myWorld->render(buffer,shader,lights);
	
	renderer->update();

	Event e;
	if (renderer->nextEvent(e, false)) { // don't wait for an event
	    switch (e.code) {
	    case Event::WindowClose:
        	renderer->shutdown(); // start the process of shutting down
		break;
		
	    case Event::KeyPress:
		switch (e.key) {
		case Key::q:
		    renderer->shutdown();
		    break;
		    
		case Key::Left:
		case Key::Right:
		    // Do stuff
		    break;
		}
		break;

	    default:
		break;		// ignore all other events
	    }
	} else {
	    // The render has shutdown and is ready to quit
	    DeleteWorld();
	    break;
	}

    }
    
}

int
main(int argc, char** argv)
{

    RenderScene();
    printf("Test Complete.\n");

    return 0;
}
