/*
 *  Copyright (C) 2008 Kristian Kristola
 *
 *  This program is distributed under the terms of the
 *  GNU General Public License.
 *
 *  This file is part of Dave the Ordinary Spaceman.
 *
 *  Dave the Ordinary Spaceman 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 3 of the License, or (at your option) any later version.
 *
 *  Dave the Ordinary Spaceman 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 Dave the Ordinary Spaceman. If not, see
 *  <http://www.gnu.org/licenses/>.
 *
 */

#ifndef __MUTTEI_OL_DAVEN_ENTINEN_LIMANULJASKA__
#define __MUTTEI_OL_DAVEN_ENTINEN_LIMANULJASKA__

#ifndef GL_GLEXT_PROTOTYPES
#  define GL_GLEXT_PROTOTYPES
#endif

#if defined(__MACOS__) || defined(__APPLE__)
#  include <OpenGL/gl.h>
#else
#  include <GL/glew.h>
#endif

#include <string>
#include <list>
#include "opengl.h"
#include "game.h"
#include "kdtree.h"

using namespace std;

class Image;
class Scene;

class Model
{
public:
  Model();
  Model(const string &name);
  virtual ~Model();
  void load(const string &name);
  virtual void render();
  void calculate_ao();
  void set_position(float x, float y, float z);
  void set_rotation(float x, float y, float z);
  float *get_position() {return position;}
  float *get_rotation() {return rotation;}
  void set_scale(float x, float y, float z);
  void set_texture(Image *tex);
  void set_self_illumination(Image *tex);
  void set_environment(Image *tex);
  void set_reflectivity(Image *tex);
  Image *get_texture() const;
  Image *get_self_illumination() const;
  Image *get_environment() const;
  Image *get_reflectivity() const;
  int blend;
  bool lighting;
  void add_model(Model *m);
  void load_shader(const string &vert, const string &frag);
  void use_same_shader_as(Model *m);

protected:
  void allocate(unsigned int vertex_count, unsigned int index_count, bool normals, bool texcoords, bool colors);
  void upload();
  void init();
  void build_shader();
  void use_shader();
  void unuse_shader();

  void build_kdtree(KDTree& kd, int& hash);
  void shoot_ao_rays(KDTree& kd);

  friend class Scene;

  Scene *scene;

  float position[3];
  float rotation[3];
  float scale[3];

  GLuint vertex_buf;
  GLuint index_buf;
  GLuint normal_buf;
  GLuint texcoord_buf;
  GLuint color_buf;
  GLfloat *vertices;
  GLuint  *indices;
  GLfloat *normals;
  GLfloat *texcoords;
  GLfloat *colors;
  unsigned int vertex_count;
  unsigned int index_count;
  list<Model *> children;
  bool shader_built;
  bool using_shader;
  GLProgram *program;
  Image *texture;
  Image *self_illumination;
  Image *environment;
  Image *reflectivity;
  bool has_ao;

private:
  bool is_3ds;
};

class Scene
{
public:
  Scene();
  ~Scene();

  void add_model(Model *model);
  void render() const;
  void set_camera_pos(float x, float y, float z);
  float *get_camera_pos();
  void set_fill_background(bool x) {fill_background = x;}
  void set_background(float r, float g, float b) {background[0] = r; background[1] = g; background[2] = b;}
  void set_background(int r, int g, int b) {set_background(r/255.0f, g/255.0f, b/255.0f);}
  float *get_background() {return &background[0];}

private:
  friend class Model;
  list<Model *> models;
  float camera_pos[3];
  bool fill_background;
  float background[3];
};

class Starfield : public Model
{
public:
  void load();
};

#endif
