//
//  Layer.h
//  Meteor
//
//  Created by Cesar Parent on 08/08/2015.
//  Copyright (c) 2015 Cesar Parent. All rights reserved.
//

#ifndef __Meteor__Layer__
#define __Meteor__Layer__

#include "Node.h"
#include "../Graphics.h"
#include <functional>
#include <memory>
#include <vector>


NS_BEGIN

class TextureProtocol;

/*!
 * @class           Layer
 * @ingroup         GameObjects
 * @brief           A node that can have other Nodes as children.
 *
 * @details         A Layer can be inserted in a Scene Graph, and can have children of its own.
 *                  Each frame, the layer. When the layer is part of a Scene Graph, it updates all
 *                  of its children and draws them on its texture before drawing itself on its
 *                  parent.
 */
class Layer : public Node {
public:
    
    /*!
     * @name        Creating Layers.
     * @{
     */
    
    /*!
     * @brief       Creates a new Layer object.
     * @param       size        Dimensions of the Layer in pixels.
     */
    Layer(size2 size);
    
    /*!
     * @brief       Creates a new Layer object
     * @param       xSize       Width of the Layer in pixels.
     * @param       ySize       Height of the Layer in pixels.
     */
    Layer(int xSize, int ySize);
    
    /*!
     * @}
     */
    
    virtual ~Layer();
    
    // MARK: -
    // Mark: Scene Graph management.
    
    /*!
     * @name        Working with the Scene Graph
     * @{
     */
    
    /*!
     * @brief       Add a node to the end of the Layer's list of children.
     * @param       child       The node to add.
     */
    void
    addChild(ARC<Node> child);
    
    /*!
     * @brief       Remove all of the Layer's children.
     */
    void
    removeAllChildren();
    
    /*!
     * @brief       Search the Layer's children for a node with a specific name.
     *
     * @details     If more than one child share the same name, the first one found is returned.
     * @param       name        The name to search for.
     * @return      The first node found by the search, or <code>ARC<nullptr></code>.
     * @see         childNodesWithName(const std::string&) const
     */
    ARC<Node>
    childNodeWithName(const std::string& name) const;
    
    /*!
     * @brief       Search the Layer's children for nodes with a specific name and returns an array
     *              containing the results.
     * @param       name        The name to search for.
     * @return      An array of nodes found by the search.
     * @see         childNodeWithName(const std::string&) const
     */
    std::vector<ARC<Node>>
    childNodesWithName(const std::string& name) const;
    
    /*!
     * @brief       Search the Layer's children with a specific name and processes them using the 
     *              given lambda function.
     * @param       name        The name to search for.
     * @param       block       Function called for each node whose name matches the search string.
     *                          The function takes the following arguments:
     *                          @table_start
     *                          @table_row{<em>`node`</em>, A node that matches the name.}
     *                          @table_row{<em>`stop`</em>, Reference to a `bool` variable. Set to
                                                            `true` to stop enumerating.}
     *                          @table_end
     * @see         childNodeWithName(const std::string&) const
     * @see         childNodesWithName(const std::string&) const
     */
    void
    enumerateChildNodesWithName(const std::string& name,
                                std::function<void(ARC<Node> node, bool& stop)> block);
    
    /*!
     * @brief       Sets wether the layer should order its children by z-position.
     * @param       order           wether the layer should order its children by z-position.
     * @see         zSort()
     */
    void
    setZSort(bool order) { _zSort = true; }
    
    /*!
     * @brief       Returns wether the layer orders its children by z-position.
     * @return      Wether the layer orders its children by z-position.
     * @see         setZSort()
     */
    bool
    zSort() const { return _zSort; }
    
    /*!
     * @}
     */
    
    
    // MARK: -
    // MARK: Getters
    
    /*!
     * @name        Inspect the Layer's physical properties
     * @{
     */
    
