/*
	Neutrino-GUI  -   DBoxII-Project

	Copyright (C) 2001 Steffen Hehn 'McClean'
	Homepage: http://dbox.cyberphoria.org/

	Copyright (C) 2009-2012 Stefan Seyfried

	License: GPL

	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 2 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, write to the Free Software
	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <gui/widget/stringinput.h>

#include <driver/fontrenderer.h>
#include <driver/rcinput.h>
#include <driver/screen_max.h>
#include <driver/display.h>

#include <gui/color.h>

#include <gui/widget/buttons.h>
#include <gui/widget/icons.h>
#include <gui/widget/msgbox.h>

#include <system/helpers.h>

#include <global.h>
#include <neutrino.h>

CStringInput::CStringInput(const neutrino_locale_t Name, std::string* Value, int Size, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, const char * const Valid_Chars, CChangeObserver* Observ, const char * const Icon)
{
	name = Name;
	init(g_Locale->getText(name), Value, Size, Hint_1, Hint_2, Valid_Chars, Observ, Icon);
}

CStringInput::CStringInput(const std::string &Name, std::string *Value, int Size, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, const char * const Valid_Chars, CChangeObserver* Observ, const char * const Icon)
{
	name = NONEXISTANT_LOCALE;
        init(Name, Value, Size, Hint_1, Hint_2, Valid_Chars, Observ, Icon);
}

CStringInput::~CStringInput()
{
	g_RCInput->killTimer (smstimer);
}

void CStringInput::setMinMax(const int min_value, const int max_value)
{
	lower_bound = min_value - 1;
	upper_bound = max_value + 1;
}

#define CStringInputSMSButtonsCount 2
const struct button_label CStringInputSMSButtons[CStringInputSMSButtonsCount] =
{
	{ NEUTRINO_ICON_BUTTON_RED   , LOCALE_STRINGINPUT_CAPS  },
	{ NEUTRINO_ICON_BUTTON_YELLOW, LOCALE_STRINGINPUT_CLEAR }
};

void CStringInput::init(const std::string &Name, std::string *Value, int Size, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, const char * const Valid_Chars, CChangeObserver* Observ, const char * const Icon)
{
	frameBuffer = CFrameBuffer::getInstance();

        head = Name;
        valueString = Value;
	lower_bound = -1;
	upper_bound = -1;
        size =  Size;

        hint_1 = Hint_1;
        hint_2 = Hint_2;
        validchars = Valid_Chars;
        iconfile = Icon ? Icon : "";

        observ = Observ;

#ifdef DEBUG_STRINGINPUT
	printf("HEAD: %s (len: %d)\n", head, strlen(head));
#endif
	hheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getHeight();
	iheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_INFO]->getHeight();
	fheight = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_FOOT]->getHeight() + 6;	// init min buttonbar height
	input_h = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getHeight() + 2;		// font height + border
	input_w = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth("M") + 2;	// hack font width + border
	offset  = 20;

	width = std::max(size*input_w + 2*offset, (int) frameBuffer->getScreenWidth() / 100 * 45);

	int tmp_w = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_TITLE]->getRenderWidth(head);

	if (!(iconfile.empty()))
	{
		int icol_w, icol_h;
		frameBuffer->getIconSize(iconfile.c_str(), &icol_w, &icol_h);
		hheight = std::max(hheight, icol_h + (offset/4));
		tmp_w += icol_w + (offset/2);
	}
	width = std::max(width, tmp_w + offset);

	bheight = input_h + 2*offset;
	if ((hint_1 != NONEXISTANT_LOCALE) || (hint_2 != NONEXISTANT_LOCALE))
	{
		if (hint_1 != NONEXISTANT_LOCALE)
		{
			tmp_w = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_INFO]->getRenderWidth(g_Locale->getText(hint_1));
			width = std::max(width, tmp_w + 2*offset);
			bheight += iheight;
		}
		if (hint_2 != NONEXISTANT_LOCALE)
		{
			tmp_w = g_Font[SNeutrinoSettings::FONT_TYPE_MENU_INFO]->getRenderWidth(g_Locale->getText(hint_2));
			width = std::max(width, tmp_w + 2*offset);
			bheight += iheight;
		}
		bheight += offset;
	}

	height = hheight+ bheight; // space for the possible numpad and the buttonbar will be added in initSMS()
	
	x = getScreenStartX(width);
	y = getScreenStartY(height);
	selected = 0;
	smstimer = 0;
	force_saveScreen = false;
	pixBuf = NULL;
}

void CStringInput::NormalKeyPressed(const neutrino_msg_t key)
{
	if (CRCInput::isNumeric(key))
	{	
		std::string tmp_value = *valueString;
		if (selected >= (int)valueString->length())
			valueString->append(selected - valueString->length() + 1, ' ');
		valueString->at(selected) = validchars[CRCInput::getNumericValue(key)];
		int current_value = atoi(*valueString);
		int tmp = current_value;
		if (lower_bound != -1 || upper_bound != -1)
		{
			if (current_value <= lower_bound)
				current_value = lower_bound + 1;
			else if (current_value >= upper_bound)
				current_value = upper_bound - 1;
			if (tmp != current_value)
				*valueString = to_string(current_value).substr(0, size);
		}
		if( (lower_bound == -1 || upper_bound == -1) || (current_value > 0 && current_value > lower_bound && current_value < upper_bound) ){
			if (selected < (size - 1))
			{
				selected++;
				paintChar(selected - 1);
			}
			if (tmp != current_value)
			{
				for (tmp = 0; tmp < size; tmp++)
					paintChar(tmp);
			}
			else
				paintChar(selected);
		}else{
			*valueString = tmp_value;
		}
	}
}

void CStringInput::keyBackspacePressed(void)
{
	if (selected > 0)
	{
		selected--;
		for (int i = selected; i < size - 1; i++)
		{
			valueString->at(i) = valueString->at(i + 1);
			paintChar(i);
		}
		valueString->at(size - 1) = ' ';
		paintChar(size - 1);
	}
}


void CStringInput::keyRedPressed()
{
	if(lower_bound == -1 || upper_bound == -1){
		if (index(validchars, ' ') != NULL)
		{
			valueString->at(selected) = ' ';

			if (selected < (size - 1))
			{
				selected++;
				paintChar(selected - 1);
			}

			paintChar(selected);
		}
	}
}

void CStringInput::keyYellowPressed()
{
	if(lower_bound == -1 || upper_bound == -1){
		selected=0;
		valueString->assign(valueString->length(), ' ');
		for(int i=0 ; i < size ; i++)
			paintChar(i);
	}
}

void CStringInput::keyBluePressed()
{
	if (((valueString->at(selected) | 32) >= 'a') && ((valueString->at(selected) | 32) <= 'z'))
	{
		char newValue = valueString->at(selected) ^ 32;
		if (index(validchars, newValue) != NULL)
		{
			valueString->at(selected) = newValue;
			paintChar(selected);
		}
	}
}

void CStringInput::keyUpPressed()
{
	int npos = 0;
	std::string tmp_value = *valueString;
	for(int count=0;count<(int)strlen(validchars);count++)
		if(valueString->at(selected)==validchars[count])
			npos = count;
	npos++;
	if(npos>=(int)strlen(validchars))
		npos = 0;
	valueString->at(selected)=validchars[npos];

	int current_value = atoi(*valueString);
	int tmp = current_value;
	if (lower_bound != -1 || upper_bound != -1)
	{
		if (current_value <= lower_bound)
			current_value = lower_bound + 1;
		else if (current_value >= upper_bound)
			current_value = upper_bound - 1;
		if (tmp != current_value)
			*valueString = to_string(current_value).substr(0, size);
	}
	if( (lower_bound == -1 || upper_bound == -1) || (current_value > 0 && current_value > lower_bound && current_value < upper_bound) ){
		if (tmp != current_value)
		{
			for (tmp = 0; tmp < size; tmp++)
				paintChar(tmp);
		}
		else
			paintChar(selected);
	}else{
		*valueString = tmp_value;
	}
}

void CStringInput::keyDownPressed()
{
	int npos = 0;
	std::string tmp_value = *valueString;
	for(int count=0;count<(int)strlen(validchars);count++)
		if(valueString->at(selected)==validchars[count])
			npos = count;
	npos--;
	if(npos<0)
		npos = strlen(validchars)-1;
	valueString->at(selected)=validchars[npos];

	int current_value = atoi(*valueString);
	int tmp = current_value;
	if (lower_bound != -1 || upper_bound != -1)
	{
		if (current_value <= lower_bound)
			current_value = lower_bound + 1;
		else if (current_value >= upper_bound)
			current_value = upper_bound - 1;
		if (tmp != current_value)
			*valueString = to_string(current_value).substr(0, size);
	}
	if( (lower_bound == -1 || upper_bound == -1) || (current_value > 0 && current_value > lower_bound && current_value < upper_bound) ){
		if (tmp != current_value)
		{
			for (tmp = 0; tmp < size; tmp++)
				paintChar(tmp);
		}
		else
			paintChar(selected);
	}else{
		*valueString = tmp_value;
	}
}

void CStringInput::keyLeftPressed()
{
	int old = selected;
	if(selected>0) {
		selected--;
	} else {
		selected = size - 1;
	}
	paintChar(old);
	paintChar(selected);
}

void CStringInput::keyRightPressed()
{
	int old = selected;
	if (selected < (size - 1)) {
		selected++;
	} else
		selected = 0;
	paintChar(old);
	paintChar(selected);
}

void CStringInput::keyMinusPressed()
{
	if(lower_bound == -1 || upper_bound == -1){
		int item = selected;
		while (item < (size -1))
		{
			valueString->at(item) = valueString->at(item+1);
			paintChar(item);
			item++;
		}
		valueString->at(item) = ' ';
		paintChar(item);
	}
}

void CStringInput::keyPlusPressed()
{
	if(lower_bound == -1 || upper_bound == -1){
		int item = size -1;
		while (item > selected)
		{
			valueString->at(item) = valueString->at(item-1);
			paintChar(item);
			item--;
		}
		valueString->at(item) = ' ';
		paintChar(item);
	}
}

std::string &CStringInput::getValue(void)
{
	return *valueString;
}

void CStringInput::forceSaveScreen(bool enable)
{
	force_saveScreen = enable;
	if (!enable && pixBuf) {
		delete[] pixBuf;
		pixBuf = NULL;
	}
}

int CStringInput::exec( CMenuTarget* parent, const std::string & )
{
	neutrino_msg_t      msg;
	neutrino_msg_data_t data;
	int res = menu_return::RETURN_REPAINT;

	std::string oldval = *valueString;
	std::string dispval = *valueString;

	if (parent)
		parent->hide();

	if (size > (int) valueString->length())
		valueString->append(size - valueString->length(), ' ');

	if (pixBuf)
		delete[] pixBuf;
	if (!parent || force_saveScreen) {
		pixBuf = new fb_pixel_t[(width + OFFSET_SHADOW) * (height + OFFSET_SHADOW)];
		if (pixBuf)
			frameBuffer->SaveScreen(x, y, width + OFFSET_SHADOW, height + OFFSET_SHADOW, pixBuf);
	}

	paint();

	uint64_t timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings::TIMING_MENU]);

	bool loop=true;
	while (loop)
	{
		frameBuffer->blit();
		if (*valueString != dispval)
		{
			CVFD::getInstance()->showMenuText(1,valueString->c_str() , selected+1);
			dispval = *valueString;
 		}

		g_RCInput->getMsgAbsoluteTimeout(&msg, &data, &timeoutEnd, true );

		if ( msg <= CRCInput::RC_MaxRC )
			timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings::TIMING_MENU]);

		if ((msg == NeutrinoMessages::EVT_TIMER) && (data == smstimer))
			msg = CRCInput::RC_right;

		if (msg < CRCInput::RC_nokey)
			g_RCInput->killTimer (smstimer);

		if (msg==CRCInput::RC_left)
		{
			keyLeftPressed();
		}
		else if (msg==CRCInput::RC_right)
		{
			keyRightPressed();
		}
		else if (*CRCInput::getUnicodeValue(msg))
		{
			NormalKeyPressed(msg);
		}
		else if (msg==CRCInput::RC_backspace)
		{
			keyBackspacePressed();
		}
		else if (msg==CRCInput::RC_red)
		{
			keyRedPressed();
		}
		else if (msg==CRCInput::RC_yellow)
		{
			keyYellowPressed();
		}
		else if ( (msg==CRCInput::RC_green) && (index(validchars, '.') != NULL))
		{
			valueString->at(selected) = '.';

			if (selected < (size - 1))
			{
				selected++;
				paintChar(selected - 1);
			}

			paintChar(selected);
		}
		else if (msg== CRCInput::RC_blue)
		{
			keyBluePressed();
		}
		else if (msg==CRCInput::RC_up)
		{
			keyUpPressed();
		}
		else if (msg==CRCInput::RC_down)
		{
			keyDownPressed();
		} else if (msg==(neutrino_msg_t)g_settings.key_volumeup)
		{
			keyPlusPressed();
		} else if (msg==(neutrino_msg_t)g_settings.key_volumedown)
		{
			keyMinusPressed();
		}
		else if (msg==CRCInput::RC_ok)
		{
			loop=false;
		}
		else if ( (msg==CRCInput::RC_home) || (msg==CRCInput::RC_timeout) )
		{
			string tmp_name = name == NONEXISTANT_LOCALE ? head : g_Locale->getText(name);
			if ((trim (*valueString) != trim(oldval)) &&
			     (ShowMsg(tmp_name, LOCALE_MESSAGEBOX_DISCARD, CMsgBox::mbrYes, CMsgBox::mbYes | CMsgBox::mbCancel) == CMsgBox::mbrCancel)) {
				timeoutEnd = CRCInput::calcTimeoutEnd(g_settings.timing[SNeutrinoSettings::TIMING_MENU] == 0 ? 0xFFFF : g_settings.timing[SNeutrinoSettings::TIMING_MENU]);
				continue;
			}

			*valueString = oldval;
			loop=false;
			res = menu_return::RETURN_EXIT_REPAINT;
		}
		else if (CNeutrinoApp::getInstance()->listModeKey(msg))
		{
			// do nothing
		}
		else
		{
			int r = handleOthers( msg, data );
			if (r & (messages_return::cancel_all | messages_return::cancel_info))
			{
				res = (r & messages_return::cancel_all) ? menu_return::RETURN_EXIT_ALL : menu_return::RETURN_EXIT;
				loop = false;
			}
			else if ( r & messages_return::unhandled )
			{
				if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & messages_return::cancel_all )
				{
					loop = false;
					res = menu_return::RETURN_EXIT_ALL;
				}
			}
		}
	}

	if (pixBuf)
	{
		frameBuffer->RestoreScreen(x, y, width + OFFSET_SHADOW, height + OFFSET_SHADOW, pixBuf);
		delete[] pixBuf;
		pixBuf = NULL;
		frameBuffer->blit();
	} else
		hide();

	*valueString = trim (*valueString);

        if ( (observ) && (msg==CRCInput::RC_ok) )
        {
                observ->changeNotify(name, (void *) valueString->c_str());
        }
	return res;
}

int CStringInput::handleOthers(const neutrino_msg_t /*msg*/, const neutrino_msg_data_t /*data*/)
{
	return messages_return::unhandled;
}

