//	TextLayout.cpp - Class which lays out a string of text for a given font.
//  ----------------------------------------------------------------------------
//	This file is part of 'NiallsAVLib', base code for any kind of audiovisual
//	apps.
//	Copyright (C) 2012  Niall Moody
//	
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
//	----------------------------------------------------------------------------

#include "TextLayout.h"

#include <cassert>

using namespace std;

//------------------------------------------------------------------------------
TextLayout::TextLayout()
{

}

//------------------------------------------------------------------------------
TextLayout::~TextLayout()
{

}

//------------------------------------------------------------------------------
const vector<TextLayout::GlyphPos>& TextLayout::getLayoutForText(const wstring& text,
																 FontMetrics& metrics,
																 float boxWidth,
																 float scale,
																 Justification justification,
																 const std::wstring& fontId,
																 MissingGlyphCallback *callback)
{
	int j, k;
	float y = 0.0f;
	bool foundSpace;
	unsigned int i;
	vector<GlyphPos> line;
	float lineWidth = 0.0f;
	FontMetrics::iterator it;

	//Because passing in 0 for the callback would be bad...
	assert(callback);

	//Make sure our return vector is empty before we start adding more to it.
	glyphPos.clear();

	for(i=0;i<text.length();++i)
	{
		it = metrics.find(text[i]);

		if((text[i] != L'\n') && (it == metrics.end()))
			it = callback->loadMissingGlyph(text[i], metrics, fontId);

		assert((text[i] == L'\n') || (it != metrics.end()));

		//If it's a line break, add the line to glyphPos.
		if(text[i] == L'\n')
		{
			addLine(line, boxWidth, lineWidth, justification);
			lineWidth = 0.0f;
			line.clear();
			y += metrics.begin()->second.lineSkip * scale;
		}
		//Otherwise, add the glyph to line.
		else
		{
			//If the next glyph would make the line wider than the box, don't
			//add it; instead remove all glyphs back to the last whitespace,
			//and add the contents of line to glyphPos.
			if((lineWidth + (it->second.advance * scale)) > boxWidth)
			{
				foundSpace = false;

				//Count back to the first space.
				for(j=((int)line.size()-1);j>=0;--j)
				{
					if(line[j].character == L' ')
					{
						//Update i, remove characters following the space from line.
						i -= (line.size()-j);
						for(k=(int)(line.size()-1);k>=j;--k)
						{
							lineWidth -= line[k].width;
							line.erase(line.begin()+k);
						}

						foundSpace = true;

						break;
					}
				}
				//If there was no space, just break the line at the last character.
				if(!foundSpace)
				{
					i-=2;
					lineWidth -= line[(line.size()-1)].width;
					line.erase(line.begin()+(line.size()-1));
				}

				addLine(line, boxWidth, lineWidth, justification);
				lineWidth = 0.0f;
				line.clear();
				y += it->second.lineSkip * scale;
			}
			else
			{
				GlyphPos glyph;

				glyph.image = it->second.image;
				glyph.position.x = lineWidth;
				glyph.position.y = y + (it->second.lineSkip-it->second.yBearing);
				glyph.position.y = y - it->second.yBearing;
				glyph.character = text[i];
				glyph.width = (-it->second.xOffset + it->second.advance) * scale;
				line.push_back(glyph);
				lineWidth += glyph.width;
			}
		}
	}

	//Add the final line if it exists.
	if(line.size())
		addLine(line, boxWidth, lineWidth, justification);

	return glyphPos;
}

//------------------------------------------------------------------------------
void TextLayout::clear()
{
	glyphPos.clear();
}

//------------------------------------------------------------------------------
void TextLayout::addLine(vector<GlyphPos>& glyphs,
						 float boxWidth,
						 float lineWidth,
						 Justification justification)
{
	unsigned int i;
	float xOffset;

	switch(justification)
	{
		case Left:
			xOffset = 0.0f;
			break;
		case Centre:
			xOffset = (boxWidth-lineWidth)*0.5f;
			break;
		case Right:
			xOffset = boxWidth - lineWidth;
			break;
	}

	for(i=0;i<glyphs.size();++i)
	{
		glyphs[i].position.x += xOffset;

		glyphPos.push_back(glyphs[i]);
	}
}
