/* === S Y N F I G ========================================================= */
/*!	\file canvas.h
**	\brief Canvas Class Implementation
**
**	\legal
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**	Copyright (c) 2007, 2008 Chris Moore
**
**	This file is part of Synfig.
**
**	Synfig is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 2 of the License, or
**	(at your option) any later version.
**
**	Synfig is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with Synfig.  If not, see <https://www.gnu.org/licenses/>.
**	\endlegal
*/
/* ========================================================================= */

/* === S T A R T =========================================================== */

#ifndef __SYNFIG_CANVAS_H
#define __SYNFIG_CANVAS_H

/* === H E A D E R S ======================================================= */

#include <map>
#include <list>
#include <ETL/handle>
#include <sigc++/signal.h>
#include <sigc++/connection.h>

#include "vector.h"
#include "string.h"
#include "canvasbase.h"
#include "valuenode.h"
#include "keyframe.h"
#include "renddesc.h"
#include "node.h"
#include "guid.h"
#include "filesystem.h"
#include "rendering/task.h"

/* === M A C R O S ========================================================= */

/* version change history:
 *
 * 0.1: the original version
 *
 *      if a waypoint goes from -179 to 179 degrees, that is a 2
 *      degree change.  there's no way to express a 720 degree
 *      rotation with a single pair of waypoints
 *
 * 0.2: svn r1227
 *
 *      angles no longer wrap at -180 degrees back to 180 degrees; if
 *      a waypoint goes from -179 to 179 degrees, that is a rotation
 *      of 358 degrees.  loading a version 0.1 canvas will modify
 *      constant angle waypoints to that they are within 180 degrees
 *      of the previous waypoint's value
 *
 *      the 'straight' blend method didn't used to work properly.  it
 *      didn't work at all on transparent pixels in layers other than
 *      the PasteCanvas layer.  for example, the examples/japan.sif
 *      file has a red circle (straight, amount=1.0) on top of a
 *      striped conical gradient.  if 'straight' was working, the
 *      conical gradient would be entirely obscured by the circle
 *      layer (even by its transparent pixels)
 *
 * 0.3: svn r1422
 *
 *      the 'straight' blend method was fixed.  loading a version 0.2
 *      or older canvas will replace the 'straight' blend method in
 *      non-pastecanvas layers with 'composite', unless they're
 *      completely transparent, in which case it will replace them
 *      with an  'alpha over' blend instead.  Images like
 *      examples/logo.sif use transparent straight blends to do
 *      masking, which no longer works now that 'straight' blending is
 *      fixed.
 *
 *      Tangent lengths calculated by the "Segment Tangent" and "BLine
 *      Tangent" ValueNodes were scaled by a factor of 0.5.
 *
 * 0.4: svn r1856
 *
 *      Stop scaling tangents by 0.5.
 *
 * 0.5: svn r1863
 *
 *      Added "offset", "scale", and "fixed_length" links to the
 *      "BLine Tangent" ValueNode.
 *
 * 0.6: svn r2067
 *
 *      Added "scale" link to the "BLine Width" ValueNode in svn r1872.
 *
 *      Added "loop" link to the "Gradient Color" ValueNode in svn r1901.
 *
 * 0.7: svn r2315
 *
 *      Added "loop" link to the "Random" ValueNode in svn r2315.
 *
 * 0.8: git 82baee2702a65a9866f3dc4a28ef163dcf43795a
 *
 *      Added "homogeneous" link to "BLineCalcVertex", "BLineCalcTangent"
 *      and "BLineCalcWidth" valuenodes.
 *
 * 0.9: git 6922776b8129fdae6cb42953b2715decc810786c
 *
 *      Added "split_radius" and "split_angle" to BLinePoint Composite
 *      Value Node
 *
 * 1.0: git 06a75a5
 *
 *      Added a canvas component called
 *
 * 1.1: git a4bbdd7
 *
 *      Added gamma into a canvas rend_desc.
 *
 *      Looped outline now uses different order
 *      for calculating positions of widthpoints (see #1284).
 *
 * 1.2: git e2adcad
 *
 *      Added 'alpha' blend method
 *
 */

