#include "person.h"
#include "mykeyboard.h"
#include "camera.h"
#include "globals.h"
#include "map.h"
#include "tdsound.h"
#include "projectile.h"
#include "particle.h"



std::deque<Person *> persons;
std::deque<Person *> actual_selection;

Person *ActualOver=NULL;



void SpawnPerson(SpawnPoint *sp) {
  Person *o;
  o=NULL;
  if (sp->type == PERSON_GUARD) {
    o = new Person();
    o->SetSize(42, 38);
    o->SetPosition(sp->x, sp->y);
    o->SetAttackRange(4);
    o->SetHuntingRange(180);
    o->SetAttackSpeed(30);
    o->SetMine(true);
    o->SetDamage(5.5);
    o->SetSpeed(3);
    o->SetCanGuard(true);
    o->SetName("Guard");
    o->LoadGraphics("ElfGuard");
    GameObjects.push_back(o);
    persons.push_back(o);
  }

  if (sp->type == PERSON_GUARD_SNOWBALL) {
    o = new Person();
    o->SetSize(42, 38);
    o->SetPosition(sp->x, sp->y);
    o->SetProjectileAttack(PROJECTILE_TYPE_SNOWBALL);
    o->SetAttackRange(200);
    o->SetHuntingRange(250);
    o->SetAttackSpeed(50);
    o->SetMaxLife(75);
    o->SetMine(true);
    o->SetDamage(5);
    o->SetSpeed(3);
    o->SetName("Snowballer");
    o->LoadGraphics("ElfGuardSnowball");
    GameObjects.push_back(o);
    persons.push_back(o);
  }


  if (sp->type == PERSON_AUTOMATED_GUARD) {
    o = new Person();
    o->SetSize(24, 24);
    o->SetMaxLife(15);
    o->SetPosition(sp->x, sp->y);
    o->SetAttackRange(4);
    o->SetHuntingRange(250);
    o->SetAttackSpeed(15);
    o->SetMine(true);
    o->SetDamage(1);
    o->SetSpeed(4);
    o->SetName("Toy Guard");
    o->SetGasProtection(true);
    o->LoadGraphics("ElfRobot");
    o->SetRobot(true);
    GameObjects.push_back(o);
    persons.push_back(o);
  }

  if (sp->type == PERSON_ELF) {
    o = new Person();
    o->SetSize(42, 38);
    o->SetPosition(sp->x, sp->y);
    o->LoadGraphics("Elf");
    //o->SetProjectileAttack(PROJECTILE_TYPE_SNOWBALL);
    o->SetDamage(4);
    o->SetAttackRange(4);
    o->SetHuntingRange(260);
    o->SetName("Insurgent");
    o->SetAttackSpeed(90);
    o->SetMoraleInfluence(2);
    o->SetMaxLife(80);
    o->SetSpeed(3);
    GameObjects.push_back(o);
    persons.push_back(o);
  }

  if (sp->type == PERSON_ELF_TORCH) {
    o = new Person();
    o->SetSize(42, 38);
    o->SetPosition(sp->x, sp->y);
    o->LoadGraphics("ElfTorch");
    o->SetDamage(4);
    o->SetAttackRange(4);
    o->SetName("Torcher");
    o->SetHuntingRange(260);
    o->SetMaxLife(50);
    o->SetAttackSpeed(90);
    o->SetMoraleInfluence(2);
    o->SetSpeed(3);
    o->AddTorch();
    GameObjects.push_back(o);
    persons.push_back(o);
  }

  if (sp->type == PERSON_ELF_SHIELD) {
    o = new Person();
    o->SetSize(42, 38);
    o->SetPosition(sp->x, sp->y);
    o->LoadGraphics("ElfShield");
    //o->SetProjectileAttack(PROJECTILE_TYPE_SNOWBALL);
    o->SetDamage(5);
    o->SetName("Brawler");
    o->SetCanGuard(true);
    o->SetAttackRange(4);
    o->SetHuntingRange(260);
    o->SetMaxLife(120);
    o->SetAttackSpeed(90);
    o->SetMoraleInfluence(2);
    o->SetSpeed(3);
    GameObjects.push_back(o);
    persons.push_back(o);
  }


  if (sp->type == PERSON_ELF_SIGN) {
    o = new Person();
    o->SetSize(42, 48);
    o->SetPosition(sp->x, sp->y);
    o->LoadGraphics("ElfSign");
    //o->SetProjectileAttack(PROJECTILE_TYPE_SNOWBALL);
    o->SetDamage(3);
    o->SetMaxLife(60);
    o->SetAttackRange(4);
    o->SetName("Supporter");
    o->SetHuntingRange(260);
    o->SetAttackSpeed(90);
    o->SetMoraleInfluence(2);
    o->SetSpeed(3);
    GameObjects.push_back(o);
    persons.push_back(o);
  }

  if (sp->type == PERSON_ELF_SNOWBALL) {
    o = new Person();
    o->SetSize(42, 38);
    o->SetPosition(sp->x, sp->y);
    o->LoadGraphics("ElfSnowball");
    o->SetProjectileAttack(PROJECTILE_TYPE_SNOWBALL);
    o->SetDamage(4);
    o->SetName("Snowballer");
    o->SetAttackRange(200);
    o->SetHuntingRange(300);
    o->SetAttackSpeed(60);
    o->SetMaxLife(50);
    o->SetMoraleInfluence(2);
    o->SetSpeed(3);
    GameObjects.push_back(o);
    persons.push_back(o);
  }

  if (sp->type == PERSON_GRINCH) {
    o = new Person();
    o->SetSize(64, 64);
    o->SetPosition(sp->x, sp->y);
    o->LoadGraphics("Grinch");
    o->SetGasProtection(true);
    //o->SetProjectileAttack(PROJECTILE_TYPE_SNOWBALL);
    o->SetName("Leader Grinch");
    o->SetDamage(15);
    o->SetMaxLife(300);
    o->SetGrinch(true);
    o->SetAttackRange(32);
    o->SetHuntingRange(260);
    o->SetAttackSpeed(60);
    o->SetMoraleInfluence(2);
    o->SetSpeed(3);
    GameObjects.push_back(o);
    persons.push_back(o);
  }

  if (sp->type == PERSON_SANTA) {
    o = new Person();
    o->SetSize(96, 96);
    o->SetPosition(sp->x, sp->y);
    o->LoadGraphics("Santa");
    o->SetName("Santa Claus");
    o->SetGasProtection(true);
    o->SetDamage(45);
    o->SetMaxLife(2500);
    o->SetSanta(true);
    o->SetAttackRange(48);
    o->SetHuntingRange(300);
    o->SetAttackSpeed(60);
    o->SetMoraleInfluence(30);
    o->SetSpeed(2);
    o->SetMine(true);
    GameObjects.push_back(o);
    persons.push_back(o);
  }

  if (sp->type == PERSON_CAMERA) {
    Camera_X = sp->x-1024/2;
    Camera_Y = sp->y-768/2;
    TargetCamera_X=Camera_X;
    TargetCamera_Y=Camera_Y;
    update_camera();
  }

  if (o!=NULL) o->SetType(sp->type);
}




