/****************************************************************************
Copyright (c) 2008-2010 Ricardo Quesada
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2011      Zynga Inc.
Copyright (c) 2013-2014 Chukong Technologies Inc.

http://www.cocos2d-x.org

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 COCOS_2D_CCAUTOPOLYGON_H__
#define COCOS_2D_CCAUTOPOLYGON_H__

#include <string>
#include <vector>
#include "platform/CCImage.h"
#include "renderer/CCTrianglesCommand.h"

NS_CC_BEGIN

/**
 * @addtogroup _2d
 * @{
 */

/**
 * PolygonInfo is an object holding the required data to display Sprites.
 * It can be a simple as a triangle, or as complex as a whole 3D mesh
 */
class CC_DLL PolygonInfo
{
public:
    /// @name Creators
    /// @{
    /**
     * Creates an empty Polygon info
     * @memberof PolygonInfo
     * @return PolygonInfo object
     */
    PolygonInfo():
    isVertsOwner(true),
    rect(cocos2d::Rect::ZERO),
    filename("")
    {
        triangles.verts = nullptr;
        triangles.indices = nullptr;
        triangles.vertCount = 0;
        triangles.indexCount = 0;
    };
    
    /**
     * Create an polygoninfo from the data of another Polygoninfo
     * @param other     another PolygonInfo to be copied
     * @return duplicate of the other PolygonInfo
     */
    PolygonInfo(const PolygonInfo& other);
    //  end of creators group
    /// @}
    
    /**
     * Copy the member of the other PolygonInfo
     * @param other     another PolygonInfo to be copied
     */
    PolygonInfo& operator= (const PolygonInfo &other);
    ~PolygonInfo();
    
    /**
     * set the data to be a pointer to a quad
     * the member verts will not be released when this PolygonInfo destructs
     * as the verts memory are managed by other objects
     * @param quad  a pointer to the V3F_C4B_T2F_Quad object
     */
    void setQuad(V3F_C4B_T2F_Quad *quad);

    /**
     * get vertex count
     * @return number of vertices
     */
    const unsigned int getVertCount() const;
    
    /**
     * get triangles count
     * @return number of triangles
     */
    const unsigned int getTriaglesCount() const;
    
    /**
     * get sum of all triangle area size
     * @return sum of all triangle area size
     */
    const float getArea() const;
    
    Rect rect;
    std::string filename;
    TrianglesCommand::Triangles triangles;
    
protected:
    bool isVertsOwner;
    
private:
    void releaseVertsAndIndices();
};


/**
 * AutoPolygon is a helper Object
 * AutoPolygon's purpose is to process an image into 2d polygon mesh in runtime
 * It has functions for each step in the process, from tracing all the points, to triangulation
 * the result can be then passed to Sprite::create() to create a Polygon Sprite
 */
class CC_DLL AutoPolygon
{
public:
    /**
     * create an AutoPolygon and initialize it with an image file
     * the image must be a 32bit PNG for current version 3.7
     * @param   filename    a path to image file, e.g., "scene1/monster.png".
     * @return  an AutoPolygon object;
     */
    AutoPolygon(const std::string &filename);
    
    /**
     * Destructor of AutoPolygon.
     */
    ~AutoPolygon();
    
    /**
     * trace all the points along the outline of the image, 
     * @warning must create AutoPolygon with filename to use this function
     * @param   rect    a texture rect for specify an area of the image
     * @param   threshold   the value when alpha is greater than this value will be counted as opaque, default to 0.0
     * @return  a vector of vec2 of all the points found in clockwise order
     * @code
     * auto ap = AutoPolygon("grossini.png");
     * auto rect = Rect(100, 100, 200, 200);
     * std::vector<Vec2> points = ap.trace(rect);//default threshold is 0.0
     * @endcode
     */
     std::vector<Vec2> trace(const cocos2d::Rect& rect, const float& threshold = 0.0);
    
    /**
     * reduce the amount of points so its faster for GPU to process and draw
     * based on Ramer-Douglas-Puecker algorithm
     * @param   points  a vector of Vec2 points as input
     * @param   rect    a texture rect for specify an area of the image to avoid over reduction
     * @param   epsilon the perpendicular distance where points smaller than this value will be discarded
     * @return  a vector of Vec2 of the remaining points in clockwise order
     * @code
     * auto ap = AutoPolygon();
     * std::vector<Vec2> reduced = ap.reduce(inputPoints, rect);//default epsilon is 2
     * @endcode
     */
    std::vector<Vec2> reduce(const std::vector<Vec2>& points, const Rect& rect, const float& epsilon = 2.0);
    