#define CURRENT_CANVAS_VERSION "1.2"

/* === T Y P E D E F S ===================================================== */

/* === C L A S S E S & S T R U C T S ======================================= */

namespace synfig {

class IndependentContext;
class ContextParams;
class Context;
class GUID;
class Canvas;
class SoundProcessor;

/*!	\class Canvas
**	\brief Canvas is a list of Layers. It is the base class for a Synfig
* document.
*
* As a node, it inherits all the parent child relationship and the GUID
* methods. As a list, it allows insertion and deletion of Layers and can
* access to the layers on the list easily.
*
* Note: the last list element is always a null Layer. It helps iterative
* methods to know when it reaches end of layer list.
*/
class Canvas : private CanvasBase, public Node
{
	/*
 --	** -- T Y P E S -----------------------------------------------------------
	*/

public:
	typedef etl::handle<Canvas> Handle;
	typedef etl::loose_handle<Canvas> LooseHandle;
	typedef etl::handle<const Canvas> ConstHandle;

	typedef std::list<Handle> Children;

	typedef CanvasBase::iterator               iterator;
	typedef CanvasBase::const_iterator         const_iterator;
	typedef CanvasBase::reverse_iterator       reverse_iterator;
	typedef CanvasBase::const_reverse_iterator const_reverse_iterator;

	/*
 --	** -- D A T A -------------------------------------------------------------
	*/

private:

	//! Contains the ID string for the Canvas
	/*!	\see get_id(), set_id() */
	String id_;

	//! Contains the name of the Canvas
	/*!	\see set_name(), get_name() */
	String name_;

	//! Contains a description of the Canvas
	/*!	\see set_description(), get_description() */
	String description_;

	//! Contains the canvas' version string
	/*!	\see set_version(), get_version() */
	String version_;

	//! Contains the author's name
	/*!	\see set_author(), get_author() */
	String author_;

	//! File name of Canvas
	/*! \see get_file_name(), set_file_name() */
	String file_name_;

	//! File identifier of Canvas
	/*! \see get_identifier(), set_identifier() */
	FileSystem::Identifier identifier_;

	//! Metadata map for Canvas.
	/*! \see get_meta_data(), set_meta_data(), erase_meta_data() */
	std::map<String, String> meta_data_;

	//! Contains a list of ValueNodes that are in this Canvas
	/*!	\see value_node_list(), find_value_node() */
	ValueNodeList value_node_list_;

	//! Contains a list of Keyframes that are in the Canvas
	/*! \see keyframe_list()*/
	KeyframeList keyframe_list_;

	//! A handle to the parent canvas of this canvas.
	/*!	If canvas is a root canvas, then this handle is empty
	**	\see parent()
	*/
	LooseHandle parent_;

	//! List containing any child Canvases
	/*!	\see children() */
	Children children_;

	//! Render Description for Canvas
	/*!	\see rend_desc() */
	RendDesc desc_;

	//! Contains the value of the last call to set_time()
	Time cur_time_;

	//! Map of external Canvases used in this Canvas
	mutable std::map<String,Handle> externals_;

	//! This flag is set if this canvas is "inline"
	bool is_inline_;

	//! True if the Canvas properties has changed
	mutable bool is_dirty_;

	//! Layer Group database
	std::map<String,std::set<etl::handle<Layer> > > group_db_;

	//! Layer Signal Connection database.
	//! Required to properly disconnect them when a layer is removed from canvas
	//! (and not necessarily deleted).
	std::map<etl::loose_handle<Layer>,std::vector<sigc::connection> > connections_;

	//! Value to store temporarily the grow value for the child outline type layers
	/*! \see get_grow_value set_grow_value */
	Real outline_grow;


	/*
 -- ** -- S I G N A L S -------------------------------------------------------
	*/

private:

	//!	Group Added
	sigc::signal<void,String> signal_group_added_;

	//!	Group Removed
	sigc::signal<void,String> signal_group_removed_;

	//! Group Changed
	sigc::signal<void,String> signal_group_changed_;

	sigc::signal<void,String,etl::handle<Layer>> signal_group_pair_added_;
	sigc::signal<void,String,etl::handle<Layer>> signal_group_pair_removed_;