bool AnyPeopleSelected() {
  std::deque<Person *>::iterator it;
  for (it=persons.begin();it!=persons.end(); it+=1) {
    if (((*it)->IsMine()) && ((*it)->IsSelected())) return true;
  }
  return false;
}

void DeselectAllPeople() {
  std::deque<Person *>::iterator it;
  for (it=persons.begin();it!=persons.end(); it+=1) {
    (*it)->Deselect();
  }
}

void GetCurrentSelection() {
  CanGuard=false;
  actual_selection.clear();
  std::deque<Person *>::iterator it;
  for (it=persons.begin();it!=persons.end(); it+=1) {
    if ((*it)->IsSelected()) {
      actual_selection.push_back((*it));
      if ((*it)->GetCanGuard()) CanGuard=true;
    }
  }
}

void Person::UpdateWalk() {
  if (guard_mode) return;
  if ((target.x == x) && (target.y == y)) {
    target.x = -1; target.y = -1;
  }
  else {
    if (actual_map->IsFree(target.x, target.y)) {
      if (has_torch) {
        int i;
        i=randn(60);
        if (i==15) {
          AddParticle(PARTICLE_TORCH_01, x, y, 1, Vec2D(0, 0), 30);
        }
      }
      dir = target-Vec2D(x, y);
      dir = dir.Normalized() * (speed*DeltaTime);
      new_angle = dir.GetAngle()+AngleToRad(90);
      if (actual_map->IsFree(x+dir.x, y+dir.y)) {
        actual_animation=1;
        if (distance_between_points(target.x, target.y, x, y) < (speed*DeltaTime)) {
          x = target.x; y = target.y;
        }
        else {
          x += dir.x; y += dir.y;
        }
      }
      else {
        target.x = -1; target.y = -1;
      }
    }
    else {
      target.x = -1; target.y = -1;
    }
  }
}


