// ------------------------------------------------------------------------
/*
	author: Marcos Boyington
   
	Copyright (C) Electronic Arts LA 2004.  All rights reserved.	
*/
// ------------------------------------------------------------------------

/*! \file */

#include <stddef.h>
#include <assert.h>
#include "reveal/input_state.h"
#include "reveal/impl/util.h"
#include "reveal/renderer.h"
#include "reveal/gui_colors.h"
#include "reveal/custom_memory.h"
#include "reveal/impl/window.h"
#include "reveal/impl/gui_metrics.h"
#include "reveal/type_traits/type_traits.h"
#include "reveal/impl/util.h"
#include "reveal/text_window.h"

namespace Reveal {

	namespace Impl {
// ------------------------------------------------------------------------------
/*!
	\brief Prevents TextWindow from being inlined all over the place
	
	If using a text window, be sure to set GuiMetrics::TEXT_HEIGHT and GuiMetrics::CHAR_WIDTH appropriately
*/
// ------------------------------------------------------------------------------

TextWindowBase::TextWindowBase( const char* name, Impl::Tree& tree, size_t numLines )
	: Impl::Window( name, tree )
	, mDisplayStart(0)
	, mBufferPos(0)
	, mNumLines(numLines)
	, mbCircled(false)
{
	SetSize(Impl::GuiMetrics::SCREEN_WIDTH * 2 / 3, Impl::GuiMetrics::SCREEN_HEIGHT * 2 / 3);
	CalcMaxDisplayable();
}

void TextWindowBase::CalcMaxDisplayable ()
{
	//Max displayable lines of text
	mMaxNumLines = (Height() - Impl::GuiMetrics::GetRowHeight() + Impl::GuiMetrics::BORDER_HEIGHT) / (Impl::GuiMetrics::TEXT_HEIGHT + 3);
	mMaxNumChars = (Width() - Impl::GuiMetrics::BORDER_WIDTH - 20) / (Impl::GuiMetrics::GetCharacterWidth());
}

void TextWindowBase::AddLine( const char* line, char buffer[][LINE_LEN] )
{
	strncpy(buffer[mBufferPos], line, LINE_LEN - 1);
	//Circular array, add at beginning when we hit the end
	if (mBufferPos < (mNumLines - 1))
	{
		mBufferPos++;
	}
	else
	{
		mBufferPos = 0;
		mbCircled = true;
	}
}

void TextWindowBase::Clear ()
{
	mbCircled = false;
	mBufferPos = 0;
}

void TextWindowBase::DoRender( const Renderer& renderer, const GuiColors& guiColors, char buffer[][LINE_LEN] ) const
{
	renderer.Bar( X(), Y(), Width(), Height(), 0x66000000 );
	renderer.Bar( X() + Width() - 20, Y() + Height() - 20, 20, 20, 0xFF222222 );
	renderer.Bar( X(), Y(), Width(), Impl::GuiMetrics::GetRowHeight(), 0xFF888888 );
	renderer.String( X() + Impl::GuiMetrics::TEXT_BORDER_WIDTH, Y() + Impl::GuiMetrics::TEXT_BORDER_HEIGHT, Name(), guiColors.mTextColor );

	ClampDisplayStart();

	char temp;
	size_t start = mDisplayStart, lastLine = mMaxNumLines;
	size_t line = 0;
	if (mbCircled)
	{
		if (mBufferPos + mDisplayStart > mNumLines)
			start = mDisplayStart - (mNumLines - mBufferPos) - 1;
		else
			start = 0;

		for ( size_t i = mBufferPos + mDisplayStart; (i < mNumLines) && (line < lastLine); i++, line++ )
		{
			//"Clip" the text to fit in the window
			temp = buffer[i][mMaxNumChars];
			buffer[i][mMaxNumChars] = '\0';

			renderer.String( X() + Impl::GuiMetrics::TEXT_BORDER_WIDTH + 20,
				Y() + Impl::GuiMetrics::GetRowHeight() + Impl::GuiMetrics::TEXT_BORDER_HEIGHT + (Impl::GuiMetrics::TEXT_HEIGHT * static_cast<int>(line)),
				buffer[i], guiColors.mTextColor );

			//Unclip
			buffer[i][mMaxNumChars] = temp;
		}
	}

	for ( size_t i = start; (i < mBufferPos) && (line < lastLine); i++, line++ )
	{
		//"Clip" the text to fit in the window
		temp = buffer[i][mMaxNumChars];
		buffer[i][mMaxNumChars] = '\0';

		renderer.String( X() + Impl::GuiMetrics::TEXT_BORDER_WIDTH + 20,
			Y() + Impl::GuiMetrics::GetRowHeight() + Impl::GuiMetrics::BORDER_HEIGHT + (Impl::GuiMetrics::TEXT_HEIGHT * static_cast<int>(line)),
			buffer[i], guiColors.mTextColor );

		//Unclip
		buffer[i][mMaxNumChars] = temp;
	}

	Window::DoRender( renderer, guiColors );
}

Impl::Widget* TextWindowBase::DoHandle( const InputState& inputState )
{
	Widget* widgetThatHandledTheInput = Window::DoHandle(inputState);

	CalcMaxDisplayable();

	int adjust = 0;

	if ( inputState.Down( InputState::DOWN ) ) 
	{
		adjust = 1;
	}
	else
	if ( inputState.Down( InputState::UP ) ) 
	{					
		adjust = -1;
	}
	else
	if ( inputState.AuxRelY() ) 
	{
		adjust = inputState.AuxRelY();
	}

	if (adjust)
	{
		widgetThatHandledTheInput = this;

		ClampDisplayStart(adjust);
	}

	return widgetThatHandledTheInput;
}

void TextWindowBase::ClampDisplayStart (int adjust) const
{
	//For sign accuracy
	int newStart = (int)mDisplayStart + adjust;
	int numElements = 0;

	if (mbCircled)
		numElements = static_cast<int>(mNumLines);
	else
		numElements = static_cast<int>(mBufferPos);

	if (newStart > (numElements - (int)mMaxNumLines))
		newStart = numElements - (int)mMaxNumLines;
	if (newStart < 0)
		newStart = 0;

	//Is now constrained to > 0
	mDisplayStart = (size_t)newStart;
}

size_t TextWindowBase::DoToString( const char* path, char* buffer, size_t size ) const 
{
	// Does nothing (can't save a text window);
	return 0;
}

} // Namespace Impl
} // Namespace Reveal;