	//!	RendDesc Changed
	sigc::signal<void> signal_rend_desc_changed_;

	//!	ID Changed
	sigc::signal<void> signal_id_changed_;

	//!	Dirty
	//sigc::signal<void> signal_dirty_;

	//!	FileName Changed
	sigc::signal<void> signal_file_name_changed_;

	//!	Metadata Changed
	sigc::signal<void, String> signal_meta_data_changed_;

	//! Key-Specific meta data changed signals
	std::map<String, sigc::signal<void> > signal_map_meta_data_changed_;


	//!	ValueBasenode Changed
	sigc::signal<void, ValueNode::Handle> signal_value_node_changed_;
	//!	ValueBasenode Renamed
	sigc::signal<void, ValueNode::Handle> signal_value_node_renamed_;
	//!	Child Value Node Added. Used in Dynamic List Value Nodes
	sigc::signal<void, ValueNode::Handle, ValueNode::Handle> signal_value_node_child_added_;
	//!	Child Value Node Removed. Used in Dynamic List Value Nodes
	sigc::signal<void, ValueNode::Handle, ValueNode::Handle> signal_value_node_child_removed_;

	/*
 -- ** -- S I G N A L   I N T E R F A C E -------------------------------------
	*/

public:

	sigc::signal<void,String,etl::handle<Layer>>& signal_group_pair_added() { return signal_group_pair_added_; }
	sigc::signal<void,String,etl::handle<Layer>>& signal_group_pair_removed() { return signal_group_pair_removed_; }

	//!	Group Added
	sigc::signal<void,String>& signal_group_added() { return signal_group_added_; }

	//!	Group Removed
	sigc::signal<void,String>& signal_group_removed() { return signal_group_removed_; }

	//! Group Changed
	sigc::signal<void,String>& signal_group_changed() { return signal_group_changed_; }

	//!	RendDesc Changed
	sigc::signal<void>& signal_rend_desc_changed() { return signal_rend_desc_changed_; }

	//!	ID Changed
	sigc::signal<void>& signal_id_changed() { return signal_id_changed_; }

	//!	File name Changed
	sigc::signal<void>& signal_file_name_changed();

	//!	Metadata Changed
	sigc::signal<void, String>& signal_meta_data_changed() { return signal_meta_data_changed_; }

	//!	Metadata Changed
	sigc::signal<void>& signal_meta_data_changed(const String& key) { return signal_map_meta_data_changed_[key]; }

	//! Value Node Changed
	sigc::signal<void, ValueNode::Handle>& signal_value_node_changed() { return signal_value_node_changed_; }
	//! Value Node Renamed
	sigc::signal<void, ValueNode::Handle>& signal_value_node_renamed() { return signal_value_node_renamed_; }

	//! Dirty
	sigc::signal<void>& signal_dirty() { return signal_changed();	}

	//! Child Value Node Added
	sigc::signal<void, ValueNode::Handle, ValueNode::Handle>& signal_value_node_child_added() { return signal_value_node_child_added_; }

	//! Child Value Node Removed
	sigc::signal<void, ValueNode::Handle, ValueNode::Handle>& signal_value_node_child_removed() { return signal_value_node_child_removed_; }

	void invoke_signal_value_node_child_removed(ValueNode::Handle, ValueNode::Handle);

	/*
 --	** -- C O N S T R U C T O R S ---------------------------------------------
	*/

protected:
	//! Canvas constructor by Canvas name
	Canvas(const String &name);

public:

	~Canvas();

	/*
 --	** -- M E M B E R   F U N C T I O N S -------------------------------------
	*/

public:

	//! Returns the set of layers in group
	std::set<etl::handle<Layer> > get_layers_in_group(const String&group);

	//! Gets all the groups
	std::set<String> get_groups()const;

	//! Gets the number of groups in this canvas
	int get_group_count()const;

	//! Renames the given group
	void rename_group(const String&old_name,const String&new_name);

	//! Returns true if the Canvas is in line
	bool is_inline()const { return is_inline_; }

	//! Returns a handle to the RendDesc for this Canvas
	RendDesc &rend_desc() { return desc_; }

