/*
 *  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 _OLIOT_JA_MUUT_METSAN_ELAIMET_
#define _OLIOT_JA_MUUT_METSAN_ELAIMET_

#include <math.h>
#include <iostream>
#include <assert.h>
#include <vector>
#include <utility>
#include "level.h"
#include "vector.h"
#include "ability.h"

//DEBUG!
#include <iostream>
using namespace std;

class Image;
class Level;
class Ability;
class Teletext;

//class Ability::Ammo;

class Fellow : public Level::Block
{
public:
  static Type
    *DAVE,
    *OERVELOE,
    *HAUKI,
    *BOSS_BIO,
    *HATTIAMPERER;

  static void load_types();

  Fellow(const Type *t);
  ~Fellow();

  void move(const Vector &velocity);
  Vector &get_position();
  void set_position(const Vector &p);
  void handle(Level *level, const float dt, const float time);
  void render();
  void go_left(const bool go);
  void go_right(const bool go);
  void jump(const float time);
  void stop_jumping();
  void stop_jumping_in_future(const float time);
  bool is_touching_floor() const;
  const Vector &is_touching_slope() const;
  void begin_physics_sequence();
  Vector get_velocity() const;
  void set_velocity(const Vector &x) {velocity = x;}
  void rise_from_dead();
  void use(const float time);
  void add_ability(Ability *a);
  Ability *remove_ability();
  float get_heading() const;
  bool effect();
  void operator <<(const pair<const char *, bool> &);
  void operator <<(const char *buffer);
  void operator >>(char *buffer) const;
  float death_animation_state() const;
  int get_score() const {return score;}
  void add_score(int x) {score += x;}
  float get_voltage() const {return voltage;}
  void add_voltage(float x) {voltage += x;}
  void sub_voltage(float x) {voltage -= x; if (voltage < 0.0f) voltage = 0.0f;}
  Vector external_force;
  const vector<Ability *> *get_inventory() const {return &inventory;}
  int get_active_ability_num() const {return active_ability;}
  Ability *get_active_ability() const {
    if (!inventory.size())
      return NULL;
    assert(0 <= active_ability && active_ability < (int)inventory.size());
    return inventory[active_ability];
  }
  void select_ability(int x);
  void select_ability(Ability::Type type);
  void inc_parts() {parts++;}
  void inc_cass() {cass++;}
  int get_parts() const {return parts;}
  int get_cass() const {return cass;}
  static int noise_chan;

private:
  Vector original_position;
  Image *image;
  Vector size;
  Vector velocity;
  Vector acceleration;
  float heading;
  float accel_left, accel_right;
  float jumped_at;
  float touched_floor_at;
  float time;
  float previous_time;
  float started_waiting_for_jumping_at;
  Vector valiaikainen_purkkaviritys;
  list<float> stop_jumping_at;
  vector<Ability *> inventory;
  int active_ability;
  bool purgga_flag;
  int score;
  float voltage;
  int parts;
  int cass;

  void collide(Level *level, const float dt, const float time);
  bool handle_collision_events(Level::Block *against, const Vector &col_surf_normal, const Vector &original_velocity);
  bool is_jumping() const;
  bool is_waiting_for_jumping() const;
  void jump_really(const float time);
  void die(const float t, int score);
  enum DeathAnimation {
    GO_DOWN,
    EXPLODE_CUBES,
    BLOCK,
    D_ANIM_LAST
  } death_animation;
  bool think();
  enum Event {
    INITIALIZE,
    COLLIDE_LEFT,
    COLLIDE_RIGHT,
    COLLIDE_UP
  };
  void event(Event e, Level::Block *target);
  void hit_by_ammo(/*class*/ Ability::Ammo *ammo);
  struct Particle
  {
    Vector3D position;
    Vector3D velocity;
    Vector3D rotation;
    Vector3D angular_velocity;
    bool alive;
    Particle() : alive(true) {}
  };
  Particle *particles;
};


#endif
