#pragma once

#include "Gdiplus.h"
#include "..\Objects\Structures.h"

namespace NSStructuresGDI
{
	inline void DrawBlurString(int currentBlurSize, Gdiplus::Graphics* Gr, Gdiplus::Font* font, 
		Gdiplus::StringFormat* format, Gdiplus::Brush* brush, BSTR Text, int len, Gdiplus::RectF& rect)
	{
		for(int j = 0; j < currentBlurSize; j++) 
		{
			rect.X += 1;
			Gr->DrawString(Text, len, font, rect, format, brush);
		}
		for(int j = 0; j < currentBlurSize; j++) 
		{
			rect.Y += 1;
			Gr->DrawString(Text, len, font, rect, format, brush);
		}
		for(int j = 0; j < currentBlurSize; j++) 
		{
			rect.X -= 1;
			Gr->DrawString(Text, len, font, rect, format, brush);
		}
		for(int j = 0; j < currentBlurSize; j++) 
		{
			rect.X -= 1;
			Gr->DrawString(Text, len, font, rect, format, brush);
		}
		for(int j = 0; j < currentBlurSize; j++) 
		{
			rect.Y -= 1;
			Gr->DrawString(Text, len, font, rect, format, brush);
		}
		for(int j = 0; j < currentBlurSize; j++) 
		{
			rect.Y -= 1;
			Gr->DrawString(Text, len, font, rect, format, brush);
		}
		for(int j = 0; j < currentBlurSize; j++) 
		{
			rect.X += 1;
			Gr->DrawString(Text, len, font, rect, format, brush);
		}
		for(int j = 0; j < currentBlurSize; j++) 
		{
			rect.X += 1;
			Gr->DrawString(Text, len, font, rect, format, brush);
		}
		for(int j = 0; j < currentBlurSize; j++) 
		{
			rect.Y += 1;
			Gr->DrawString(Text, len, font, rect, format, brush);
		}
	}
	
	class CPen : public NSStructures::CPen
	{
	public:
		CPen() : NSStructures::CPen()
		{
		}
		CPen( const CPen& other ) : NSStructures::CPen(other)
		{
		}
		CPen& operator=(const CPen& other)
		{
			NSStructures::CPen::operator=(other);
			return *this;
		}
		virtual ~CPen()
		{
		}

		Gdiplus::Pen* GetPen()
		{
			Gdiplus::Color color((BYTE)Alpha, GetRValue(Color), GetGValue(Color), GetBValue(Color));

			Gdiplus::Pen* pPen = new Gdiplus::Pen(color, (Gdiplus::REAL)Size);
			pPen->SetDashStyle((Gdiplus::DashStyle)DashStyle);
			pPen->SetStartCap((Gdiplus::LineCap)LineStartCap);
			pPen->SetEndCap((Gdiplus::LineCap)LineEndCap);
			pPen->SetLineJoin((Gdiplus::LineJoin)LineJoin);

			if ((Count != 0) && (DashPattern != NULL))
			{
				Gdiplus::REAL* pPattern = new Gdiplus::REAL[Count];
				for (long i = 0; i < Count; ++i)
				{
					pPattern[i] = (Gdiplus::REAL)DashPattern[i];
				}
				
				pPen->SetDashPattern(pPattern, Count);
				RELEASEARRAYOBJECTS(pPattern);
			}

			pPen->SetDashOffset((float)DashOffset);
			pPen->SetAlignment( (Gdiplus::PenAlignment)Align );

			return pPen;
		}
	};

	class CBrush : public NSStructures::CBrush
	{
	public:
		CBrush() : NSStructures::CBrush()
		{
		}
		CBrush( const CBrush& other ) : NSStructures::CBrush(other)
		{
		}
		CBrush& operator=(const CBrush& other)
		{
			NSStructures::CBrush::operator=(other);
			return *this;
		}
		virtual ~CBrush()
		{
		}

