#ifndef _H_AGK_SHAPE_
#define _H_AGK_SHAPE_

// Common includes
#include "cPhysicsBody.h"
//#include "AGKShader.h"
#include "Globals.h"
#include "uString.h"

//#include "Common.h"

#include <vector>

namespace AGK
{

#define CROP(val,_min,_max)				(min(max(val,_min),_max))
#define ABS(val)						(val < 0 ? -val : val)
#define DEGTORAD(a)						(a*(PI/180.0f))
#define RADTODEG(a)						(a/(PI/180.0f))

#define AGK_SHAPE_VISIBLE				0x00000001
#define AGK_SHAPE_SCREENLOCK			0x00000002
#define AGK_SHAPE_ACTIVE				0x00000004
#define AGK_SHAPE_MANAGED				0x00000008

#define AGK_SHAPE_SYMBOL_CHEFRONARROW		0
#define AGK_SHAPE_SYMBOL_DIRECTIONARROW		1
#define AGK_SHAPE_SYMBOL_NOTCHEDARROW		2
#define AGK_SHAPE_SYMBOL_ARROW				3

#define AGK_SHAPE_SETFILTER_GROUP			1
#define AGK_SHAPE_SETFILTER_CATEGORY		2
#define AGK_SHAPE_SETFILTER_MASK			4
#define AGK_SHAPE_SETFILTER_ALL				7
#define AGK_SHAPE_SETFILTER_SINGLEBITSET	8
#define AGK_SHAPE_SETFILTER_SINGLEBITUNSET	16

	typedef struct cVertex2D {
		float x, y;
		cVertex2D(float x, float y) :x(x), y(y) {}
		cVertex2D() :x(0), y(0) {}
		float SqLength() { return x * x + y * y; }
		//struct cVertex2D& operator=(const b2Vec2& other) { x = other.x; y = other.y; return *this; }
		struct cVertex2D& operator*(float other) { return struct cVertex2D(x * other, y * other); }
		struct cVertex2D& operator+(const struct cVertex2D& other) { return struct cVertex2D(x + other.x, y + other.y); }
		struct cVertex2D& operator-(const struct cVertex2D& other) { return struct cVertex2D(x - other.x, y - other.y); }
		struct cVertex2D& operator/(const struct cVertex2D& other) { return struct cVertex2D(x / other.x, y / other.y); }
		struct cVertex2D& operator+=(const struct cVertex2D& other) { x += other.x; y += other.y; return *this; }
	}cVertex2D;

	typedef struct cColor {
		float r, g, b, a;
	}cColor;

	typedef struct cUV {
		float u, v;
		cUV() :u(0), v(0) {}
		cUV(float u, float v) :u(u), v(v) {}
		struct cUV& operator=(const cVertex2D& other) { u = other.x; v = other.y; return *this; }
	}cUV;

	typedef struct cShapeVertex {
		cVertex2D	position;
		cColor		color;
		cUV			uv;
	}cShapeVertex;

	class cVertexAttrib;
	class cImage;
	class AGKShader;

	class _EXPORT_ cShape : cPhysicsBody
	{
		friend class cShapeMgr;

		UINT iID = 0;
		int iNumVertices = 0;
		int iNumIndices = 0;
		UINT iVertexBufferSize = 0;
		UINT iIndexBufferSize = 0;

		bool bIsDirty = true;
		bool bIsDataOwner = true;
		USHORT* pIndices = NULL;
		float* pVBOVertexBuffer = NULL;
		int iVertexStride = 0;
		UINT iVBOVertices = 0;
		UINT iVBOIndices = 0;
		int iUsedAttribs = 0;
		int iPosAttrib = -1;
		int iColorAttrib = -1;
		int iUVAttrib = -1;

		cVertexAttrib** pAttributes;

		float fMinX = 0;
		float fMaxX = 0;
		float fMinY = 0;
		float fMaxY = 0;
		float fRadius = 0;
		float fUVAnchorX = 0;
		float fUVAnchorY = 0;

		UINT iFlags = NULL;
		float fWidth = 0.0f;
		float fHeight = 0.0f;
		float fPositionX = 0.0f;
		float fPositionY = 0.0f;
		float fScaleV = 1.0f;
		float fScaleH = 1.0f;
		float fRotation = 0.0f;
		float fUVRotation = 0.0f;
		float fDepth = 0.001f;
		int iTransparencyMode = 1;
		int iDrawingMode = 0;

		cImage* pImage = NULL;
		static AGKShader* pDefaultShader;
		AGKShader* pShader = NULL;

		//ePhysicsShape ePhyShape = eNone;

	public:
		//cShape(USHORT numVert, float* verts, USHORT numInd, USHORT* indices);
		cShape();
		~cShape();

		void ClearAttribs();
		void ClearRawVertexData();
		