	//! Returns a handle to the RendDesc for this Canvas
	const RendDesc &rend_desc()const { return desc_; }

	//! Gets the name of the canvas
	const String & get_name()const { return name_; }

	//! Sets the name of the canvas
	void set_name(const String &x);

	//! Gets the version string of the canvas
	const String get_version()const { return version_; }

	//! Sets the version string of the canvas
	void set_version(const String &x) { version_ = x; }

	//! Gets the author of the canvas
	const String & get_author()const { return author_; }

	//! Sets the author of the canvas
	void set_author(const String &x);

	//! Gets the description of the canvas
	const String & get_description()const { return description_; }

	String get_string()const;

	//! Sets the name of the canvas
	void set_description(const String &x);

	//! Gets the ID of the canvas
	const String & get_id()const { return id_; }

	//! Sets the ID of the canvas
	void set_id(const String &x);

	//!	Returns the data string for the given meta data key
	String get_meta_data(const String& key)const;

	//!	Returns a list of meta data keys
	std::list<String> get_meta_data_keys()const;

	//! Sets a meta data key to a specific string
	void set_meta_data(const String& key, const String& data);

	//! Removes a meta data key
	void erase_meta_data(const String& key);

	//! Gets the relative ID string for an ancestor Canvas
	String get_relative_id(etl::loose_handle<const Canvas> x)const;

	//! Gets the relative ID string for an ancestor Canvas. Don't call it directly
	String _get_relative_id(etl::loose_handle<const Canvas> x)const;

	//! Returns \c true if the Canvas is a root Canvas. \c false otherwise
	bool is_root()const { return !parent_; }

	//! Returns a handle to the parent Canvas.
	/*! The returned handle will be empty if this is a root canvas */
	LooseHandle parent()const { return parent_; }

	//! Returns a handle to the root Canvas
	LooseHandle get_root()const;

	//! Starting from itself, search for first non-inline canvas in parent-tree
	LooseHandle get_non_inline_ancestor()const;

	//! Returns a list of all child canvases in this canvas
	std::list<Handle> &children() { return children_; }

	//! Returns a list of all child canvases in this canvas
	const std::list<Handle> &children()const { return children_; }

	//! Gets the color at the specified point
	//Color get_color(const Point &pos)const;

	//! Sets the time for all the layers in the canvas
	void set_time(Time t)const;

	//! Loads resources (frames) for all the external layers in the canvas
	void load_resources(Time t)const;

	//! Returns the current time of the Canvas
	Time get_time()const { return cur_time_; }

	//! Returns the number of layers in the canvas
	int size() const noexcept;

	//! Removes all the layers from the canvas
	void clear() noexcept;

	//! Returns true if the canvas has no layers
	bool empty() const noexcept;

	//! Returns a reference to the ValueNodeList for this Canvas
	// ValueNodeList &value_node_list() { return value_node_list_; }

	//! Returns a reference to the ValueNodeList for this Canvas
	const ValueNodeList &value_node_list()const;

	//! Returns a reference to the KeyframeList for this Canvas
	KeyframeList &keyframe_list();

	//! Returns a reference to the KeyframeList for this Canvas
	const KeyframeList &keyframe_list()const;

	//! Finds the ValueNode in the Canvas with the given \a id
	/*!	\return If found, returns a handle to the ValueNode.
	**		Otherwise, returns an empty handle.
	*/
	ValueNode::Handle find_value_node(const String &id, bool might_fail);

	//! \internal \writeme
	ValueNode::Handle surefind_value_node(const String &id);

	//! Finds the ValueNode in the Canvas with the given \a id
	/*!	\return If found, returns a handle to the ValueNode.
	**		Otherwise, returns an empty handle.
	*/
	ValueNode::ConstHandle find_value_node(const String &id, bool might_fail)const;

	//! Adds a Value node by its Id.
	/*! Throws an error if the Id is not
	//! correct or the Value node is already exported
	**/
	void add_value_node(ValueNode::Handle x, const String &id);

	//! writeme
	//void rename_value_node(ValueNode::Handle x, const String &id);