		Gdiplus::Brush* GetBrush()
		{
			// create texture brush
			if (c_BrushTypeTexture == Type || c_BrushTypePattern == Type)
			{
				Gdiplus::TextureBrush* brush = GetTextureBrush();
				return brush;
			}
			// create hatch brush
			else if (Type >= c_BrushTypeHatch1 && Type <= c_BrushTypeHatch53)
			{
				Gdiplus::Color color1((BYTE)Alpha1, GetRValue(Color1), GetGValue(Color1), GetBValue(Color1));
				Gdiplus::Color color2((BYTE)Alpha2, GetRValue(Color2), GetGValue(Color2), GetBValue(Color2));
				Gdiplus::HatchBrush* brush = new Gdiplus::HatchBrush((Gdiplus::HatchStyle)(Type - c_BrushTypeHatch1), color1, color2);

				return brush;
			}
			// create gradient brush (Horizontal, Vertical, Diagonals)
			else if (Type >= c_BrushTypeHorizontal && Type <= c_BrushTypeDiagonal2)
			{
				Gdiplus::LinearGradientBrush* brush = GetLinearGradientBrushHVD(Type - c_BrushTypeHorizontal);
				return brush;
			}
			// create gradient brush (rect with center)
			else if ((c_BrushTypeCenter == Type) || (c_BrushTypePathGradient1 == Type))
			{
				Gdiplus::PathGradientBrush* brush = GetPathGradientBrushCenter();
				return brush;
			}
			// create path gradient brush 2 for letter
			if (c_BrushTypePathGradient2 == Type)
			{   
				Gdiplus::PathGradientBrush* brush = GetPathGradientBrush();
				return brush;
			}

			// create gradient brush (Cylinder Horizontal, Cylinder Vertical)
			else if (Type >= c_BrushTypeCylinderHor && Type <= c_BrushTypeCylinderVer)
			{
				Gdiplus::LinearGradientBrush* brush = GetLinearGradientBrushCHV(Type - c_BrushTypeCylinderHor);
				return brush;
			}

			// create solid brush
			// if (c_BrushTypeSolid == nBrushType)
			Gdiplus::Color color((BYTE)Alpha1, GetRValue(Color1), GetGValue(Color1), GetBValue(Color1));
			Gdiplus::SolidBrush* brush = new Gdiplus::SolidBrush(color);
			return brush;
		}

