/*************************************************************************/
/*  tween.h                                                              */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                    http://www.godotengine.org                         */
/*************************************************************************/
/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur.                 */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/
#ifndef TWEEN_H
#define TWEEN_H

#include "scene/main/node.h"


class Tween : public Node {

	OBJ_TYPE( Tween, Node );

public:
	enum TweenProcessMode {
		TWEEN_PROCESS_FIXED,
		TWEEN_PROCESS_IDLE,
	};

    enum TransitionType {
        TRANS_LINEAR,
        TRANS_SINE,
        TRANS_QUINT,
        TRANS_QUART,
        TRANS_QUAD,
        TRANS_EXPO,
        TRANS_ELASTIC,
        TRANS_CUBIC,
        TRANS_CIRC,
        TRANS_BOUNCE,
        TRANS_BACK,

		TRANS_COUNT,
    };

    enum EaseType {
        EASE_IN,
        EASE_OUT,
        EASE_IN_OUT,
		EASE_OUT_IN,

		EASE_COUNT,
    };

private:
	enum InterpolateType {

		INTER_PROPERTY,
		INTER_METHOD,
		FOLLOW_PROPERTY,
		FOLLOW_METHOD,
		TARGETING_PROPERTY,
		TARGETING_METHOD,
		INTER_CALLBACK,
	};

	struct InterpolateData {
		bool active;
		InterpolateType type;
		bool finish;
		bool call_deferred;
		real_t elapsed;
		ObjectID id;
		StringName key;
		Variant initial_val;
		Variant delta_val;
		Variant final_val;
		ObjectID target_id;
		StringName target_key;
		real_t times_in_sec;
		TransitionType trans_type;
		EaseType ease_type;
		real_t delay;
		int args;
		Variant arg[5];
	};

	String autoplay;
	TweenProcessMode tween_process_mode;
	bool processing;
	bool active;
	bool repeat;
	float speed_scale;
	mutable int pending_update;

	List<InterpolateData> interpolates;

	struct PendingCommand {
		StringName key;
		int args;
		Variant arg[10];
	};
	List<PendingCommand> pending_commands;

	void _add_pending_command(StringName p_key
		,const Variant& p_arg1=Variant()
		,const Variant& p_arg2=Variant()
		,const Variant& p_arg3=Variant()
		,const Variant& p_arg4=Variant()
		,const Variant& p_arg5=Variant()
		,const Variant& p_arg6=Variant()
		,const Variant& p_arg7=Variant()
		,const Variant& p_arg8=Variant()
		,const Variant& p_arg9=Variant()
		,const Variant& p_arg10=Variant()
	);
	void _process_pending_commands();

	typedef real_t (*interpolater)(real_t t, real_t b, real_t c, real_t d);
	static interpolater interpolaters[TRANS_COUNT][EASE_COUNT];

	real_t _run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d);
	Variant& _get_delta_val(InterpolateData& p_data);
	Variant& _get_initial_val(InterpolateData& p_data);
	Variant _run_equation(InterpolateData& p_data);
	bool _calc_delta_val(const Variant& p_initial_val, const Variant& p_final_val, Variant& p_delta_val);
	bool _apply_tween_value(InterpolateData& p_data, Variant& value);

	void _tween_process(float p_delta);
	void _set_process(bool p_process,bool p_force=false);

protected:

	bool _set(const StringName& p_name, const Variant& p_value);
	bool _get(const StringName& p_name,Variant &r_ret) const;
	void _get_property_list(List<PropertyInfo> *p_list) const;
	void _notification(int p_what);

	static void _bind_methods();

public:

	bool is_active() const;
	void set_active(bool p_active);

	bool is_repeat() const;
	void set_repeat(bool p_repeat);

	void set_tween_process_mode(TweenProcessMode p_mode);
	TweenProcessMode get_tween_process_mode() const;

	void set_speed(float p_speed);
	float get_speed() const;

	bool start();
	bool reset(Object *p_node, String p_key);
	bool reset_all();
	bool stop(Object *p_node, String p_key);
	bool stop_all();
	bool resume(Object *p_node, String p_key);
	bool resume_all();
	bool remove(Object *p_node, String p_key);
	bool remove_all();

	bool seek(real_t p_time);
	real_t tell() const;
	real_t get_runtime() const;

	bool interpolate_property(Object *p_node
		, String p_property
		, Variant p_initial_val
		, Variant p_final_val
		, real_t p_times_in_sec
		, TransitionType p_trans_type
		, EaseType p_ease_type
		, real_t p_delay = 0
	);

	bool interpolate_method(Object *p_node
		, String p_method
		, Variant p_initial_val
		, Variant p_final_val
		, real_t p_times_in_sec
		, TransitionType p_trans_type
		, EaseType p_ease_type
		, real_t p_delay = 0
	);

	bool interpolate_callback(Object *p_object
		, real_t p_times_in_sec
		, String p_callback
		, VARIANT_ARG_DECLARE
	);

	bool interpolate_deferred_callback(Object *p_object
		, real_t p_times_in_sec
		, String p_callback
		, VARIANT_ARG_DECLARE
	);

	bool follow_property(Object *p_node
		, String p_property
		, Variant p_initial_val
		, Object *p_target
		, String p_target_property
		, real_t p_times_in_sec
		, TransitionType p_trans_type
		, EaseType p_ease_type
		, real_t p_delay = 0
	);

	bool follow_method(Object *p_node
		, String p_method
		, Variant p_initial_val
		, Object *p_target
		, String p_target_method
		, real_t p_times_in_sec
		, TransitionType p_trans_type
		, EaseType p_ease_type
		, real_t p_delay = 0
	);

	bool targeting_property(Object *p_node
		, String p_property
		, Object *p_initial
		, String p_initial_property
		, Variant p_final_val
		, real_t p_times_in_sec
		, TransitionType p_trans_type
		, EaseType p_ease_type
		, real_t p_delay = 0
	);

	bool targeting_method(Object *p_node
		, String p_method
		, Object *p_initial
		, String p_initial_method
		, Variant p_final_val
		, real_t p_times_in_sec
		, TransitionType p_trans_type
		, EaseType p_ease_type
		, real_t p_delay = 0
	);

	Tween();
	~Tween();
};

VARIANT_ENUM_CAST( Tween::TweenProcessMode );
VARIANT_ENUM_CAST( Tween::TransitionType );
VARIANT_ENUM_CAST( Tween::EaseType );

#endif