	//! Removes a Value Node from the Canvas by its Handle
	void remove_value_node(ValueNode::Handle x, bool might_fail);

	//! Removes a Value Node from the Canvas by its Id
	void remove_value_node(const String &id, bool might_fail) { remove_value_node(find_value_node(id, might_fail), might_fail); }

	//! Finds a child Canvas in the Canvas with the given \a name
	/*!	\return If found, returns a handle to the child Canvas.
	**		If not found, it creates a new Canvas and returns it
	**		If an error occurs, it returns an empty handle
	*/
	Handle surefind_canvas(const String &id,String &warnings);

	//! Finds a child Canvas in the Canvas with the given \a id
	/*!	\return If found, returns a handle to the child Canvas.
	**		Otherwise, returns an empty handle.
	*/
	Handle find_canvas(const String &id, String &warnings);

	//! Finds a child Canvas in the Canvas with the given \a id
	/*!	\return If found, returns a handle to the child Canvas.
	**		Otherwise, returns an empty handle.
	*/
	ConstHandle find_canvas(const String &id, String &warnings)const;

	//! Returns the file path from the file name
	String get_file_path()const;

	//! Sets the filename (with path)
	void set_file_name(const String &);

	//! Gets the filename (with path)
	String get_file_name()const;

	//! Gets file_system of the canvas
	FileSystem::Handle get_file_system()const;

	//! Sets the file identifier
	void set_identifier(const FileSystem::Identifier &);

	//! Gets the file identifier
	const FileSystem::Identifier& get_identifier()const;

	//! Creates a new child canvas, and returns its handle
	Handle new_child_canvas();

	//! Creates a new child canvas with an ID of \a id, and returns its handle
	Handle new_child_canvas(const String &id);

	//! Adds the given canvas as a child
	Handle add_child_canvas(Handle child_canvas, const String &id);

	//! Remove Child Canvas by its handle. If Current canvas is a child of a parent
	//! it ask to the parent to remove the Child canvas.
	void remove_child_canvas(Handle child_canvas);

	//! Finds a Layer by its position.
	//! \see get_context()
	etl::handle<Layer> find_layer(const ContextParams &context_params, const Point &pos);

	//! Gets the depth of a particular Layer by its handle
	int get_depth(etl::handle<Layer>)const;

	//! Retrieves the first layer of the list of Layers
	IndependentContext get_independent_context()const;

	//! Retrieves the first layer of the list of Layers assigned with rendering parameters
	Context get_context(const ContextParams &params)const;

	//! Retrieves the first layer of the list of Layers assigned with rendering parameters
	Context get_context(const Context &parent_context)const;

	//! Retrieves sorted list of Layers and Context of the first layer with rendering parameters
	Context get_context_sorted(const ContextParams &params, CanvasBase &out_list) const;

	//! Creates sorted context and builds task for rendering based on it with applied gamma
	rendering::Task::Handle build_rendering_task(const ContextParams &context_params) const;

	int indexof(const const_iterator &iter) const;
	iterator byindex(int index);
	const_iterator byindex(int index) const;

	iterator find_index(const etl::handle<Layer> &layer, int &index);
	const_iterator find_index(const etl::handle<Layer> &layer, int &index) const;