		Gdiplus::ImageAttributes* GetImageAttributes( int nAlpha )
		{
			if( nAlpha < 0 || nAlpha >= 255 )
				return NULL;

			Gdiplus::REAL alph = (Gdiplus::REAL)(nAlpha / 255.0);
			Gdiplus::ColorMatrix colorMatrix = {	
				1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
				0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
				0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
				0.0f, 0.0f, 0.0f, alph, 0.0f,
				0.0f, 0.0f, 0.0f, 0.0f, 1.0f
			};

			Gdiplus::ImageAttributes* pImageAtt = new Gdiplus::ImageAttributes();
			if( pImageAtt )
				pImageAtt->SetColorMatrix(&colorMatrix, Gdiplus::ColorMatrixFlagsDefault, Gdiplus::ColorAdjustTypeBitmap);

			return pImageAtt;
		}
		void SetTextureBrushTransform( Gdiplus::TextureBrush* pBrush )
		{
			if( !pBrush )
				return;

			Gdiplus::Image* pImage = pBrush->GetImage();
			Gdiplus::RectF  oImageRect;
			Gdiplus::Unit   oImageUnit;
			if( pImage )
			{
				pImage->GetBounds( &oImageRect, &oImageUnit );
				delete pImage;
			}

			pBrush->ResetTransform();

			Gdiplus::PointF oBrushOffset;
			oBrushOffset.X = Rect.X;
			oBrushOffset.Y = Rect.Y;

			switch(TextureMode)
			{
			case c_BrushTextureModeStretch:
				pBrush->ScaleTransform( Rect.Width / oImageRect.Width, Rect.Height / oImageRect.Height );
				break;
			
			case c_BrushTextureModeTileCenter:
				oBrushOffset.X += (Rect.Width - oImageRect.Width)/2;
				oBrushOffset.Y += (Rect.Height - oImageRect.Height)/2;
				pBrush->SetWrapMode( Gdiplus::WrapModeTile );
				break;

			default:
				pBrush->SetWrapMode( Gdiplus::WrapModeTile );
			}

			if( oBrushOffset.X != 0 || oBrushOffset.Y != 0)
			{
				pBrush->TranslateTransform( oBrushOffset.X, oBrushOffset.Y, Gdiplus::MatrixOrderAppend );
			}

		}
		// create texture brush
		Gdiplus::TextureBrush* GetTextureBrush()
		{
			// create texture brush with textureAlpha
			Gdiplus::TextureBrush* pBrush = NULL;

			BSTR bsPath = TexturePath.AllocSysString();
			Gdiplus::Bitmap oTextureBitmap(bsPath);
			SysFreeString(bsPath);
			
			// пытаемся создать текстуру с атрибутами
			Gdiplus::ImageAttributes* pImageAttr = GetImageAttributes( Alpha1 );
			pBrush = new Gdiplus::TextureBrush(&oTextureBitmap, Gdiplus::Rect(0, 0, oTextureBitmap.GetWidth(), oTextureBitmap.GetHeight()), pImageAttr);

			RELEASEOBJECT(pImageAttr);

			SetTextureBrushTransform( pBrush );

			return pBrush;
		}
		// create gradient brush (Horizontal, Vertical, Diagonals)
		Gdiplus::LinearGradientBrush* GetLinearGradientBrushHVD(int type)
		{
			Gdiplus::Color color1((BYTE)Alpha1, GetRValue(Color1), GetGValue(Color1), GetBValue(Color1));
			Gdiplus::Color color2((BYTE)Alpha2, GetRValue(Color2), GetGValue(Color2), GetBValue(Color2));
			Gdiplus::LinearGradientBrush* brush = new Gdiplus::LinearGradientBrush(Rect, color1, color2, (Gdiplus::LinearGradientMode)type);
			if( brush )
			{
				brush->SetWrapMode( Gdiplus::WrapModeTileFlipXY );
			}
			// TODO: brush->SetGammaCorrection(TRUE);
			return brush;
		}
		// create gradient brush (Cylinder Horizontal, Cylinder Vertical)
		Gdiplus::LinearGradientBrush* GetLinearGradientBrushCHV(int type)
		{
			Gdiplus::Color color1((BYTE)Alpha1, GetRValue(Color1), GetGValue(Color1), GetBValue(Color1));
			Gdiplus::Color color2((BYTE)Alpha2, GetRValue(Color2), GetGValue(Color2), GetBValue(Color2));
			
			float angle = 0;

			switch(type)
			{
			case 0: angle = -90; break;
			case 1: angle = -180; break;
			}

			Gdiplus::LinearGradientBrush* brush = new Gdiplus::LinearGradientBrush(Rect, color1, color2, angle);
			if( brush )
			{
				brush->SetBlendBellShape(0.70f);
				brush->SetWrapMode( Gdiplus::WrapModeTileFlipXY );
			}
			return brush;
		}
		// create gradient brush (rect with center)
		Gdiplus::PathGradientBrush* GetPathGradientBrushCenter()
		{
			Gdiplus::Color color1((BYTE)Alpha1, GetRValue(Color1), GetGValue(Color1), GetBValue(Color1));
			Gdiplus::Color color2((BYTE)Alpha2, GetRValue(Color2), GetGValue(Color2), GetBValue(Color2));
			Gdiplus::GraphicsPath path; path.AddRectangle(Rect);
			Gdiplus::PathGradientBrush* brush = new Gdiplus::PathGradientBrush(&path);
			if( brush )
			{
				Gdiplus::Color colors[] = {color1};
				int count = 1;
				brush->SetSurroundColors(colors, &count);
				brush->SetCenterColor(color2);
				brush->SetWrapMode( Gdiplus::WrapModeTileFlipXY );
				// TODO: need add Center coordinate 
			}
			return brush;
		}
		// create path gradient brush2 for letter
		Gdiplus::PathGradientBrush* GetPathGradientBrush(int index = 1)
		{
			Gdiplus::Color color1((BYTE)Alpha1, GetRValue(Color1), GetGValue(Color1), GetBValue(Color1));
			Gdiplus::Color color2((BYTE)Alpha2, GetRValue(Color2), GetGValue(Color2), GetBValue(Color2));
			Gdiplus::GraphicsPath path; path.AddRectangle(Rect);
			Gdiplus::PathGradientBrush* brush = new Gdiplus::PathGradientBrush(&path);

			if( brush )
			{
				int colorsCount = 4;
				Gdiplus::Color colors1[] = {color1, color2, color2, color1};
				Gdiplus::Color colors2[] = {color2, color1, color1, color2};
				Gdiplus::Color* pColors = (index % 2) ? colors2 : colors1;
				Gdiplus::Color& oColor = (index % 2) ? color2 : color1;

				brush->SetSurroundColors(pColors, &colorsCount);
				brush->SetCenterColor(oColor);
				brush->SetWrapMode( Gdiplus::WrapModeTileFlipXY );
			}
			return brush;
		}
	};

