#include "KGF.h"



std::deque<VObject *> VisualObjects;

float VisualObject_Cleanup=0;


const int CLEANUP_DELAY=1;

VObject *VObjectZOrder_list=NULL;

void AddVObjectZOrder(VObject *v);

void UpdateVObjectZSorting() {
  std::deque<VObject *>::iterator it;
  VObjectZOrder_list = NULL;
  for (it = VisualObjects.begin(); it!=VisualObjects.end(); it+=1) {
    AddVObjectZOrder((*it));
  }
}


void AddVObjectZOrder(VObject *v) {
   int b, l;
   VObject *iter, *l_iter;
   b = 0;
   l = 0;
   v->set_z_prev(NULL);
   v->set_z_next(NULL);
   if (VObjectZOrder_list == NULL) {
      VObjectZOrder_list = v;
   }
   else {
     iter = VObjectZOrder_list;
      while (l == 0) {
        if (v->GetZ() < iter->GetZ()) {
          if (iter == VObjectZOrder_list) {
            iter->set_z_prev(v);
            v->set_z_next(iter);
            VObjectZOrder_list = v;
          }
          else {
            iter->get_z_prev()->set_z_next(v);
            v->set_z_prev(iter->get_z_prev());
            v->set_z_next(iter);
            iter->set_z_prev(v);
          }
          b = 1;
          break;
        }
        l_iter = iter;
        iter = iter->get_z_next();
        if (iter == NULL) {l = 1; iter = l_iter;}
      }
      if (VObjectZOrder_list->get_z_next()==NULL) iter = VObjectZOrder_list;
      if (!b) {
        v->set_z_prev(iter);
        iter->set_z_next(v);
      }
   }
}



VObject *AddVObject(VObject *v) {
  VisualObjects.push_back(v);
  return v;
}



void DeleteVObject(VObject *v) {
  std::deque<VObject *>::iterator it;
  it=VisualObjects.begin();
  while (it!=VisualObjects.end()) {
    if ((*it) == v) {
      delete v;
      VisualObjects.erase(it);
    }
    else it++;
  }
}


void CleanupVisualObjects() {
  std::deque<VObject *> new_list;
  std::deque<VObject *> del_list;
  new_list.clear();
  del_list.clear();
  std::deque<VObject *>::iterator it;
  for (it=VisualObjects.begin(); it!=VisualObjects.end(); it++) {
    if ((*it)->Dead()) {
      del_list.push_back(*it);
    }
    else {
      new_list.push_back(*it);
    }
  }
  VisualObjects.clear();
  for (it=new_list.begin(); it!=new_list.end(); it++) {
    VisualObjects.push_back(*it);
  }
  for (it=del_list.begin(); it!=del_list.end(); it++) {
    delete (*it);
  }
  new_list.clear();
  del_list.clear();
}

void ClearVisualObjects() {
  std::deque<VObject *>::iterator it;
  for (it=VisualObjects.begin(); it!=VisualObjects.end(); it++) {
    (*it)->SetLife(0);
  }
  CleanupVisualObjects();
}



void UpdateVisualObjects() {
  std::deque<VObject *>::iterator it;
  for (it=VisualObjects.begin(); it!=VisualObjects.end(); it++) {
    (*it)->Update();
  }

  VisualObject_Cleanup -= DeltaTime;
  if (VisualObject_Cleanup <= 0) {
    VisualObject_Cleanup = CLEANUP_DELAY;
    CleanupVisualObjects();
  }

  UpdateVObjectZSorting();
}

void DrawVisualObjects() {
  VObject *it;
  it = VObjectZOrder_list;
  while (it != NULL) {
    it->Draw();
    it = it->get_z_next();
  }
}



void VObject::Update() {
  if (animations.size() > 0) {
    std::deque<Animation *> new_list;
    std::deque<Animation *> del_list;
    new_list.clear();
    del_list.clear();
    std::deque<Animation *>::iterator it;
    for (it=animations.begin(); it!=animations.end(); it++) {
      if ((*it)->life > 0) {
        new_list.push_back(*it);
      }
      else {
        del_list.push_back(*it);
      }
    }
    animations.clear();
    for (it=new_list.begin(); it!=new_list.end(); it++) {
      animations.push_back(*it);
      UpdateAnimation(*it);
    }
    for (it=del_list.begin(); it!=del_list.end(); it++) {
      delete (*it);
    }
    new_list.clear();
    del_list.clear();
  }
  if (Dead()) return;

  if (an_sprite != NULL) {
    an_sprite->Update();
    actual_bit = an_sprite->GetActualBit();
  }

  if (decay) {
    life -= DeltaTime;
    if (life < 0) life = 0;
  }
}


void VObject::ClearAnimationType(enum AnimationType t) {
  std::deque<Animation *> new_list;
  std::deque<Animation *> del_list;
  new_list.clear();
  del_list.clear();
  std::deque<Animation *>::iterator it;
  for (it=animations.begin(); it!=animations.end(); it++) {
    if ((*it)->type == t) {
      del_list.push_back(*it);
    }
    else {
      new_list.push_back(*it);
    }
  }
  animations.clear();
  for (it=new_list.begin(); it!=new_list.end(); it++) {
    animations.push_back(*it);
  }
  for (it=del_list.begin(); it!=del_list.end(); it++) {
    delete (*it);
  }
  new_list.clear();
  del_list.clear();
}