void Person::UpdateAttack() {
  float t_x, t_y;
  t_x = target_p->GetX();
  t_y = target_p->GetY();
  if (target_p->IsUnconscious()) {target_p=NULL;SearchForNextEnemy(); return;}
  if (distance_between_points(t_x, t_y, x, y) > (attack_range+target_p->GetW())) {
    target.x = t_x;
    target.y = t_y;
    dir = Vec2D(x, y)-Vec2D(t_x, t_y);
    dir = dir.Normalized();
    target += dir*(target_p->GetW()/2);
  }
  else {
    target.x = -1;
    target.y = -1;
    if (attack_wait > 0) attack_wait -= DeltaTime;
    if (attack_wait < 0) attack_wait = 0;
    if (attack_wait == 0) {
      Attack(target_p);
      attack_wait = attack_speed;
      actual_animation = 2;
      dir = Vec2D(t_x, t_y)-Vec2D(x, y);
      dir = dir.Normalized();
      new_angle = dir.GetAngle()+AngleToRad(90);
    }
  }
}


void Person::UpdateSweep() {
  if (target_p == NULL) {
    target.x = target_sweep.x;
    target.y = target_sweep.y;
  }
  SearchForNextEnemy();
}



void Person::SearchForNextEnemy() {
  std::deque<Person *>::iterator it;
  Person *best_target;
  float best_dist,dist;
  if (flashed) return;
  best_target=NULL;
  best_dist = 100000000;
  for (it=persons.begin();it!=persons.end(); it+=1) {
    if (!InSameSide(*it) && (!(*it)->IsUnconscious())) {
      float tx, ty;
      tx = (*it)->GetX();
      ty = (*it)->GetY();
      if (mabs(tx-x) < hunt_range) {
      if (mabs(ty-y) < hunt_range) {
        dist=distance_between_points(tx, ty, x, y);
        if (dist < hunt_range) {
          if (dist < best_dist) {
            best_target=(*it);
            best_dist=dist;
          }
        }
      }
      }
    }
  }

  target_p = best_target;
}


void Person::InfluenceMorale() {
  if (morale_effect) return;
  std::deque<Person *>::iterator it;
  for (it=persons.begin(); it!=persons.end(); it+=1) {
    if ((*it) != this) {
      if ((InSameSide((*it))) && (!(*it)->IsUnconscious())) {
        float dist=distance_between_points((*it)->GetX(), (*it)->GetY(), x, y);
        if (dist < 300) {
          (*it)->AffectMorale(-morale_influence);
        }
      }
      else if ((!InSameSide((*it))) && (!(*it)->IsUnconscious())) {
        float dist=distance_between_points((*it)->GetX(), (*it)->GetY(), x, y);
        if (dist < 300) {
          (*it)->AffectMorale(morale_influence);
        }
      }
    }
  }
  morale_effect=true;
}


void Person::Attack(Person *p) {
  ALLEGRO_SAMPLE *samp;
  int i;

  if (projectile_attack != -1) {
    Vec2D targ;
    float tx, ty;
    tx = p->GetX()+(randn(15)-7);
    ty = p->GetY()+(randn(15)-7);
    targ = Vec2D(tx, ty) - Vec2D(x, y);

    float speed;
    speed=4;

    AddProjectile(x, y, 4, damage, targ.GetMagnitude()/(speed*0.75), mine, targ.Normalized(), projectile_attack);
  }
  else {
    i = randn(5)-1;
    samp = hit[i];
    if (santa) samp = hit[5];
    play_3d_sample(samp, 1.0, 1.0, ALLEGRO_PLAYMODE_ONCE, RSW(x), RSH(y));
    AddParticle(PARTICLE_HIT_01, p->GetX(), p->GetY(), 0, Vec2D(0, 0), 0.5);
    p->Hit(damage);
  }
}


void Person::Hit(float v) {
  if (guard_mode) life -= (v * damage_reduction) * 0.25;
  else life -= v * damage_reduction;
}


