//  SuperTux - WalkingBadguy
//  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
//
//  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 3 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, see <http://www.gnu.org/licenses/>.

#include "badguy/walking_badguy.hpp"

#include <math.h>

#include "sprite/sprite.hpp"

WalkingBadguy::WalkingBadguy(const Vector& pos,
                             const std::string& sprite_name_,
                             const std::string& walk_left_action_,
                             const std::string& walk_right_action_,
                             int layer_,
                             const std::string& light_sprite_name) :
  BadGuy(pos, sprite_name_, layer_, light_sprite_name),
  walk_left_action(walk_left_action_),
  walk_right_action(walk_right_action_),
  walk_speed(80),
  max_drop_height(-1),
  turn_around_timer(),
  turn_around_counter()
{
}

WalkingBadguy::WalkingBadguy(const Vector& pos,
                             Direction direction,
                             const std::string& sprite_name_,
                             const std::string& walk_left_action_,
                             const std::string& walk_right_action_,
                             int layer_,
                             const std::string& light_sprite_name) :
  BadGuy(pos, direction, sprite_name_, layer_, light_sprite_name),
  walk_left_action(walk_left_action_),
  walk_right_action(walk_right_action_),
  walk_speed(80),
  max_drop_height(-1),
  turn_around_timer(),
  turn_around_counter()
{
}

WalkingBadguy::WalkingBadguy(const ReaderMapping& reader,
                             const std::string& sprite_name_,
                             const std::string& walk_left_action_,
                             const std::string& walk_right_action_,
                             int layer_,
                             const std::string& light_sprite_name) :
  BadGuy(reader, sprite_name_, layer_, light_sprite_name),
  walk_left_action(walk_left_action_),
  walk_right_action(walk_right_action_),
  walk_speed(80),
  max_drop_height(-1),
  turn_around_timer(),
  turn_around_counter()
{
}

void
WalkingBadguy::initialize()
{
  if (m_frozen)
    return;
  m_sprite->set_action(m_dir == Direction::LEFT ? walk_left_action : walk_right_action);
  m_col.m_bbox.set_size(m_sprite->get_current_hitbox_width(), m_sprite->get_current_hitbox_height());
  m_physic.set_velocity_x(m_dir == Direction::LEFT ? -walk_speed : walk_speed);
  m_physic.set_acceleration_x (0.0);
}

void
WalkingBadguy::set_walk_speed (float ws)
{
  walk_speed = fabsf(ws);
  /* physic.set_velocity_x(dir == LEFT ? -walk_speed : walk_speed); */
}

void
WalkingBadguy::add_velocity (const Vector& velocity)
{
  m_physic.set_velocity(m_physic.get_velocity() + velocity);
}

void
WalkingBadguy::active_update(float dt_sec, float dest_x_velocity)
{
  BadGuy::active_update(dt_sec);

  float current_x_velocity = m_physic.get_velocity_x ();

  if (m_frozen)
  {
    m_physic.set_velocity_x (0.0);
    m_physic.set_acceleration_x (0.0);
  }
  /* We're very close to our target speed. Just set it to avoid oscillation */
  else if ((current_x_velocity > (dest_x_velocity - 5.0f)) &&
           (current_x_velocity < (dest_x_velocity + 5.0f)))
  {
    m_physic.set_velocity_x (dest_x_velocity);
    m_physic.set_acceleration_x (0.0);
  }
  /* Check if we're going too slow or even in the wrong direction */
  else if (((dest_x_velocity <= 0.0f) && (current_x_velocity > dest_x_velocity)) ||
           ((dest_x_velocity > 0.0f) && (current_x_velocity < dest_x_velocity)))
  {
    /* acceleration == walk-speed => it will take one second to get from zero
     * to full speed. */
    m_physic.set_acceleration_x (dest_x_velocity);
  }
  /* Check if we're going too fast */
  else if (((dest_x_velocity <= 0.0f) && (current_x_velocity < dest_x_velocity)) ||
           ((dest_x_velocity > 0.0f) && (current_x_velocity > dest_x_velocity)))
  {
    /* acceleration == walk-speed => it will take one second to get twice the
     * speed to normal speed. */
    m_physic.set_acceleration_x ((-1.f) * dest_x_velocity);
  }
  else
  {
    /* The above should have covered all cases. */
    assert(false);
  }

  if (max_drop_height > -1) {
    if (on_ground() && might_fall(max_drop_height+1))
    {
      turn_around();
    }
  }

  if ((m_dir == Direction::LEFT) && (m_physic.get_velocity_x () > 0.0f)) {
    m_dir = Direction::RIGHT;
    set_action (walk_right_action, /* loops = */ -1);
  }
  else if ((m_dir == Direction::RIGHT) && (m_physic.get_velocity_x () < 0.0f)) {
    m_dir = Direction::LEFT;
    set_action (walk_left_action, /* loops = */ -1);
  }
}

void
WalkingBadguy::active_update(float dt_sec)
{
  active_update (dt_sec, (m_dir == Direction::LEFT) ? -walk_speed : +walk_speed);
}

void
WalkingBadguy::collision_solid(const CollisionHit& hit)
{

  update_on_ground_flag(hit);

  if (hit.top) {
    if (m_physic.get_velocity_y() < 0) m_physic.set_velocity_y(0);
  }
  if (hit.bottom) {
    if (m_physic.get_velocity_y() > 0) m_physic.set_velocity_y(0);
  }

  if ((hit.left && (m_dir == Direction::LEFT)) || (hit.right && (m_dir == Direction::RIGHT))) {
    turn_around();
  }

}

HitResponse
WalkingBadguy::collision_badguy(BadGuy& , const CollisionHit& hit)
{
  if (hit.top) {
    return FORCE_MOVE;
  }

  if ((hit.left && (m_dir == Direction::LEFT)) || (hit.right && (m_dir == Direction::RIGHT))) {
    turn_around();
  }

  return CONTINUE;
}

void
WalkingBadguy::turn_around()
{
  if (m_frozen)
    return;
  m_dir = m_dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT;
  if (get_state() == STATE_INIT || get_state() == STATE_INACTIVE || get_state() == STATE_ACTIVE) {
    m_sprite->set_action(m_dir == Direction::LEFT ? walk_left_action : walk_right_action);
  }
  m_physic.set_velocity_x(-m_physic.get_velocity_x());
  m_physic.set_acceleration_x (-m_physic.get_acceleration_x ());

  // if we get dizzy, we fall off the screen
  if (turn_around_timer.started()) {
    if (turn_around_counter++ > 10) kill_fall();
  } else {
    turn_around_timer.start(1);
    turn_around_counter = 0;
  }

}

void
WalkingBadguy::freeze()
{
  BadGuy::freeze();
  m_physic.set_velocity_x(0);
}

void
WalkingBadguy::unfreeze()
{
  BadGuy::unfreeze();
  WalkingBadguy::initialize();
}

float
WalkingBadguy::get_velocity_y() const
{
  return m_physic.get_velocity_y();
}

void
WalkingBadguy::set_velocity_y(float vy)
{
  m_physic.set_velocity_y(vy);
}

/* EOF */
