#include "geometrytext.h"

namespace VRUT
{

	GeometryText::GeometryText(GEOMETRY_ID _gid) : Geometry(_gid, ClassType), size(1.0f), spacing(1.0f), majorJustify(Justify_Begin), minorJustify(Justify_First), fontFamily(0), fontStyle(0) {}

	GeometryText::GeometryText(const GeometryText &g) : Geometry(g), text(g.text), size(g.size), spacing(g.spacing), majorJustify(g.majorJustify), minorJustify(g.minorJustify),
		fontFamily(g.fontFamily), fontName(g.fontName), fontStyle(g.fontStyle) {}

	void GeometryText::SetText(const wxString &text)
	{
		this->text = text;
	}

	wxString GeometryText::GetText() const
	{
		return text;
	}

	void GeometryText::SetSize(float size)
	{
		this->size = size;
	}

	float GeometryText::GetSize() const
	{
		return size;
	}

	void GeometryText::SetSpacing(float spacing)
	{
		this->spacing = spacing;
	}

	float GeometryText::GetSpacing() const
	{
		return spacing;
	}

	void GeometryText::SetMajorJustify(Justify justify)
	{
		majorJustify = justify;
	}

	GeometryText::Justify GeometryText::GetMajorJustify() const
	{
		return majorJustify;
	}

	void GeometryText::SetMinorJustify(Justify justify)
	{
		minorJustify = justify;
	}

	GeometryText::Justify GeometryText::GetMinorJustify() const
	{
		return minorJustify;
	}

	wxFont GeometryText::GetFont() const
	{
		wxFont font;
		switch (fontFamily)
		{
		case 0://default
			font.SetFamily(wxFONTFAMILY_DEFAULT);
			break;
		case 1://decorative
			font.SetFamily(wxFONTFAMILY_DECORATIVE);
			break;
		case 2://roman (serif)
			font.SetFamily(wxFONTFAMILY_ROMAN);
			break;
		case 3://script (handwriting)
			font.SetFamily(wxFONTFAMILY_SCRIPT);
			break;
		case 4://swiss (sans-serif)
			font.SetFamily(wxFONTFAMILY_SWISS);
			break;
		case 5://modern (fixed pitch),
			font.SetFamily(wxFONTFAMILY_MODERN);
			break;
		case 6://teletype (typewriter)
			font.SetFamily(wxFONTFAMILY_TELETYPE);
			break;
		case 7://custom (fontName is used)
			if (!font.SetFaceName(fontName))
			{
				LOGERROR(wxT("GeometryText::GetFont unable to initialize font with name ")+fontName+wxT(", default font is used"));
				font.SetFamily(wxFONTFAMILY_DEFAULT);
			}
			break;
		default:
			font.SetFamily(wxFONTFAMILY_DEFAULT);
			LOGERROR(wxString::Format(wxT("GeometryText::GetFont unsupported fontFamily (%i), default font is used"), fontFamily));
			break;
		}
		switch (fontStyle&3)
		{
		case 0://normal
			font.SetWeight(wxFONTWEIGHT_NORMAL);
			break;
		case 1://light
			font.SetWeight(wxFONTWEIGHT_LIGHT);
			break;
		case 2://bold
			font.SetWeight(wxFONTWEIGHT_BOLD);
			break;
		case 3://max
			font.SetWeight(wxFONTWEIGHT_MAX);
			break;
		}
		switch ((fontStyle>>2)&3)
		{
		case 0://normal
			font.SetStyle(wxFONTSTYLE_NORMAL);
			break;
		case 1://italic
			font.SetStyle(wxFONTSTYLE_ITALIC);
			break;
		case 2://slant
			font.SetStyle(wxFONTSTYLE_SLANT);
			break;
		case 3://max
			font.SetStyle(wxFONTSTYLE_MAX);
			break;
		}
		if (fontStyle&32)
			font.MakeUnderlined();
		if (fontStyle&64)
			font.MakeStrikethrough();

		return font;
	}

