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 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 CStringInput::keyRightPressed()
{
	int old = selected;
	if (selected < (size - 1)) {
		selected++;
	} else
		selected = 0;
	paintChar(old);
	paintChar(selected);
}
void CStringInput::keyLeftPressed()
{
	int old = selected;
	if(selected>0) {
		selected--;
	} else {
		selected = size - 1;
	}
	paintChar(old);
	paintChar(selected);
}
void CStringInputSMS::keyUpPressed()
{
	last_digit = -1;

	if (selected > 0)
	{
		int lastselected = selected;
		selected = 0;
		paintChar(lastselected);
		paintChar(selected);
	}
}
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::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);
	}
}
void BigCharWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter;
    painter.begin(this);

    // paint with default background color
    painter.fillRect(event->rect(), QWidget::palette().color(QWidget::backgroundRole()));

    QPen pen;
    pen.setColor({149,195,244,255});
    pen.setWidth(3);
    pen.setStyle(Qt::PenStyle::SolidLine);

    State *state = State::getInstance();
    quint8* charPtr = state->getCharAtIndex(_charIndex);

    for (int y=0; y<_tileProperties.size.height(); y++) {
        for (int x=0; x<_tileProperties.size.width(); x++) {

            paintChar(painter, pen, charPtr, QPoint{x,y});
            // chars could be 64 chars away from each other
            charPtr +=  _tileProperties.interleaved * 8;
        }
    }

    paintSeparators(painter);
    paintFocus(painter);

    painter.end();
}
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 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 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 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::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::paint(bool sms)
{
	frameBuffer->paintBoxRel(x + SHADOW_OFFSET, y + SHADOW_OFFSET, width, height, COL_MENUCONTENTDARK_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));
}
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(), ' ');

	fb_pixel_t * pixbuf = NULL;
	if (!parent) {
		pixbuf = new fb_pixel_t[(width + SHADOW_OFFSET) * (height + SHADOW_OFFSET)];
		if (pixbuf)
			frameBuffer->SaveScreen(x, y, width + SHADOW_OFFSET, height + SHADOW_OFFSET, 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) )
		{
			if ((*valueString != oldval) &&
			     (ShowMsg(name, LOCALE_MESSAGEBOX_DISCARD, CMessageBox::mbrYes, CMessageBox::mbYes | CMessageBox::mbCancel) == CMessageBox::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 ((msg ==CRCInput::RC_sat) || (msg == CRCInput::RC_favorites))
		{
		}
		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 + SHADOW_OFFSET, height + SHADOW_OFFSET, pixbuf);
		delete[] pixbuf;//Mismatching allocation and deallocation: pixbuf
		frameBuffer->blit();
	} else
		hide();

	*valueString = trim (*valueString);

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