void CStringInput::hide()
{
	frameBuffer->paintBackgroundBoxRel(x, y, width + OFFSET_SHADOW, height + OFFSET_SHADOW);
	frameBuffer->blit();
}

void CStringInput::paint(bool sms)
{
	frameBuffer->paintBoxRel(x + OFFSET_SHADOW, y + OFFSET_SHADOW, width, height, COL_SHADOW_PLUS_0, RADIUS_LARGE, CORNER_ALL); //round
	frameBuffer->paintBoxRel(x, y + hheight, width, bheight, COL_MENUCONTENT_PLUS_0, sms ? 0 : RADIUS_LARGE, CORNER_BOTTOM);

	CComponentsHeader header(x, y, width, hheight, head, iconfile);
	header.paint(CC_SAVE_SCREEN_NO);

	int tmp_y = y+ hheight+ offset+ input_h+ offset;
	if ((hint_1 != NONEXISTANT_LOCALE) || (hint_2 != NONEXISTANT_LOCALE))
	{
		if (hint_1 != NONEXISTANT_LOCALE)
		{
			tmp_y += iheight;
			g_Font[SNeutrinoSettings::FONT_TYPE_MENU_INFO]->RenderString(x+ offset, tmp_y, width- 2*offset, g_Locale->getText(hint_1), COL_MENUCONTENT_TEXT);
		}
		if (hint_2 != NONEXISTANT_LOCALE)
		{
			tmp_y += iheight;
			g_Font[SNeutrinoSettings::FONT_TYPE_MENU_INFO]->RenderString(x+ offset, tmp_y, width- 2*offset, g_Locale->getText(hint_2), COL_MENUCONTENT_TEXT);
		}
		tmp_y += offset;
	}

	int icol_w = 0;
	int icol_h = 0;
	if (sms)
	{
		frameBuffer->getIconSize(NEUTRINO_ICON_NUMERIC_PAD, &icol_w, &icol_h);
		frameBuffer->paintIcon(NEUTRINO_ICON_NUMERIC_PAD, x + (width/2) - (icol_w/2), tmp_y);

		//buttonbar
		::paintButtons(x, y+ hheight+ bheight, width, CStringInputSMSButtonsCount, CStringInputSMSButtons, width, fheight);
		//::paintButtons(x, y + height, width, 2, CStringInputSMSButtons, width);
	}

	for (int count=0;count<size;count++)
		paintChar(count);
}

