#include "monsterp.h"

#include "etc.h"

#include "pic8/monster.h"

#define MONSTER_BITMAP_WIDTH  (monster_width*DISP_PICTURE_TICS_X)
#define MONSTER_BITMAP_HEIGHT (monster_height*DISP_PICTURE_TICS_Y)
#define MONSTER_BITMAP_BITMAP (void *)monster_bits

int Monster_GetX(Monster monster) { return (monster->x); }
int Monster_GetY(Monster monster) { return (monster->y); }
int Monster_GetSpeed(Monster monster) { return (monster->speed); }
int Monster_GetCounter(Monster monster) { return (monster->counter); }

/*===========================================================================*/
/* オブジェクトの生成と削除                                                  */
/*===========================================================================*/

/*---------------------------------------------------------------------------*/
/* 生成                                                                      */
/*---------------------------------------------------------------------------*/

Monster Monster_Create(Monster monster, Disp disp, Map map,
		       int x, int y, int dir, int type, int speed)
{
  if (monster == NULL) Error("Monster_Create", "Cannot allocate memory.");

  monster->x = x;
  monster->y = y;
  monster->dir = dir;
  monster->type = type;
  monster->counter = 0;
  monster->speed = speed;

  Map_Put(map, monster->x, monster->y, MAP_NONE);

  monster->picture = Picture_Create(&monster->_picture,
				    disp,
				    MONSTER_BITMAP_WIDTH,
				    MONSTER_BITMAP_HEIGHT,
				    MONSTER_BITMAP_BITMAP);
  monster->character = Character_Create(&monster->_character,
					disp,
					monster->picture,
					x, y);

  Character_Locate(monster->character, monster->x, monster->y);
  Character_Visible(monster->character);

  return (monster);
}

/*---------------------------------------------------------------------------*/
/* 削除                                                                      */
/*---------------------------------------------------------------------------*/

Monster Monster_Destroy(Monster monster)
{
  if (monster == NULL) Error("Monster_Destroy()", "Invalid object.");
  if (monster->character) {
    monster->character = Character_Destroy(monster->character);
  }
  return (NULL);
}

/*---------------------------------------------------------------------------*/
/* 移動                                                                      */
/*---------------------------------------------------------------------------*/

static int DirToVX(int dir)
{
  if (dir == MONSTER_LEFT ) return (-1);
  if (dir == MONSTER_RIGHT) return ( 1);
  return (0);
}

static int DirToVY(int dir)
{
  if (dir == MONSTER_UP  ) return (-1);
  if (dir == MONSTER_DOWN) return ( 1);
  return (0);
}

static int VectorToDir(int vx, int vy)
{
  if (vx == -1) return (MONSTER_LEFT);
  if (vx ==  1) return (MONSTER_RIGHT);
  if (vy == -1) return (MONSTER_UP);
  if (vy ==  1) return (MONSTER_DOWN);
  return (0);
}

static int Sign(int x)
{
  if (x < 0) return (-1);
  if (x > 0) return (1);
  return (0);
}

static int Move_OIKAKE(Monster monster, Map map, Man man)
{
  int x, y, vx, vy, i;

  x = monster->x + DirToVX(monster->dir);
  y = monster->y + DirToVY(monster->dir);
  if (Map_IsAbleToMove(map, x, y) && (getrand(100) > 80)) {
    monster->x = x;
    monster->y = y;
    return (1);
  }

  for (i = 0; i < 5; i++) {
    vx = Sign(Man_GetX(man) - monster->x);
    vy = Sign(Man_GetY(man) - monster->y);
    if (vx && vy) {
      if (getrand(2)) vx = 0; else vy = 0;
    }
    x = monster->x + vx;
    y = monster->y + vy;
    if (Map_IsAbleToMove(map, x, y)) {
      monster->x = x;
      monster->y = y;
      monster->dir = VectorToDir(vx, vy);
      return (1);
    }
  }

  if (i == 5) {
    for (i = 0; i < 5; i++) {
      if (i > 0) monster->dir = getrand(4);
      x = monster->x + DirToVX(monster->dir);
      y = monster->y + DirToVY(monster->dir);
      if (Map_IsAbleToMove(map, x, y)) {
	monster->x = x;
	monster->y = y;
	return (1);
      }
    }
  }

  return (0);
}