void VObject::UpdateAnimation(Animation *an) {
  an->life -= DeltaTime;
  switch (an->type) {
    case MOVE_X :
      x += (an->target_s/an->duration) * DeltaTime;
      break;
    case MOVE_Y :
      y += (an->target_s/an->duration) * DeltaTime;
      break;
    case SIZE_W :
      w += (an->target_s/an->duration) * DeltaTime;
      break;
    case SIZE_H :
      h += (an->target_s/an->duration) * DeltaTime;
      break;
    case TINT :
      float dr, dg, db, da, br, bg, bb, ba, tr, tg, tb, ta;
      al_unmap_rgba_f(an->target_c, &dr, &dg, &db, &da);
      al_unmap_rgba_f(tint, &br, &bg, &bb, &ba);
      tr = br+dr*DeltaTime;
      tg = bg+dg*DeltaTime;
      tb = bb+db*DeltaTime;
      ta = ba+da*DeltaTime;
      tr = SnapFloat(tr, 0.0, 1.0);
      tg = SnapFloat(tg, 0.0, 1.0);
      tb = SnapFloat(tb, 0.0, 1.0);
      ta = SnapFloat(ta, 0.0, 1.0);
      tint = al_map_rgba_f(tr, tg, tb, ta);
      break;
    case ROTATE :
      angle += an->target_s / an->duration;
      break;
  }
}


void VObject::AnimateMoveTo(float tx, float ty, float d) {
  Animation *an;
  an = new Animation(MOVE_X, d);
  an->target_s = tx-x;
  animations.push_back(an);

  an = new Animation(MOVE_Y, d);
  an->target_s = ty-y;
  animations.push_back(an);

}

void VObject::AnimateMoveX(float t, float d) {
  Animation *an;
  an = new Animation(MOVE_X, d);
  an->target_s = t-x;
  animations.push_back(an);
}

void VObject::AnimateMoveY(float t, float d) {
  Animation *an;
  an = new Animation(MOVE_Y, d);
  an->target_s = t-y;
  animations.push_back(an);
}


void VObject::AnimateScale(float t, float d) {
  AnimateSizeW(or_w*t, d);
  AnimateSizeH(or_h*t, d);
}

void VObject::AnimateSizeW(float t, float d) {
  Animation *an;
  an = new Animation(SIZE_W, d);
  an->target_s = t-w;
  animations.push_back(an);
}

void VObject::AnimateSizeH(float t, float d) {
  Animation *an;
  an = new Animation(SIZE_H, d);
  an->target_s = t-h;
  animations.push_back(an);
}


void VObject::AnimateTint(float dr, float dg, float db, float da, float d) {
  Animation *an;
  an = new Animation(TINT, d);
  float br, bg, bb, ba;
  al_unmap_rgba_f(tint, &br, &bg, &bb, &ba);
  an->target_c = al_map_rgba_f((dr-br)/d, (dg-bg)/d, (db-bb)/d, (da-ba)/d);
  animations.push_back(an);
}

void VObject::AnimateRotate(float dr, float d) {
  Animation *an;
  an = new Animation(ROTATE, d);
  an->target_s = dr;
  animations.push_back(an);
}

void VObject::Draw() {
  if (Dead()) return;
  if (c_x == -1) {
    if (shadow > 0) {
      ColorBlend(al_map_rgba_f(0, 0, 0, 0.3));
      actual_bit->DrawTransformed(RSX(x)+RSX(shadow), RSY(y)+RSY(shadow), RSX(w), RSY(h), angle);
      NormalBlend();
    }

    ColorBlend(tint);
    if (motion_blur) actual_bit->DrawTransformedMotionBlur(RSX(x), RSY(y), RSX(w), RSY(h), angle, 0, Vec2D(x, y)-Vec2D(l_x, l_y), angle-l_angle);
    else actual_bit->DrawTransformed(RSX(x), RSY(y), RSX(w), RSY(h), angle);
    NormalBlend();

  }
  else {
    if (shadow > 0) {
      ColorBlend(al_map_rgba_f(0, 0, 0, 0.3));
      actual_bit->DrawCentered(RSX(x)+RSX(shadow), RSY(y)+RSY(shadow), RSX(w), RSY(h), c_x, c_y, angle);
      NormalBlend();
    }


    ColorBlend(tint);

    if (motion_blur) actual_bit->DrawCenteredMotionBlur(RSX(x), RSY(y), RSX(w), RSY(h), c_x, c_y, angle, 0, Vec2D(x, y)-Vec2D(l_x, l_y), angle-l_angle);
    else actual_bit->DrawCentered(RSX(x), RSY(y), RSX(w), RSY(h), c_x, c_y, angle);

    NormalBlend();
  }

  l_x=x; l_y=y;
  l_angle=angle;
}

VObject::~VObject() {
  std::deque<Animation *>::iterator it;
  for (it=animations.begin(); it!=animations.end(); it++) {
    (*it)->life=0;
  }
  life=0;
  Update();

  if (an_sprite != NULL) delete an_sprite;
}
