#include "demo.h"



trans_t * new_trans(trans_t * list, float * dest, float from, float to, int length, int delay)
{
  trans_t * ret = get_new_trans();

  if (list)
  {
    ret->next = list->next;
    list->next = ret;
  }
  else
  {
    ret->next = NULL;
  }

  ret->length = length;
  ret->delay = delay;

  ret->dest_f = dest;
  ret->from_f = from;
  ret->to_f = to;
  
  return ret;
}



trans_t * new_trans_i(trans_t * list, int * dest, int from, int to, int length, int delay)
{
  trans_t * ret = get_new_trans();

  if (list)
  {
    ret->next = list->next;
    list->next = ret;
  }
  else
  {
    ret->next = NULL;
  }

  ret->length = length;
  ret->delay = delay;

  ret->dest_i = dest;
  ret->from_i[0] = from;
  ret->to_i[0] = to;

  return ret;
}



trans_t * new_trans_func(trans_t * list, anim_func_ptr func, void * arg, int length, int delay)
{
  trans_t * ret = get_new_trans();

  if (list)
  {
    ret->next = list->next;
    list->next = ret;
  }
  else
  {
    ret->next = NULL;
  }

  ret->custom_func = func;
  ret->custom_arg = arg;

  ret->length = length;
  ret->delay = delay;

  return ret;
}



trans_t * new_trans_ca(trans_t * list, ALLEGRO_COLOR * dest,
                       int r1, int g1, int b1, int a1,
                       int r2, int g2, int b2, int a2,
                       int length, int delay)
{
  trans_t * ret = get_new_trans();

  if (list)
  {
    ret->next = list->next;
    list->next = ret;
  }
  else
  {
    ret->next = NULL;
  }

  ret->dest_c = dest;
  
  ret->from_i[0] = r1;
  ret->from_i[1] = g1;
  ret->from_i[2] = b1;
  ret->from_i[3] = a1;

  ret->to_i[0] = r2;
  ret->to_i[1] = g2;
  ret->to_i[2] = b2;
  ret->to_i[3] = a2;
  
  ret->length = length;
  ret->delay = delay;

  return ret;
}



trans_t * new_trans_c(trans_t * list, ALLEGRO_COLOR * dest,
                      int r1, int g1, int b1,
                      int r2, int g2, int b2,
                      int length, int delay)
{
  return new_trans_ca(list, dest, r1, g1, b1, 255, r2, g2, b2, 255, length, delay);
}



int run_trans(trans_t * list)
{
  int all_done = true;

  if (list == NULL)
    return all_done;
  
  for (trans_t * tr = list; tr; tr = tr->next)
  {
    if (tr->delay)
    {
      tr->delay--;
      all_done = false;
      continue;
    }
    
    if (tr->frame >= tr->length)
      continue;

    all_done = false;

    ASSERT(tr->length > 0);

    if (tr->custom_func)
    {
      tr->custom_func(tr->custom_arg);
    }
    else
    {
      float input = (float)(tr->frame + 1) / (float)tr->length;
      float curve = curve_coeff(input, tr->curve);

      if (tr->dest_c)
      {
        int value[4];
        
        for (int i = 0; i < 4; i++)
          value[i] = tr->from_i[i] + (tr->to_i[i] - tr->from_i[i]) * curve;
        
        *(tr->dest_c) = al_map_rgba(value[0], value[1], value[2], value[3]);
      }
      else
      {
        if (tr->inertia > 0)
        {
          if (tr->frame >= tr->inertia)
            input = 0;
          else 
            input = (float)(tr->frame + 1) / (float)tr->inertia;
        }
        
        if (tr->inertia)
        {
          if (tr->dest_f)
          {
            float * vel = &tr->vel_f;
          
            if (tr->inertia_chain)
              vel = &tr->inertia_chain->vel_f;
          
            if (tr->frame < tr->inertia)
              *vel += tr->from_f + (tr->to_f - tr->from_f) * curve;
          
            *(tr->dest_f) += *vel;
          }
        }
        else
        {
          if (tr->dest_f)
            *(tr->dest_f) = tr->from_f + (tr->to_f - tr->from_f) * curve;
          else if (tr->dest_i)
            *(tr->dest_i) = tr->from_i[0] + (tr->to_i[0] - tr->from_i[0]) * curve;
        }

      }
    }

    // Common for all types
    tr->frame++;
  }

  return all_done;
}



float curve_coeff(float input, int curve_type)
{
  float ret;
  
  if (curve_type == CURVE_REXP ||
      curve_type == CURVE_RBUMP ||
      curve_type == CURVE_RSQUARE ||
      curve_type == CURVE_RCUBIC)
  {
    input = 1 - input;
  }
  
  switch (curve_type)
  {
    default:
    case CURVE_LINEAR:
      ret = input;
      break;
            
    case CURVE_BUMP:
    case CURVE_RBUMP:
      ret = 1 - (2 * input - 1) * (2 * input - 1);
      break;
            
    case CURVE_SQUARE:
    case CURVE_RSQUARE:
      ret = input * input;
      break;
            
    case CURVE_CUBIC:
    case CURVE_RCUBIC:
      ret = input * input * input;
      break;
            
    case CURVE_EXP:
    case CURVE_REXP:
      ret = pow(input, 10);
      break;
  }

  return ret;
}