static int Move_OTOBOKE(Monster monster, Map map, Man man)
{
  int x, y, i;

  if (getrand(100) > 70) monster->dir = getrand(4);

  for (i = 0; i < 5; i++) {
    if (i > 0) monster->dir = getrand(4);
    x = monster->x + DirToVX(monster->dir);
    y = monster->y + DirToVY(monster->dir);
    if (Map_IsAbleToMove(map, x, y)) {
      monster->x = x;
      monster->y = y;
      return (1);
    }
  }

  return (0);
}

static int Move_CHOKUSHIN(Monster monster, Map map, Man man)
{
  int x, y, i;

  if (getrand(100) > 97) monster->dir = getrand(4);

  for (i = 0; i < 5; i++) {
    if (i > 0) monster->dir = getrand(4);
    x = monster->x + DirToVX(monster->dir);
    y = monster->y + DirToVY(monster->dir);
    if (Map_IsAbleToMove(map, x, y)) {
      monster->x = x;
      monster->y = y;
      return (1);
    }
  }

  return (0);
}

static int Move_KIMAGURE(Monster monster, Map map, Man man)
{
  int x, y, vx, vy, i;

  x = monster->x + DirToVX(monster->dir);
  y = monster->y + DirToVY(monster->dir);
  if (Map_IsAbleToMove(map, x, y) && (getrand(100) > 75)) {
    monster->x = x;
    monster->y = y;
    return (1);
  }

  for (i = 0; i < 5; i++) {
    if (getrand(100) > 70) {
      vx = Sign(Man_GetX(man) - monster->x);
      vy = Sign(Man_GetY(man) - monster->y);
    } else if (getrand(100) > 10) {
      vx = getrand(2) * 2 - 1;
      vy = getrand(2) * 2 - 1;
    } else {
      vx = vy = 0;
    }
    if (vx && vy) {
      if (getrand(2)) vx = 0; else vy = 0;
    }

    x = monster->x + vx;
    y = monster->y + vy;
    if (Map_IsAbleToMove(map, x, y)) {
      monster->x = x;
      monster->y = y;
      monster->dir = VectorToDir(vx, vy);
      return (1);
    }
  }

  return (0);
}

int Monster_Move(Monster monster, Map map, Man man)
{
  monster->counter += monster->speed;

  while (monster->counter > 10000) {
    monster->counter -= 10000;
    switch (monster->type) {
    case MONSTER_TYPE_OIKAKE :
      Move_OIKAKE(monster, map, man);
      break;
    case MONSTER_TYPE_OTOBOKE :
      Move_OTOBOKE(monster, map, man);
      break;
    case MONSTER_TYPE_CHOKUSHIN :
      Move_CHOKUSHIN(monster, map, man);
      break;
    case MONSTER_TYPE_KIMAGURE :
      Move_KIMAGURE(monster, map, man);
      break;
    }

    Map_Put(map, monster->x, monster->y, MAP_NONE);
    Character_Locate(monster->character, monster->x, monster->y);
  }

  Character_Locate(monster->character, monster->x, monster->y);
  return (0);
}

int Monster_Explode(Monster monster, Map map, Man man)
{
  int x, y;

  x = monster->x;
  y = monster->y;

  while (1) {
    monster->x = getrand(Map_GetWidth(map));
    monster->y = getrand(Map_GetHeight(map));

    if ( (monster->x >= Man_GetX(man) - 5) &&
	 (monster->x <= Man_GetX(man) + 5) &&
	 (monster->y >= Man_GetY(man) - 5) &&
	 (monster->y <= Man_GetY(man) + 5) )
      continue;

    if (Map_IsAbleToMove(map, monster->x, monster->y)) break;
  }

  monster->dir = getrand(4);
  Map_Put(map, monster->x, monster->y, MAP_NONE);
  Character_Locate(monster->character, monster->x, monster->y);

  Map_Explode(map, x, y);

  return (0);
}

/*****************************************************************************/
/* End of File.                                                              */
/*****************************************************************************/
