Ease :: enum {
   linear;
   quad;
   cubic;
   quart;
   quint;
   expo;
   sine;
   circ;
   back;
   elastic;
}

Transition :: enum {
  in;
  out;
  in_out;
}

/*
   Calculate a tween value for the given process, easing function, and transition.

   The value returned can be used like so:
      progress := 1.0 / time_in_seconds;

      current = original + ease(progress, .cubic, .in_out);

   @Note: Progress must be between 0.0 and 1.0
*/
ease :: (progress: $T, $$ease: Ease = .linear, $$transition: Transition = .in) -> T
#modify { return TypeIsFloat(T); }
{
   p := progress;

   #insert -> string {
      expressions :: string.[
         "p",                                                                        // linear
         "p * p",                                                                    // quad
         "p * p * p",                                                                // cubic
         "p * p * p * p",                                                            // quart
         "p * p * p * p * p",                                                        // quint
         "math.pow(2, (10 * (p - 1)))",                                              // expo
         "-math.cos(p * (math.PI * 0.5)) + 1",                                       // sine
         "-(math.sqrt(1 - (p * p)) - 1)",                                            // circ
         "p * p * (2.7 * p - 1.7)",                                                  // back
         "-math.pow(2, 10 * (p - 1)) * math.sin((p - 1.075) * (math.PI * 2) / 0.3)"  // elastic
      ];

      b: basic.String_Builder;

      if is_constant(ease) basic.append(*b, "#");
      basic.append(*b, "if ease == {\n");

      info := type_info(Ease);
      for info.names {
         basic.print_to_builder(*b, #string END
         case .%1; %2if transition == {
            case .in;
               return %3;

            case .out;
               p = 1 - p;
               return p - (%3);

            case .in_out;
               p *= 2;
               if p < 1 {
                  return 0.5 * (%3);
               }

               p = 2 - p;
               return 0.5 * (1 - (%3)) + 0.5;
         }
         END, it, ifx is_constant(transition) "#" else "", expressions[it_index]);
      }

      basic.append(*b, "}\n");

      return basic.builder_to_string(*b);
   }

   return p;
}


#scope_file;

#import "jc";
math  :: #import "Math"; // @future
basic :: #import "Basic"; // @future