    /*!
     * @brief       Returns the point in the layer that corresponds to the node’s position.
     * @return      The point in the layer that corresponds to the node’s position.
     */
    vec2
    anchorPoint() const { return _anchorPoint; }
    
    /*!
     * @brief       Returns the origin of the layer's coordinate system.
     * @return      The origin of the layer's coordinate system.
     */
    virtual mat33
    origin() const { return AffineTransform::translate2D(_anchorPoint * (vec2)_size); }
    
    /*!
     * @brief       Returns the layer's dimensions in pixels.
     * @return      The layer's dimensions in pixels.
     */
    size2
    size() const { return _size; }
    
    /*!
     * @brief       Returns the layer texture's color.
     * @return      The layer texture's color.
     */
    Color
    color() const { return target()->color(); }
    
    /*!
     * @brief       Returns the layer's background color.
     * @return      The layer's background color.
     */
    Color
    backgroundColor() const { return _backgroundColor; }
    
    /*!
     * @brief       Return the layer's bounding box.
     * @return      The layer's bounding box.
     */
    Rect
    frame() const;
    
    /*!
     * @}
     */
    
    // MARK: -
    // MARK: Setters
    
    /*!
     * @name        Changing the Layer's physical properties
     * @{
     */
    
    /*!
     * @brief       Sets the point in the layer that corresponds to the node’s position.
     * @param       anchor      Layer's anchor point.
     */
    void
    setAnchorPoint(const vec2& anchor) { _anchorPoint = anchor; _frameDirty = true; }
    
    /*!
     * @brief       Sets the point in the layer that corresponds to the node’s position.
     * @param       x           The anchor point's x coordinate.
     * @param       y           The anchor point's y coordinate.
     */
    void
    setAnchorPoint(float x, float y) { setAnchorPoint({x, y}); }
    
    /*!
     * @brief       Sets the layer's dimensions.
     * @param       size    The layer's dimensions.
     */
    void
    setSize(const size2& size);
    
    /*!
     * @brief   Sets the layer's background color
     * @param   color   The layer's background color.
     */
    void
    setBackgroundColor(const Color& color) { _backgroundColor = color; };
    
    /*!
     * @brief       Sets the layer's texture color.
     * @param       color   The layer's texture color.
     */
    void
    setColor(const Color& color) { target()->setColor(color); }
    
    /*!
     * @}
     */
    
    // MARK: -
    // MARK: Update and Drawing
    
    /*!
     * @internal
     */
    virtual void
    _update(double delta);
    
    /*!
     * @internal
     */
    virtual void
    _draw(RendererProtocol& renderer);
    
    /*!
     * @internal
     */
    virtual void
    draw(RendererProtocol& renderer);
    
protected:
    
    /*!
     * @internal
     */
    virtual TextureProtocol*
    target() const { return _target.raw(); }
    
private:
    
    /*!
     * @internal
     * @brief       Invalidate cached global transform for the node and all its sub-tree.
     */
    void
    invalidateCachedTransform();
    
    /*!
     * @brief       Deletes nodes that were flagged for deletion.
     */
    void
    updateTree();
    
    /*!
     * @brief       Layer's size.
     */
    size2          _size;
    
    /*!
     * @brief       List of the Layer's children.
     */
    std::vector<ARC<Node>> _children;
    
    /*!
     * @brief       Texture the layer will draw on, before rendering it to the
     */
    ARC<TextureProtocol> _target;
    
    /*!
     * @brief       Define swhere in the frame the position points to.
     */
    vec2           _anchorPoint;
    
    /*!
     * @brief       Layer's background color
     */
    Color           _backgroundColor;
    
    /*!
     * @brief       Wether children should be sorted according to their Z position at draw
     *              time.
     */
    bool            _zSort;
    
    /*!
     * @brief       Holds the layer's cached frame.
     */
    mutable Rect    _frame;
    
    /*!
     * @brief       Determines wether the cached frame is valid.
     */
    mutable bool    _frameDirty;
};



NS_END

#endif /* defined(__Meteor__Layer__) */