    /**
     * expand the points along their edge, useful after you reduce the points that cuts into the sprite
     * using ClipperLib
     * @param   points  a vector of Vec2 points as input
     * @param   rect    a texture rect for specify an area of the image, the expanded points will be clamped in this rect, ultimately resulting in a quad if the expansion is too great
     * @param   epsilon the distance which the edges will expand
     * @return  a vector of Vec2 as the result of the expansion
     * @code
     * auto ap = AutoPolygon();
     * std::vector<Vec2> expanded = ap.expand(inputPoints, rect, 2.0);
     * @endcode
     */
    std::vector<Vec2> expand(const std::vector<Vec2>& points, const Rect& rect, const float& epsilon);
    
    /**
     * Triangulate the input points into triangles for rendering
     * using poly2tri
     * @warning points must be closed loop, cannot have 2 points sharing the same position and cannot intersect itself
     * @param   points  a vector of vec2 points as input
     * @return  a Triangles object with points and indices
     * @code
     * auto ap = AutoPolygon();
     * TrianglesCommand::Triangles myPolygons = ap.triangulate(myPoints);
     * @endcode
     */
    TrianglesCommand::Triangles triangulate(const std::vector<Vec2>& points);
    
    /**
     * calculate the UV coordinates for each points based on a texture rect
     * @warning This method requires the AutoPolygon object to know the texture file dimension
     * @param   rect    a texture rect to specify where to map the UV
     * @param   verts   a pointer to the verts array, served both as input and output verts
     * @param   count   the count for the verts array
     * @code
     * auto ap = AutoPolygon("grossini.png");
     * TrianglesCommand::Triangles myPolygons = ap.triangulate(myPoints);
     * ap.calculateUV(rect, myPolygons.verts, 20);
     * @endcode
     */
    void calculateUV(const Rect& rect, V3F_C4B_T2F* verts, const ssize_t& count);
    
    /**
     * a helper function, packing trace, reduce, expand, triangulate and calculate uv in one function
     * @param   rect    texture rect, use Rect::ZERO for the size of the texture, default is Rect::ZERO
     * @param   epsilon the value used to reduce and expand, default to 2.0
     * @param   threshold   the value where bigger than the threshold will be counted as opaque, used in trace
     * @return  a PolygonInfo, to use with sprite
     * @code
     * auto ap = AutoPolygon("grossini.png");
     * PolygonInfo myInfo = ap.generateTriangles();//use all default values
     * auto sp1 = Sprite::create(myInfo);
     * polygonInfo myInfo2 = ap.generateTriangles(Rect::ZERO, 5.0, 0.1);//ap can be reused to generate another set of PolygonInfo with different settings
     * auto sp2 = Sprite::create(myInfo2);
     * @endcode
     */
    PolygonInfo generateTriangles(const Rect& rect = Rect::ZERO, const float& epsilon = 2.0, const float& threshold = 0.05);
    
    /**
     * a helper function, packing autoPolygon creation, trace, reduce, expand, triangulate and calculate uv in one function
     * @warning if you want to repetitively generate polygons, consider create an AutoPolygon object, and use generateTriangles function, as it only reads the file once
     * @param   filename     A path to image file, e.g., "scene1/monster.png".
     * @param   rect    texture rect, use Rect::ZERO for the size of the texture, default is Rect::ZERO
     * @param   epsilon the value used to reduce and expand, default to 2.0
     * @param   threshold   the value where bigger than the threshold will be counted as opaque, used in trace
     * @return  a PolygonInfo, to use with sprite
     * @code
     * auto sp = Sprite::create(AutoPolygon::generatePolygon("grossini.png"));
     * @endcode
     */
    static PolygonInfo generatePolygon(const std::string& filename, const Rect& rect = Rect::ZERO, const float epsilon = 2.0, const float threshold = 0.05);
protected:
    Vec2 findFirstNoneTransparentPixel(const Rect& rect, const float& threshold);
    std::vector<cocos2d::Vec2> marchSquare(const Rect& rect, const Vec2& first, const float& threshold);
    unsigned int getSquareValue(const unsigned int& x, const unsigned int& y, const Rect& rect, const float& threshold);

    unsigned char getAlphaByIndex(const unsigned int& i);
    unsigned char getAlphaByPos(const Vec2& pos);

    int getIndexFromPos(const unsigned int& x, const unsigned int& y){return y*_width+x;};
    cocos2d::Vec2 getPosFromIndex(const unsigned int& i){return cocos2d::Vec2(i%_width, i/_width);};

    std::vector<cocos2d::Vec2> rdp(std::vector<cocos2d::Vec2> v, const float& optimization);
    float perpendicularDistance(const cocos2d::Vec2& i, const cocos2d::Vec2& start, const cocos2d::Vec2& end);

    //real rect is the size that is in scale with the texture file
    Rect getRealRect(const Rect& rect);
    
    Image* _image;
    unsigned char * _data;
    std::string _filename;
    unsigned int _width;
    unsigned int _height;
    float _scaleFactor;
    unsigned int _threshold;
};

NS_CC_END

#endif // #ifndef COCOS_2D_CCAUTOPOLYGON_H__