/*
------------------------------------------------------------
   Kobo Deluxe - An enhanced SDL port of XKobo
------------------------------------------------------------
 * Copyright (C) 1995, 1996 Akira Higuchi
 * Copyright (C) 2001-2003, 2007 David Olofson
 * 
 * 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.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef XKOBO_H_ENEMIES
#define XKOBO_H_ENEMIES

#include <stdio.h>
#include "kobo.h"
#include "config.h"
#include "myship.h"
#include "manage.h"
#include "sound.h"

#define ABS(x)   (((x)>=0) ? (x) : (-(x)))
#define MAX(x,y) (((x)>(y)) ? (x) : (y))

#define	HEALTH_INDESTRUCTIBLE	9000000

class _enemy;
class _enemies;
//---------------------------------------------------------------------------//
struct enemy_kind
{
	int	score;
	void (_enemy::*make) ();
	void (_enemy::*move) ();
	void (_enemy::*kill) ();
	int	hitsize;
	int	bank, frame;
	int	layer;
};


extern const enemy_kind beam;
extern const enemy_kind explosion;
extern const enemy_kind explosion3;
extern const enemy_kind explosion4;
extern const enemy_kind explosion5;
extern const enemy_kind ringexpl;
extern const enemy_kind beamexpl;
extern const enemy_kind boltexpl;
extern const enemy_kind rockexpl;
extern const enemy_kind enemy1;
extern const enemy_kind enemy2;
extern const enemy_kind enemy3;
extern const enemy_kind enemy4;
extern const enemy_kind enemy5;
extern const enemy_kind enemy6;
extern const enemy_kind enemy7;
extern const enemy_kind bomb1;
extern const enemy_kind bomb2;
extern const enemy_kind bombdeto;
extern const enemy_kind cannon;
extern const enemy_kind pipe1;
extern const enemy_kind core;
extern const enemy_kind pipe2;
extern const enemy_kind rock;
extern const enemy_kind ring;
extern const enemy_kind enemy_m1;
extern const enemy_kind enemy_m2;
extern const enemy_kind enemy_m3;
extern const enemy_kind enemy_m4;


//---------------------------------------------------------------------------//
enum _state_t
{
	notuse,
	reserved,
	moving
};

class _enemy
{
	cs_obj_t	*object;	/* For the gfxengine connection */
	_state_t	_state;
	const enemy_kind *ek;
	int		x, y;
	int		h, v;
	int		di;
	int		a, b;
	int		count;
	int		health;
	int		damage;
	int		shootable;
	int		diffx, diffy;
	int		norm;
	int		hitsize;
	int		bank, frame;
	void hit(int dmg);
	void move_enemy_m(int quick, int maxspeed);
	void move_enemy_template(int quick, int maxspeed);
	void move_enemy_template_2(int quick, int maxspeed);
	void move_enemy_template_3(int quick, int maxspeed);
	void shot_template(const enemy_kind * ekp,
			int shift, int rand_num, int maxspeed);
	void shot_template_8_dir(const enemy_kind * ekp);
      public:
	 _enemy();
	inline void init();
	inline void release();
	void state(_state_t s);
	inline void move();
	inline void move_intro();
	inline void put();
	inline int make(const enemy_kind * k,
			int px, int py, int h1, int v1, int dir = 0);
	inline int realize();
	inline int is_pipe();
	inline int erase_cannon(int px, int py);

	void kill_default();

	void make_beam();
	void move_beam();
	void kill_beam();

	void make_rock();
	void move_rock();
	void kill_rock();

	void make_ring();
	void move_ring();
	void kill_ring();

	void make_bomb();
	void move_bomb1();
	void move_bomb2();

	void make_expl();
	void move_expl();

	void make_cannon();
	void move_cannon();
	void kill_cannon();

	void make_core();
	void move_core();
	void kill_core();

	void make_pipe1();
	void move_pipe1();

	void make_pipe2();
	void move_pipe2();

	void make_enemy1();
	void move_enemy1();

	void make_enemy2();
	void move_enemy2();

	void make_enemy3();
	void move_enemy3();

	void make_enemy4();
	void move_enemy4();

	void make_enemy();
	void move_enemy5();
	void move_enemy6();
	void move_enemy7();

	void make_enemy_m1();
	void move_enemy_m1();

	void make_enemy_m2();
	void move_enemy_m2();

	void make_enemy_m3();
	void move_enemy_m3();

	void make_enemy_m4();
	void move_enemy_m4();
};

//---------------------------------------------------------------------------//
class _enemies
{
	static _enemy enemy[ENEMY_MAX];
	static _enemy *enemy_max;
	static const enemy_kind *ekind_to_generate_1;
	static const enemy_kind *ekind_to_generate_2;
	static int e1_interval;
	static int e2_interval;
	static int explocount;
      public:
	static int is_intro;
	static int init();
	static void off();
	static void move();
	static void move_intro();
	static void put();
	static int make(const enemy_kind * ek,
			int x, int y, int h = 0, int v = 0, int di = 0);
	static const enemy_kind *randexp();
	static int erase_cannon(int x, int y);
	static int exist_pipe();
	static void set_ekind_to_generate(const enemy_kind * ek1, int i1,
			const enemy_kind * ek2, int i2);
	static inline const enemy_kind *ek1()
	{
		return ekind_to_generate_1;
	}
	static inline const enemy_kind *ek2()
	{
		return ekind_to_generate_2;
	}
	static inline int eint1()
	{
		return e1_interval;
	}
	static inline int eint2()
	{
		return e2_interval;
	}
};