void CStringInput::paintChar(int pos)
{
	if(pos<(int)valueString->length())
		paintChar(pos, valueString->at(pos));
}

void CStringInput::paintChar(int pos, const char c)
{
	int xpos = x+ offset+ pos* input_w;
	int ypos = y+ hheight+ offset;

	char ch[2] = {c, 0};

	fb_pixel_t color;
	fb_pixel_t bgcolor;

	getItemColors(color, bgcolor, pos == selected);

	frameBuffer->paintBoxRel(xpos, ypos, input_w, input_h, bgcolor);
	frameBuffer->paintBoxFrame(xpos, ypos, input_w, input_h, 1, COL_MENUCONTENT_PLUS_2);

	int ch_w = g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->getRenderWidth(ch);
	int ch_x = xpos + std::max(input_w/2 - ch_w/2, 0);
	g_Font[SNeutrinoSettings::FONT_TYPE_MENU]->RenderString(ch_x, ypos+ input_h, ch_w, ch, color);
}

CStringInputSMS::CStringInputSMS(const neutrino_locale_t Name, std::string *Value, int Size, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, const char * const Valid_Chars, CChangeObserver* Observ, const char * const Icon)
		: CStringInput(Name, Value, Size, Hint_1, Hint_2, Valid_Chars, Observ, Icon)
{
	initSMS(Valid_Chars);
}