void Person::UpdateGeneral() {
  if (!Pause) {
  if (smoked) {
    target_p=NULL;
    target.x=target_sweep.x=-1;
  }
  if ((actual_animation == 2) && (!sprites[2].Finished()));
  else {
    actual_animation=0;
  }

  if (!IsUnconscious()) {
    UpdateAI();
    if (target_sweep.x != -1) UpdateSweep();
    if (target.x != -1) UpdateWalk();
    if (target_p != NULL) UpdateAttack();
  }
  else {
    selected = false;
  }



  dir = off_force.Normalized() * (speed*DeltaTime);
  if (actual_map->IsFree(x+dir.x, y+dir.y)) {
    x += dir.x; y += dir.y;
  }


  if (morale < 0) morale += 0.01*DeltaTime;
  if (morale > 0) morale -= 0.01*DeltaTime;

  UpdateAngle();
  Separate();

  if (((Vec2D(x, y)-Vec2D(last_x, last_y)).GetMagnitude() < speed/2) && (target.x != -1)) {
    inmovile_time += DeltaTime;
    if (inmovile_time > 5) {
      target.x=-1; target.y=-1;
      if (target_p == NULL) target_sweep.x=-1;
    }
  }
  else {
    inmovile_time = 0;
  }

  last_x=x;
  last_y=y;
  }

  if (mouse_x < GUI_LIMIT) {

  UpdateOver();

  if ((!RectangleSelection) && (over)) {
    if ((Keyboard_Recently_Input[ALLEGRO_KEY_MOUSE_LCLICK]) && (DelayAfterDoubleClick==0)) {
      DeselectAllPeople();
      selected = 1;
    }
  }


  }

  if ((guard_mode==1) && (target_p==NULL)) actual_animation=4;

  if (IsUnconscious()) {
    InfluenceMorale();
    actual_animation = 3;
    z = -5;
    guard_mode=0;
    morale=0;
  }

  if (flashed > 0) flashed -= DeltaTime;
  if (flashed < 0) flashed = 0;
  if (flashed) {
    target_p = NULL;
    target_sweep.x=-1;
  }
  if (smoked) {
    target_p=NULL;
    target.x=target_sweep.x=-1;
    int r;
    r = randn(1000);
    if (r == 50) {
      int i;
      i=randn(4)-1;
      play_3d_sample(cough[i], 1.2, 1.0, ALLEGRO_PLAYMODE_ONCE, RSW(x), RSH(y));
    }
    actual_animation = 3;
    new_angle=angle;
  }



  if (!Pause) sprites[actual_animation].Update();
  actual = sprites[actual_animation].GetActualBitmap();

  attacker=NULL;
  smoked=false;
}



void Person::UpdateAI() {
  delay_ai -= DeltaTime;

  if (delay_ai > 140) delay_ai = 140;
  if (guard_mode) return;

  if (smoked) return;

  if (morale < -50) {
    target_p=NULL;
    target_sweep.x=-1;
  }

  if (santa) {
    int l;
    hoho_delay-=DeltaTime;
    if (hoho_delay < 0) {
      hoho_delay = 150 + randn(400);
      int r;
      r=randn(HOHO_SIZE)-1;
      play_3d_sample(hoho[r], 1.6, 1.0, ALLEGRO_PLAYMODE_ONCE, RSW(x), RSH(y));
    }
  }

  if (delay_ai <= 0) delay_ai = 20+randn(20);
  else return;


  if (morale < -50) {
    float mx, my;
    mx = randn(250)-125;
    my = randn(250)-125;
    TargetWalk(x+mx, y+my);
    delay_ai = 120+randn(20);
    return;
  }

  //if (type_ai == TYPE_AI_NORMAL) {
    if (target.x != -1) return;
    if (target_p != NULL) return;
    SearchForNextEnemy();
    if ((target_p == NULL) && (!mine)) {
      float mx, my;
      mx = randn(60)-15;
      my = randn(60)-15;
      TargetWalk(original_x+mx, original_y+my);
      delay_ai = 60+randn(20);
    }
  //}
}