extern _enemies enemies;


inline void _enemy::init()
{
	release();
}

inline void _enemy::release()
{
	state(notuse);
}

inline int _enemy::make(const enemy_kind * k, int px, int py,
		int h1, int v1, int dir)
{
	if(_state != notuse)
		return -1;

	ek = k;
	state(reserved);
	x = PIXEL2CS(px);
	y = PIXEL2CS(py);
	di = dir;
	h = h1;
	v = v1;
	a = 0;
	b = 0;
	count = 0;
	health = 20;
	damage = 50;
	shootable = 1;
	hitsize = ek->hitsize;
	bank = ek->bank;
	frame = ek->frame;
	(this->*(ek->make)) ();
	return 0;
}

inline void _enemy::hit(int dmg)
{
	if(HEALTH_INDESTRUCTIBLE != health)
		health -= dmg;
	if(enemies.is_intro)
	{
		if(health <= 0)
		{
			enemies.make(&explosion, CS2PIXEL(x), CS2PIXEL(y));
			release();
		}
		return;
	}
	else if(health > 0)
	{
		if(ek == &rock)
			sound.g_bolt_hit_rock(x, y);
		else
			sound.g_bolt_hit(x, y);
		return;
	}

	manage.add_score(ek->score);
	(this->*(ek->kill)) ();
}

inline int _enemy::erase_cannon(int px, int py)
{
	if( (_state != notuse) && (ek == &cannon)
			&& ((signed)(CS2PIXEL(x) & (WORLD_SIZEX - 1)) >> 4 == px)
			&& ((signed)(CS2PIXEL(y) & (WORLD_SIZEY - 1)) >> 4 == py)
	  )
	{
		release();
		return 1;
	}
	return 0;
}

inline void _enemy::move()
{
	if(_state != moving)
		return;
	x += h;
	y += v;
	diffx = CS2PIXEL(x) - myship.get_x();
	diffy = CS2PIXEL(y) - myship.get_y();

	if(diffx > (WORLD_SIZEX >> 1))
	{
		diffx -= WORLD_SIZEX;
		x -= PIXEL2CS(WORLD_SIZEX);
	}
	if(diffx < -(WORLD_SIZEX >> 1))
	{
		diffx += WORLD_SIZEX;
		x += PIXEL2CS(WORLD_SIZEX);
	}
	if(diffy > (WORLD_SIZEY >> 1))
	{
		diffy -= WORLD_SIZEY;
		y -= PIXEL2CS(WORLD_SIZEY);
	}
	if(diffy < -(WORLD_SIZEY >> 1))
	{
		diffy += WORLD_SIZEY;
		y += PIXEL2CS(WORLD_SIZEY);
	}

	norm = MAX(ABS(diffx), ABS(diffy));
	(this->*(ek->move)) ();

	// Handle collisions with the player ship
	if((hitsize >= 0) && (norm < (hitsize + HIT_MYSHIP)))
		if(prefs->cmd_indicator)
			sound.g_player_damage();
		else if(myship.alive())
		{
			hit(game.damage);	// Ship damages object
			myship.hit(damage);	// Object damages ship
		}

	// Handle collisions with player bolts (Player bolts kill themselves
	// when they hit something, so we don't need to hit them from here.)
	if(!shootable || (norm >= ((VIEWLIMIT >> 1) + 8)))
		return;

	int dmg = myship.hit_bolt(CS2PIXEL(x), CS2PIXEL(y),
				hitsize + HIT_BOLT, health);
	if(dmg)
		if(prefs->cmd_indicator)
			sound.g_player_damage();
		else
			hit(dmg);	// Bolt damages object
}

inline void _enemy::move_intro()
{
	if(_state != moving)
		return;
	x += h;
	y += v;
	diffx = CS2PIXEL(x) - myship.get_x();
	diffy = CS2PIXEL(y) - myship.get_y();

	if(diffx > (WORLD_SIZEX >> 1))
	{
		diffx -= WORLD_SIZEX;
		x -= PIXEL2CS(WORLD_SIZEX);
	}
	if(diffx < -(WORLD_SIZEX >> 1))
	{
		diffx += WORLD_SIZEX;
		x += PIXEL2CS(WORLD_SIZEX);
	}
	if(diffy > (WORLD_SIZEY >> 1))
	{
		diffy -= WORLD_SIZEY;
		y -= PIXEL2CS(WORLD_SIZEY);
	}
	if(diffy < -(WORLD_SIZEY >> 1))
	{
		diffy += WORLD_SIZEY;
		y += PIXEL2CS(WORLD_SIZEY);
	}

	norm = MAX(ABS(diffx), ABS(diffy));
	(this->*(ek->move)) ();
}

inline void _enemy::put()
{
	if(!object)
		return;
	object->point.v.x = x;
	object->point.v.y = y;
	cs_obj_image(object, bank, frame + di - 1);
}

inline int _enemy::realize()
{
	if(_state == reserved)
	{
		state(moving);
		if(object)
		{
			object->point.v.x = x;
			object->point.v.y = y;
			cs_point_force(&object->point);
		}
	}
	return (_state == moving);
}

inline int _enemy::is_pipe()
{
	return ((_state != notuse) && ((ek == &pipe1) || (ek == &pipe2)));
}


#endif				// XKOBO_H_ENEMIES