CStringInputSMS::CStringInputSMS(const std::string &Name, std::string *Value, int Size, const neutrino_locale_t Hint_1, const neutrino_locale_t Hint_2, const char * const Valid_Chars, CChangeObserver* Observ, const char * const Icon)
   		: CStringInput(Name, Value, Size, Hint_1, Hint_2, Valid_Chars, Observ, Icon)
{
	initSMS(Valid_Chars);
}

void CStringInputSMS::initSMS(const char * const Valid_Chars)
{
	last_digit = -1;				// no key pressed yet
	const char CharList[10][11] = { "0 -_/()<>=",	// 10 characters
					"1+.,:!?%\\'",  //' for c't search ;)
					"abc2ä",
					"def3",
					"ghi4",
					"jkl5",
					"mno6ö",
					"pqrs7ß",
					"tuv8ü",
					"wxyz9" };

	for (int i = 0; i < 10; i++)
	{
		int j = 0;
		for (int k = 0; k < (int) strlen(CharList[i]); k++)
			if (strchr(Valid_Chars, CharList[i][k]) != NULL)
				Chars[i][j++] = CharList[i][k];
		if (j == 0)
			Chars[i][j++] = ' ';	// prevent empty char lists
		arraySizes[i] = j;
	}

	int icol_w = 0, icol_h = 0;
	frameBuffer->getIconSize(NEUTRINO_ICON_NUMERIC_PAD, &icol_w, &icol_h);
	bheight += icol_h + offset;

	icol_w = icol_h = 0;
	for (int i = 0; i < CStringInputSMSButtonsCount; i++)
	{
		// get max iconsize in buttonbar
		int w = 0, h = 0;
		frameBuffer->getIconSize(CStringInputSMSButtons[i].button, &w, &h);
		icol_w = std::max(icol_w, w);
		icol_h = std::max(icol_h, h);
	}
	fheight = std::max(fheight, icol_h + offset/5);

	height = hheight + bheight + fheight;
	y = getScreenStartY(height);
}