void Person::UpdateAngle() {
  float ang, toang, ldist, rdist;
  ang = angle;
  toang = new_angle;
  rdist=0;
  ldist=0;
  if (ang > toang) {
    rdist = ALLEGRO_PI*2 - ang + toang;
    ldist = ang - toang;
  }
  if (ang < toang) {
    rdist = toang - ang;
    ldist = ang + (ALLEGRO_PI*2 - toang);
  }
  if (rdist < ldist) {
    if (rdist > AngleToRad(6)) {
      angle += AngleToRad(6)*DeltaTime;
    }
    else {
      angle = new_angle;
    }
  }
  if (ldist < rdist) {
    if (ldist > AngleToRad(6)) {
      angle -= AngleToRad(6)*DeltaTime;
    }
    else {
      angle = new_angle;
    }
  }
}

void Person::Separate() {
  off_force = Vec2D(0, 0);
  if (guard_mode) return;
  if (IsUnconscious()) return;
  std::deque<Person *>::iterator it;
  for (it=persons.begin(); it!=persons.end(); it+=1) {
    if (((*it) != this) && (!(*it)->IsUnconscious())) {
      if ((*it)->IsInRange(x, y, w/2)) {
        Vec2D dir;
        dir = Vec2D(x, y) - Vec2D((*it)->GetX(), (*it)->GetY());
        off_force += dir;
      }
    }
  }
}

bool Person::IsInRange(float tx, float ty, float tr) {
  if (mabs(tx-x) > tr) return false;
  if (mabs(ty-y) > tr) return false;
  return distance_between_points(tx, ty, x, y) < tr;
}



void Person::TargetAttack(Person *p) {
  if (p==this) return;
  target_p = p;
}


void Person::UpdateOver() {
  over = false;
  if (ActualOver!=NULL) return;
  if (EditorMode) return;
  if (InsideScreen()) {
    if (distance_between_points(mouse_x+RSW(Camera_X), mouse_y+RSW(Camera_Y), RSW(x), RSW(y)) < RSW(w)/2) {
      if (!IsUnconscious()) {
        over = true;
        ActualOver = this;
      }
    }
  }
}

void Person::LoadGraphics(std::string filename) {
  sprites.push_back(AnimatedSprite());
  sprites[0].Load("Person/" + filename + "/Stand");
  sprites.push_back(AnimatedSprite());
  sprites[1].Load("Person/" + filename + "/Walk");
  sprites.push_back(AnimatedSprite());
  sprites[2].Load("Person/" + filename + "/Attack");
  sprites.push_back(AnimatedSprite());
  sprites[3].Load("Person/" + filename + "/Unconscious");
  sprites.push_back(AnimatedSprite());
  sprites[4].Load("Person/" + filename + "/Guard");

  portrait=load_cache_bitmap("Graphics/Person/"+filename+"/Portrait.png");
  if (mine) portrait->get_red_bit();
}



void Person::DrawGeneral() {
  if (InsideScreen()) {
    if (actual != NULL) {
      if (Shadows) {
        COLOR_BLEND(al_map_rgba_f(0, 0, 0, 0.3));
        DRAW_TRANSFORMED(actual->get_bit(), rx+RSW(6), ry+RSH(6), rw, rh, angle, 0);
        NORMAL_BLEND;
      }
    }
    ALLEGRO_BITMAP *bit;
    if (mine) bit = selected_bit;
    else bit = selected_enemy_bit;
    if (morale < -50) bit = selected_coward_bit;


    if ((over) && (!selected)) {
      OPACITY_BLEND(0.4);
      DRAW_TRANSFORMED(bit, rx, ry, rw, rw, 0, 0);
      NORMAL_BLEND;
    }
    if (((!over) && (selected)) || (morale < -50)) {
      OPACITY_BLEND(0.6);
      DRAW_TRANSFORMED(bit, rx, ry, rw, rw, 0, 0);
      NORMAL_BLEND;
    }
    if ((over) && (selected)) {
      OPACITY_BLEND(1.0);
      DRAW_TRANSFORMED(bit, rx, ry, rw, rw, 0, 0);
      NORMAL_BLEND;
    }
    if ((target.x != -1) && (mine)) {
      DRAW_TRANSFORMED(bit, RSW(target.x-Camera_X), RSH(target.y-Camera_Y), RSW(5), RSW(5), 0, 0);
    }
  }


}

void Person::DrawLight() {
  if ((!IsUnconscious()) && (has_torch)) {
    DRAW_TRANSFORMED(lights[0], rx, ry, RSW(300+randn(10)), RSH(300+randn(10)), 0, 0);
  }
}