	void GeometryText::BuildAABB()
	{
		// Find number of lines and maximum number of characters in lines
		int number_of_lines = 0;
		int max_of_chars_on_lines = 0;
		if (!text.empty())
		{
			int current_line_size = 0;
			for (size_t i = 0; i < text.size(); i++)
			{
				if (text[i] == wxT('\n'))
				{
					if (max_of_chars_on_lines < current_line_size)		max_of_chars_on_lines = current_line_size;
					current_line_size = 0;
					number_of_lines++;
				}
				else
				{
					current_line_size++;
				}
			}

			// Don't forget the last line
			if (max_of_chars_on_lines < current_line_size)
				max_of_chars_on_lines = current_line_size;

			number_of_lines++;
		}

		// Compute total text width and height
		float width = float(max_of_chars_on_lines) * size;
		float height = float(number_of_lines) * size * spacing * 1.5f;//TODO: why the 1.5 coefficient is needed?

		// Compute aabb using justify
		switch (majorJustify)
		{
		default:
		case Justify_First:
		case Justify_Begin:
			aabb.MinBound.x = 0.0f;
			break;
		case Justify_Middle:
			aabb.MinBound.x = -width*0.5f;
			break;
		case Justify_End:
			aabb.MinBound.x = -width;
			break;
		}
		aabb.MaxBound.x = aabb.MinBound.x + width;

		switch (minorJustify)
		{
		default:
		case Justify_First:
			aabb.MinBound.y = -height + size*spacing;
			break;
		case Justify_Begin:
			aabb.MinBound.y = -height;
			break;
		case Justify_Middle:
			aabb.MinBound.y = -height*0.5f;
			break;
		case Justify_End:
			aabb.MinBound.y = 0.0f;
			break;
		}
		aabb.MaxBound.y = aabb.MinBound.y + height;

		// We may miss some below-baseline parts of glyphs or out-of-glyph parts. To avoid this, I add
		// a half of line height to height and half of glyph width to width, hopefully it is enough.
		aabb.MinBound.x -= size * 0.5f;
		aabb.MaxBound.x += size * 0.5f;
		aabb.MinBound.y -= size * 0.5f;
		aabb.MaxBound.y += size * 0.5f;

		// Z size of the text is naturally zero, but I use a very small value so that the application doesn't go mad
		aabb.MinBound.z = -size * 0.01f;
		aabb.MaxBound.z = size * 0.01f;
	}

	std::vector<Triangle> GeometryText::Triangulate() const
	{
		LOGWARNING(wxT("GeometryText::Triangulate not implemented"));
		return std::vector<Triangle>();
	}

	std::vector<TriangleData> GeometryText::TriangulateData() const
	{
		LOGWARNING(wxT("GeometryText::TriangulateData not implemented"));
		return std::vector<TriangleData>();
	}

	void GeometryText::AddBox(const Vector3 &/*position*/, const Vector3 &/*size*/)
	{
		LOGWARNING(wxT("GeometryText::AddBox not implemented"));
	}

	bool GeometryText::AddGeometry(const Geometry *geom)
	{
		if (!geom)		return true;		// Adding empty geometry is OK

		LOGWARNING(wxT("GeometryText::AddGeometry not implemented"));
		return false;
	}

	bool GeometryText::AddGeometries(const std::vector<Geometry *> &geoms)
	{
		LOGERROR(wxT("GeometryText::AddGeometries is not implemented"));
		return false;
	}

	void GeometryText::Divide(std::vector<Geometry*> &/*segments*/) const
	{
		LOGWARNING(wxT("GeometryText::Divide not implemented"));
	}

	void GeometryText::DivideToPrimitives(std::vector<Geometry*> &/*segments*/) const
	{
		LOGWARNING(wxT("GeometryText::DivideToPrimitives not implemented"));
	}

	void GeometryText::FlipNormals()
	{
		LOGWARNING(wxT("GeometryText::FlipNormals not implemented"));
	}

	void GeometryText::TextureMappingPlanar(const Matrix &/*texMatrix*/)
	{
		LOGWARNING(wxT("GeometryText::TextureMappingPlanar not implemented"));
	}

	void GeometryText::TextureMappingTriplanar(const Matrix &/*texMatrix*/)
	{
		LOGWARNING(wxT("GeometryText::TextureMappingTriplanar not implemented"));
	}

	void GeometryText::TextureMappingSpherical(const Matrix &/*texMatrix*/)
	{
		LOGWARNING(wxT("GeometryText::TextureMappingSpherical not implemented"));
	}

	void GeometryText::Clear()
	{
		text.Clear();
		aabb.SetInvalid();
	}

	bool GeometryText::IsSameAs(const smart_pointer<Geometry> & geo) const
	{
		if (!geo)
			return false;
		if (geo->type != this->type)
			return false;
		const GeometryText *geo_text = SafeStaticCast<const GeometryText *>(geo.getPtr());		// We have checked the type
		return text.IsSameAs(geo_text->text);
	}

	wxString GeometryText::ToString() const
	{
		wxString res = Geometry::ToString()+wxString::Format(wxT("Font: size=%f, spacing=%f, majorJustify=%i, minorJustify=%i, fontFamily=%i, fontStyle=%i, fontName=<"), size, spacing, majorJustify, minorJustify,
			fontFamily, fontStyle)+fontName+wxT(">\nText :")+text;
		return res;
	}

	Geometry * GeometryText::Clone() const
	{
		return new GeometryText(*this);
	}

}