	class CFont : public NSStructures::CFont
	{
	public:
		CFont() : NSStructures::CFont()
		{
		}
		CFont( const CFont& other ) : NSStructures::CFont(other)
		{
		}
		CFont& operator=(const CFont& other)
		{
			NSStructures::CFont::operator=(other);
			return *this;
		}
		virtual ~CFont()
		{
		}

		Gdiplus::Font* GetFont(Gdiplus::Unit units = Gdiplus::UnitPixel)
		{
			BSTR family = Name.AllocSysString();

			int style = Gdiplus::FontStyleRegular;

			if (Bold)
				style |= Gdiplus::FontStyleBold;
			if (Italic)
				style |= Gdiplus::FontStyleItalic;
			if (Underline)
				style |= Gdiplus::FontStyleUnderline;
			if (Strikeout)
				style |= Gdiplus::FontStyleStrikeout;

			Gdiplus::Font* font = new Gdiplus::Font(family, (Gdiplus::REAL)Size, style, units);

			SysFreeString(family);
			return font;
		}

		Gdiplus::Font* GetFontBySize(double dSize, Gdiplus::Unit units = Gdiplus::UnitPixel)
		{
			BSTR family = Name.AllocSysString();

			int style = Gdiplus::FontStyleRegular;

			if (Bold)
				style |= Gdiplus::FontStyleBold;
			if (Italic)
				style |= Gdiplus::FontStyleItalic;
			if (Underline)
				style |= Gdiplus::FontStyleUnderline;
			if (Strikeout)
				style |= Gdiplus::FontStyleStrikeout;

			Gdiplus::Font* font = new Gdiplus::Font(family, (Gdiplus::REAL)dSize, style, units);

			SysFreeString(family);
			return font;
		}
	};

	class CShadow : public NSStructures::CShadow
	{
	public:
		CShadow() : NSStructures::CShadow()
		{
		}
		CShadow( const CShadow& other ) : NSStructures::CShadow(other)
		{
		}
		CShadow& operator=(const CShadow& other)
		{
			NSStructures::CShadow::operator=(other);
			return *this;
		}
		virtual ~CShadow()
		{
		}