		void CreateVertices(int numVertices, int numIndices);
		void MakeTriangle(float x1, float y1, float x2, float y2, float x3, float y3);
		void MakeTriangle(float width, float height);
		void MakeRectangle(float width, float height);
		void MakePolygon(float width, float height, UINT edges = 3);
		void MakeStar(float width, float height, float inner, UINT pikes = 3);
		void MakeSymbol(float width, float height, UINT symbol);

		void Load(const uString& file);
		void Save(const uString& file);
		void LoadFromMemory(UINT numVertices, float* verts, float* colors, float* uvs, USHORT numIndices, USHORT* indices);
		void GetVertices(UINT* outCount, cVertex2D** outVertices, cColor** outColor, cUV** outUV);
		void SetVertexBufferDirty() { bIsDirty = true; }
		bool IsManaged() { return (iFlags & AGK_SHAPE_MANAGED) > 0; }

		void BuildUV();
		void BuildColor();
		void CalcBoundingBox();
		void ProcessVertexData(int keepBuffers = 0);
		void CreateVBOBuffer(float* pVertices, USHORT numVertices, UINT vertexStride, USHORT* pIndices, USHORT numIndices, int keepBuffers);
		void PlatformGenBuffers();
		void DeleteLocalVBOBuffer();


		UINT GetID() { return iID; }
		bool GetInScreen();
		bool GetVisible() { return (iFlags & AGK_SHAPE_VISIBLE) > 0; }
		float GetX() { return fPositionX; }
		float GetY() { return fPositionY; }
		float GetScaleX() { return fScaleH; }
		float GetScaleY() { return fScaleV; }
		float GetAngle() { return RADTODEG(fRotation); }
		float GetUVAngle() { return RADTODEG(fUVRotation); }
		float GetWidth() { return fWidth; }
		float GetHeight() { return fHeight; }
		float GetRadius() { return fRadius; }
		float GetDepth() { return fDepth; }
		float GetAnchorX() { return -fMinX; }
		float GetAnchorY() { return -fMinY; }
		void GetVertexPosition(USHORT index, float& outx, float& outy);
		void GetVertexColor(USHORT index, float& outr, float& outg, float& outb, float& outa);
		void GetVertexUV(USHORT index, float& outu, float& outv);
		bool IsFixedToScreen() { return (iFlags & AGK_SHAPE_SCREENLOCK) > 0; }
		float GetScreenX();
		float GetScreenY();

		void SetID(UINT id) { iID = id; }
		void SetVisible(bool mode) { iFlags = mode ? iFlags | AGK_SHAPE_VISIBLE : iFlags & (~AGK_SHAPE_VISIBLE); }
		void SetPosition(float positionX, float positionY) { fPositionX = positionX; fPositionY = positionY; }
		void SetScale(float scaleH, float scaleV) { fScaleH = scaleH; fScaleV = scaleV; }
		void SetAngle(float angle) { fRotation = DEGTORAD(angle); }
		void SetUVAngle(float angle) { fUVRotation = DEGTORAD(angle); }
		void SetImage(cImage* image) { pImage = image; }
		void SetSize(float width, float height);
		void SetDepth(float depth) { fDepth = CROP(depth, 0.0001f, 0.9999f); }
		void MoveAnchor(float movex, float movey);
		void SetUVAnchor(float anchorx, float anchory) { fUVAnchorX = anchorx; fUVAnchorY = anchory; }
		void Ink(float r, float g, float b, float a);
		void SetVertexBuffer(int numVertices, void* vertices, UINT stride, UINT flags);
		void SetIndexBuffer(int numIndices, USHORT* indices);
		void SetVertexPosition(USHORT index, float posx, float posy);
		void SetVertexColor(USHORT index, float r, float g, float b, float a);
		void SetVertexUV(USHORT index, float u, float v);
		void FixToScreen(bool mode) { iFlags = mode ? iFlags | AGK_SHAPE_SCREENLOCK : iFlags & (~AGK_SHAPE_SCREENLOCK); }
		void FixTransformation();

		void Draw();
		void PlatformDraw();
		void Update();

		//	Physics
		void SetPhysicsShapePolygonByIndex(UINT points, UINT pindex, int vindex, int id = -1);
		int AddPhysicsShapePolygon(UINT points, UINT pindex, int vindex);
		//cSprite* GetContactSprite2();
		

		void SetPhysicsOn(cPhysicsBody::eBodyType type);

		// Physics virtual functions
		void SetPhysicsShape(cPhysicsBody::ePhysicsShape shape, int index = -1) override;
		void UpdatePhysics() override;

	private:
		void release_shape();
		USHORT get_nearest_vertex_index(const cVertex2D& from);
		bool is_vertex_in_box(const cVertex2D& tl, const cVertex2D& br, const cVertex2D& v);

		void phy_calc_collision_radius() override;
	};
}

#endif	//	_H_AGK_SHAPE_