void CStringInputSMS::NormalKeyPressed(const neutrino_msg_t key)
{
	if (CRCInput::isNumeric(key))
	{
		int numericvalue = CRCInput::getNumericValue(key);
		if (last_digit != numericvalue)
		{
			if ((last_digit != -1) &&	// there is a last key
			    (selected < (size- 1)))	// we can shift the cursor one field to the right
			{
				selected++;
				paintChar(selected - 1);
			}
			keyCounter = 0;
		}
		else
			keyCounter = (keyCounter + 1) % arraySizes[numericvalue];
		valueString->at(selected) = Chars[numericvalue][keyCounter];
		last_digit = numericvalue;
		paintChar(selected);
		g_RCInput->killTimer (smstimer);
		smstimer = g_RCInput->addTimer(2*1000*1000);
	}
	else
	{
		valueString->at(selected) = *CRCInput::getUnicodeValue(key);
		keyRedPressed();   /* to lower, paintChar */
		keyRightPressed(); /* last_digit = -1, move to next position */
	}
}

void CStringInputSMS::keyBackspacePressed(void)
{
	last_digit = -1;
	CStringInput::keyBackspacePressed();
}

void CStringInputSMS::keyRedPressed()		// switch between lower & uppercase
{
	if (((valueString->at(selected) | 32) >= 'a') && ((valueString->at(selected) | 32) <= 'z'))
	valueString->at(selected) ^= 32;

	paintChar(selected);
}