	//! Return the first Canvas layer list iterator.
	/*! It is just a wrap to std::begin() for CanvasBase. */
	iterator begin() noexcept;
	//! Return the first Canvas layer list const_iterator.
	/*! It is just a wrap to std::begin() for CanvasBase. */
	const_iterator begin() const noexcept;
	const_iterator cbegin() const noexcept;
	//! Returns the last Canvas layer list iterator. Notice that it
	/*! overrides the std::end() member that would return an iterator
	 * just past the last element of the list.*/
	iterator end() noexcept;
	//! Returns the last Canvas layer list const_iterator. Notice that it
	/*! overrides the std::end() member that would return an iterator
	 * just past the last element of the list.*/
	const_iterator end() const noexcept;
	const_iterator cend() const noexcept;
	//! Returns the last Canvas layer list reverse iterator. Notice that it
	/*! overrides the std::rbegin() member that would return an iterator
	 * just past the last element of the list.*/
	reverse_iterator rbegin() noexcept;
	//! Returns the last Canvas layer list reverse const iterator. Notice that it
	/*! overrides the std::rbegin() member that would return an iterator
	 * just past the last element of the list.*/
	const_reverse_iterator rbegin() const noexcept;
	const_reverse_iterator crbegin() const noexcept;
	//! Returns the first Canvas layer list reverse iterator.
	/*! It is just a wrap to std::rend() for CanvasBase.*/
	reverse_iterator rend() noexcept;
	//! Returns the first Canvas layer list reverse const iterator.
	/*! It is just a wrap to std::rend() for CanvasBase.*/
	const_reverse_iterator rend() const noexcept;
	const_reverse_iterator crend() const noexcept;
	//! Returns first layer in Canvas layer stack
	etl::handle<Layer> &front();
	//! Returns first layer in Canvas layer stack
	const etl::handle<Layer> &front()const;
	//! Returns last layer in Canvas layer stack
	etl::handle<Layer> &back();
	//! Returns last layer in Canvas layer stack
	const etl::handle<Layer> &back()const;
	//! Inserts a layer just before the last layer.
	//! \see end(), insert(iterator iter,etl::handle<Layer> x)
	void push_back(etl::handle<Layer> x);
	//! Inserts a layer just at the beginning of the Canvas layer list
	void push_front(etl::handle<Layer> x);
	//! Inserts a layer in the last position of the Canvas layer list
	//! Uses the standard methods and doesn't perform any parentship
	//! or signal update
	void push_back_simple(etl::handle<Layer> x);
	//! Inserts a layer before the given position by \iter and performs
	//! the proper child parent relationships and signals update
	void insert(iterator iter,etl::handle<Layer> x);
	//! Removes a layer from the Canvas layer list and its group and parent
	//! relatioship.
	//! It also clears the connections this canvas made on that layer.
	//! see connections_
	void erase(iterator iter);
	//! Sets to be a inline canvas of a given Canvas \parent. The inline
	//! Canvas inherits the groups and the render description.
	//! \see rend_desc()
	void set_inline(LooseHandle parent);
	//! Returns a Canvas handle with "Untitled" as ID
	static Handle create();
	//! Creates an inline Canvas for a given Canvas \parent
	static Handle create_inline(Handle parent);

	//! Clones (copies) the Canvas
	Handle clone(const GUID& deriv_guid=GUID(), bool for_export=false)const;

	//! Stores the external canvas by its file name and the Canvas handle
	void register_external_canvas(String file, Handle canvas);

	//! Set/Get members for the outline grow value
	Real get_outline_grow()const;
	void set_outline_grow(Real x);

#if 0
	void show_canvas_ancestry(String file, int line, String note)const;
	void show_canvas_ancestry()const;
#endif

#ifdef _DEBUG
	void show_externals(String file, int line, String text) const;
	void show_structure(int i) const;
#endif	// _DEBUG

private:
	//! Sets parent and raises on_parent_set event
	void set_parent(const Canvas::LooseHandle &parent);
	//! Adds a \layer to a group given by its \group string to the group
	//! database
	void add_group_pair(String group, etl::handle<Layer> layer);
	//! Removes a \layer from a group given by its \group string to the group
	//! database
	void remove_group_pair(String group, etl::handle<Layer> layer);
	//! Connect layer signals related to layer groups and register these
	//! connections.
	//! \see connections_
	void add_connections(etl::loose_handle<Layer> layer);
	//! Disconnect the stored signals connections of the layer.
	//! \see connections_
	void disconnect_connections(etl::loose_handle<Layer> layer);

protected:
	//! Parent changed
	virtual void on_parent_set();
	//! Sets the Canvas to dirty and calls Node::on_changed()
	virtual void on_changed();
	//! Collects the times (TimePoints) of the Layers of the Canvas and
	//! stores it in the passed Time Set \set
	//! \see Node::get_times()
	virtual void get_times_vfunc(Node::time_set &set) const;

public:
	void fill_sound_processor(SoundProcessor &soundProcessor) const;
}; // END of class Canvas

}; // END of namespace synfig

/* === E N D =============================================================== */

#endif