		void Draw(Gdiplus::Graphics* Gr, Gdiplus::Font* font, Gdiplus::StringFormat* format, BSTR Text, int len, Gdiplus::RectF& rectDraw, BYTE alpha = 0xFF)
		{
			if (!Visible || !Gr)
				return;

			Gdiplus::RectF shadowRect = rectDraw;
			shadowRect.X += float(DistanceX);
			shadowRect.Y += float(DistanceY);

			Gdiplus::Color color((BYTE)(Alpha*alpha/255), GetRValue(Color), GetGValue(Color), GetBValue(Color));
			Gdiplus::SolidBrush solid(color);

			if (BlurSize) 
			{	
				Gdiplus::RectF temp = shadowRect;
				int nBlurSize = int(BlurSize);
				for(int i = 1; i <= nBlurSize + 1; i++) 
				{
					int iAlpha = (int)((double)((Alpha*alpha/255.)/8/(i)/1.1));
					solid.SetColor(Gdiplus::Color((BYTE)iAlpha, GetRValue(Color), GetGValue(Color), GetBValue(Color)));
					DrawBlurString(i, Gr, font, format, &solid, Text, len, temp);
					temp = shadowRect;
				}
			}
			else
				Gr->DrawString(Text, len, font, shadowRect, format, &solid);
		}
	};

	class CBackground : public NSStructures::CBackground
	{
	public:
		CBackground() : NSStructures::CBackground()
		{
		}
		CBackground( const CBackground& other ) : NSStructures::CBackground(other)
		{
		}
		CBackground& operator=(const CBackground& other)
		{
			NSStructures::CBackground::operator=(other);
			return *this;
		}
		virtual ~CBackground()
		{
		}

		void Draw(Gdiplus::Graphics* Gr, Gdiplus::Font* font, Gdiplus::StringFormat* format, BSTR Text, int len, Gdiplus::RectF& rectDraw, BYTE alpha = 0xFF)
		{			
		}
	};

	class CEdgeText : public NSStructures::CEdgeText
	{
	public:
		CEdgeText() : NSStructures::CEdgeText()
		{
		}
		CEdgeText( const CEdgeText& other ) : NSStructures::CEdgeText(other)
		{
		}
		CEdgeText& operator=(const CEdgeText& other)
		{
			NSStructures::CEdgeText::operator=(other);
			return *this;
		}
		virtual ~CEdgeText()
		{
		}

		void Draw(Gdiplus::Graphics* Gr, Gdiplus::Font* font, Gdiplus::StringFormat* format, BSTR Text, int len, Gdiplus::RectF& rectDraw, BYTE alpha = 0xFF)
		{
			if (!Visible || !Gr)
				return;

			if (Visible == -1)
			{
				Gdiplus::Color color((BYTE)((double)(Alpha*alpha/255.), GetRValue(Color), GetGValue(Color), GetBValue(Color)));
				Gdiplus::SolidBrush solid(color);
				Gdiplus::RectF temp = rectDraw;
				for(int i = 1; i <= Dist; i++) 
				{
					int iAlpha = (int)((double)(Alpha*alpha/255./8/(i)/1.1));
					solid.SetColor(Gdiplus::Color((BYTE)iAlpha, GetRValue(Color), GetGValue(Color), GetBValue(Color)));
					DrawBlurString(i, Gr, font, format, &solid, Text, len, temp);
					temp = rectDraw;
				}
			}
			if (Visible == 1)
			{
				// save old smoothing mode and set high quality mode
				Gdiplus::SmoothingMode oldSmoothingMode = Gr->GetSmoothingMode();
				Gr->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);

				// get glyphs path 
				Gdiplus::FontFamily family; font->GetFamily(&family);
				Gdiplus::GraphicsPath path; path.AddString(Text, len, &family, font->GetStyle(), font->GetSize(), rectDraw, format);

				// draw text
				int offset = int(Dist);
				for (int i = 1; i <= offset; i++)
				{
					Gdiplus::Pen pen(Gdiplus::Color((BYTE)(Alpha*i*alpha/(offset*255)), GetRValue(Color), GetGValue(Color), GetBValue(Color)), (Gdiplus::REAL) 2*(offset - i + 1));
					pen.SetLineCap(Gdiplus::LineCapRound, Gdiplus::LineCapRound, Gdiplus::DashCapRound);
					Gr->DrawPath(&pen, &path);
				}

				// restore old smoothing mode
				Gr->SetSmoothingMode(oldSmoothingMode);
			}
		}
	};
}