void CStringInputSMS::keyYellowPressed()		// clear all
{
	last_digit = -1;
	CStringInput::keyYellowPressed();
}

void CStringInputSMS::keyUpPressed()
{
	last_digit = -1;

	if (selected > 0)
	{
		int lastselected = selected;
		selected = 0;
		paintChar(lastselected);
		paintChar(selected);
	}
}

void CStringInputSMS::keyDownPressed()
{
	last_digit = -1;

	int lastselected = selected;

	selected = size - 1;

	while (valueString->at(selected) == ' ')
	{
		selected--;
		if (selected < 0)
			break;
	}

	if (selected < (size - 1))
		selected++;

	paintChar(lastselected);
	paintChar(selected);
}

void CStringInputSMS::keyLeftPressed()
{
	last_digit = -1;
	CStringInput::keyLeftPressed();
}

void CStringInputSMS::keyRightPressed()
{
	last_digit = -1;
	CStringInput::keyRightPressed();
}

void CStringInputSMS::paint(bool /*unused*/)
{
	CStringInput::paint(true /* sms */);
}

void CPINInput::paintChar(int pos)
{
	CStringInput::paintChar(pos, (valueString->at(pos) == ' ') ? ' ' : '*');
}

int CPINInput::exec( CMenuTarget* parent, const std::string & )
{
	neutrino_msg_t      msg;
	neutrino_msg_data_t data;

	int res = menu_return::RETURN_REPAINT;

	if (parent)
		parent->hide();

	if (size > (int) valueString->length())
		valueString->append(size - valueString->length(), ' ');

	paint();

	bool loop = true;

	while(loop)
	{
		frameBuffer->blit();
		g_RCInput->getMsg( &msg, &data, 300 );

		if (msg==CRCInput::RC_left)
		{
			keyLeftPressed();
		}
		else if (msg==CRCInput::RC_right)
		{
			keyRightPressed();
		}
		else if (CRCInput::isNumeric(msg))
		{
			int old_selected = selected;
			NormalKeyPressed(msg);
			if ( old_selected == ( size- 1 ) )
				loop=false;
		}
		else if ( (msg==CRCInput::RC_up) ||
				  (msg==CRCInput::RC_down) )
		{
			g_RCInput->postMsg( msg, data );
			res = menu_return::RETURN_EXIT;
			loop=false;
		}
		else if ( (msg==CRCInput::RC_home) || (msg==CRCInput::RC_timeout) || (msg==CRCInput::RC_ok) )
		{
			loop=false;
		}
		else
		{
			int r = handleOthers(msg, data);
			if (r & (messages_return::cancel_all | messages_return::cancel_info))
			{
				res = (r & messages_return::cancel_all) ? menu_return::RETURN_EXIT_ALL : menu_return::RETURN_EXIT;
				loop = false;
			}
			else if ( r & messages_return::unhandled )
			{
				if ( CNeutrinoApp::getInstance()->handleMsg( msg, data ) & ( messages_return::cancel_all | messages_return::cancel_info ) )
				{
					loop = false;
					res = menu_return::RETURN_EXIT_ALL;
				}
			}
		}
	}

	hide();

	*valueString = trim (*valueString);

	if ( (observ) && (msg==CRCInput::RC_ok) )
	{
		observ->changeNotify(name, (void *) valueString->c_str());
	}

	return res;
}

void CEnquiryInput::paintChar(int pos)
{
	if (blind)
		CStringInput::paintChar(pos, (valueString->at(pos) == ' ') ? ' ' : '*');
	else
			CStringInput::paintChar(pos, valueString->at(pos));
}

int CPLPINInput::handleOthers(neutrino_msg_t msg, neutrino_msg_data_t data)
{
	int res = messages_return::unhandled;

	if ( msg == NeutrinoMessages::EVT_PROGRAMLOCKSTATUS )
	{
		// trotzdem handlen
		CNeutrinoApp::getInstance()->handleMsg(msg, data);

		if (data != (neutrino_msg_data_t) fsk)
			res = messages_return::cancel_info;
	}

	return res;
}

const char * CPLPINInput::getHint1(void)
{
	if (fsk == 0x100)
	{
		hint_1 = LOCALE_PARENTALLOCK_LOCKEDCHANNEL;
		return g_Locale->getText(hint_1);
	}
	else
	{
		sprintf(hint, g_Locale->getText(LOCALE_PARENTALLOCK_LOCKEDPROGRAM), fsk);
		return hint;
	}
}

#define borderwidth OFFSET_SHADOW // FIXME: do we need border around ??

int CPLPINInput::exec( CMenuTarget* parent, const std::string & )
{
	fb_pixel_t * pixbuf = new fb_pixel_t[(width+ 2* borderwidth) * (height+ 2* borderwidth)];

	if (pixbuf)
		frameBuffer->SaveScreen(x- borderwidth, y- borderwidth, width+ 2* borderwidth, height+ 2* borderwidth, pixbuf);

	// clear border
	frameBuffer->paintBackgroundBoxRel(x- borderwidth, y- borderwidth, width+ 2* borderwidth, borderwidth);
	frameBuffer->paintBackgroundBoxRel(x- borderwidth, y+ height, width+ 2* borderwidth, borderwidth);
	frameBuffer->paintBackgroundBoxRel(x- borderwidth, y, borderwidth, height);
	frameBuffer->paintBackgroundBoxRel(x+ width, y, borderwidth, height);

	int res = CPINInput::exec ( parent, "" );

	if (pixbuf)
	{
		frameBuffer->RestoreScreen(x - borderwidth, y- borderwidth, width+ 2* borderwidth, height+ 2* borderwidth, pixbuf);
		delete[] pixbuf;//Mismatching allocation and deallocation: pixbuf
	}

	return ( res );
}
