/*
	Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
	и других спецификаций для функционирования компактных приложений на языке
	Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

	Copyright © 2016, 2019 Малик Разработчик

	Это свободная программа: вы можете перераспространять ее и/или изменять
	ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
	в каком она была опубликована Фондом свободного программного обеспечения;
	либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

	Эта программа распространяется в надежде, что она будет полезной,
	но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
	или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
	общественной лицензии GNU.

	Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
	вместе с этой программой. Если это не так, см.
	<https://www.gnu.org/licenses/>.
*/


package javax.microedition.lcdui;

import java.lang.ref.*;
import malik.emulator.io.j2me.input.*;
import malik.emulator.media.graphics.*;

public class TextField extends Item
		implements Input
{
	private static final class Destination extends WeakReference
			implements CharDestination
	{
		Destination(TextField thisItem)
		{
			super(thisItem);
		}

		public void insert(char src)
		{
			TextField thisItem;
			if((thisItem = (TextField) get()) == null)
			{
				return;
			}
			thisItem.insert(src);
		}

		public void insert(String src)
		{
			TextField thisItem;
			if((thisItem = (TextField) get()) == null)
			{
				return;
			}
			thisItem.insert(src);
		}
	}

	public static final int ANY = 0;
	public static final int EMAILADDR = 1;
	public static final int NUMERIC = 2;
	public static final int PHONENUMBER = 3;
	public static final int URL = 4;
	public static final int DECIMAL = 5;
	public static final int CONSTRAINT_MASK = 0xffff;
	public static final int PASSWORD = 0x010000;
	public static final int UNEDITABLE = 0x020000;
	public static final int SENSITIVE = 0x040000;
	public static final int NON_PREDICTIVE = 0x080000;
	public static final int INITIAL_CAPS_WORD = 0x100000;
	public static final int INITIAL_CAPS_SENTENCE = 0x200000;


	private boolean controlFocused;
	private boolean pointerFocused;
	private int scrollPosition;
	private Font font;
	private TextInput input;
	private CommandListener capabilities;

	public TextField(String label, String text, int maxLength, int constraints)
	{
		this(LAYOUT_DEFAULT, -1, label, null, null, null, text, maxLength, constraints);
	}

	public TextField(int layout, int lockedWidth, String label,
			Command[] commands, Command defaultCommand, ItemCommandListener listener,
			String text, int maxLength, int constraints)
	{
		super(layout, lockedWidth, -1, label, Displayable.concatCommands(commands,
				TextInput.getAdditionalCapabilitiesCommands(constraints, false)),
				defaultCommand, listener);
		TextInput input;
		this.font = Font.getFont(Font.FONT_INPUT_TEXT);
		this.input = input = new TextInput(text, maxLength, constraints);
		this.capabilities = input.new AdditionalCapabilities(new Destination(this));
	}

	public void insert(String src, int position)
	{
		TextInput input;
		synchronized(input = this.input)
		{
			input.insert(src, position);
			if(src.length() > 0)
			{
				notifyPaint();
			}
		}
	}

	public void insert(char[] src, int offset, int length, int position)
	{
		TextInput input;
		synchronized(input = this.input)
		{
			input.insert(src, offset, length, position);
			if(length > 0)
			{
				notifyPaint();
			}
		}
	}

	public void delete(int offset, int length)
	{
		TextInput input;
		synchronized(input = this.input)
		{
			input.delete(offset, length);
			if(length > 0)
			{
				notifyPaint();
			}
		}
	}

	public void setString(String src)
	{
		TextInput input;
		synchronized(input = this.input)
		{
			input.setString(src);
			notifyPaint();
		}
	}

	public void setChars(char[] src, int offset, int length)
	{
		TextInput input;
		synchronized(input = this.input)
		{
			input.setChars(src, offset, length);
			notifyPaint();
		}
	}

	public void setConstraints(int constraints)
	{
		int oldConstraints;
		TextInput input;
		synchronized(input = this.input)
		{
			if((oldConstraints = input.getConstraints()) != constraints)
			{
				input.setConstraints(oldConstraints);
				removeCommands(TextInput.getAdditionalCapabilitiesCommands(oldConstraints, false));
				addCommands(TextInput.getAdditionalCapabilitiesCommands(constraints, false));
				notifyPaint();
			}
		}
	}

	public void setInitialInputMode(String characterSubset)
	{
		input.setInitialInputMode(characterSubset);
	}

	public int setMaxSize(int maxLength)
	{
		int result;
		TextInput input;
		synchronized(input = this.input)
		{
			if(input.getMaxSize() != maxLength)
			{
				result = input.setMaxSize(maxLength);
				notifyPaint();
			} else
			{
				result = maxLength;
			}
		}
		return result;
	}

	public int size()
	{
		return input.size();
	}

	public int getChars(char[] dst)
	{
		int result;
		TextInput input;
		synchronized(input = this.input)
		{
			result = input.getChars(dst);
		}
		return result;
	}

	public int getConstraints()
	{
		return input.getConstraints();
	}

	public int getCaretPosition()
	{
		return input.getCaretPosition();
	}

	public int getMaxSize()
	{
		return input.getMaxSize();
	}

	public String getString()
	{
		String result;
		TextInput input;
		synchronized(input = this.input)
		{
			result = input.getString();
		}
		return result;
	}

	void paintContent(Graphics render, int contentWidth, int contentHeight)
	{
		boolean cfocused;
		int a;
		int s;
		int x;
		int scrollPosition;
		char[] text;
		Font font;
		TextInput input;
		render.setFont(font = this.font);
		synchronized(input = this.input)
		{
			render.setColor(RasterCanvas.getSystemColor((a = (cfocused = controlFocused) ?
					((input.getConstraints() & UNEDITABLE) != 0 ? 3 : 1) : 0) + 0x20));
			render.drawElementOfGUI(8, a, 0, 0, 0, contentWidth, (s = font.getHeight()) + 4);
			render.translate(2, 2);
			render.clipRect(0, 0, contentWidth - 4, s);
			render.drawChars(text = input.getChars(), 0, input.getLength(),
					-(scrollPosition = this.scrollPosition) + 1, 0, Graphics.LEFT | Graphics.TOP);
			if(cfocused)
			{
				x = font.charsWidth(text, 0, input.getCaretPosition()) - scrollPosition;
				render.drawLine(x, 0, x, s - 1);
			}
		}
	}

	void onKeyPressed(int key, int charCode)
	{
		TextInput input;
		synchronized(input = this.input)
		{
			input.keyPressed(key, charCode);
			if(input.isChanged())
			{
				correctScrollPosition();
				super.notifyStateChanged();
			}
		}
	}

	void onKeyRepeated(int key, int charCode)
	{
		TextInput input;
		synchronized(input = this.input)
		{
			input.keyPressed(key, charCode);
			if(input.isChanged())
			{
				correctScrollPosition();
				super.notifyStateChanged();
			}
		}
	}

	void onKeyReleased(int key)
	{
		input.keyReleased(key);
	}

	void onContentPointerPressed(int x, int y, int button)
	{
		pointerFocused = true;
		pointerHandle(scrollPosition + x - 2);
	}

	void onContentPointerDragged(int x, int y)
	{
		pointerHandle(scrollPosition + x - 2);
	}

	void onContentPointerReleased(int x, int y, int button)
	{
		pointerFocused = false;
	}

	void onCommandAction(Command command)
	{
		if(TextInput.isAdditionalCapabilitiesCommand(command))
		{
			capabilities.commandAction(command, getOwner());
			return;
		}
		super.onCommandAction(command);
	}

	void onTraverseOut()
	{
		controlFocused = false;
		notifyPaint();
	}

	boolean onTraverseNeedStayOn(int direction, int viewportWidth, int viewportHeight,
			int[] visibleRectangle)
	{
		boolean result;
		boolean cfocused;
		int caretPosition;
		TextInput input;
		if((cfocused = controlFocused) != (controlFocused = true))
		{
			notifyPaint();
		}
		if(pointerFocused)
		{
			return true;
		}
		switch(direction)
		{
		case Canvas.UP:
		case Canvas.DOWN:
			return !cfocused;
		case Canvas.LEFT:
			result = false;
			synchronized(input = this.input)
			{
				if(!cfocused)
				{
					input.setCaretPosition(input.getLength());
					correctScrollPosition();
					result = true;
				}
				else if((caretPosition = input.getCaretPosition()) > 0)
				{
					input.setCaretPosition(caretPosition - 1);
					correctScrollPosition();
					result = true;
				}
			}
			return result;
		case Canvas.RIGHT:
			result = false;
			synchronized(input = this.input)
			{
				if(!cfocused)
				{
					input.setCaretPosition(0);
					correctScrollPosition();
					result = true;
				}
				else if((caretPosition = input.getCaretPosition()) < input.getLength())
				{
					input.setCaretPosition(caretPosition + 1);
					correctScrollPosition();
					result = true;
				}
			}
			return result;
		}
		return true;
	}

	boolean keyHandling(int key)
	{
		switch(key)
		{
		default:
			return false;
		case MIDletProxy.KEY_BACKSPACE:
		case MIDletProxy.KEY_SHIFT:
		case MIDletProxy.KEY_SPACE:
		case MIDletProxy.KEY_0:
		case MIDletProxy.KEY_1:
		case MIDletProxy.KEY_2:
		case MIDletProxy.KEY_3:
		case MIDletProxy.KEY_4:
		case MIDletProxy.KEY_5:
		case MIDletProxy.KEY_6:
		case MIDletProxy.KEY_7:
		case MIDletProxy.KEY_8:
		case MIDletProxy.KEY_9:
		case MIDletProxy.KEY_A:
		case MIDletProxy.KEY_B:
		case MIDletProxy.KEY_C:
		case MIDletProxy.KEY_D:
		case MIDletProxy.KEY_E:
		case MIDletProxy.KEY_F:
		case MIDletProxy.KEY_G:
		case MIDletProxy.KEY_H:
		case MIDletProxy.KEY_I:
		case MIDletProxy.KEY_J:
		case MIDletProxy.KEY_K:
		case MIDletProxy.KEY_L:
		case MIDletProxy.KEY_M:
		case MIDletProxy.KEY_N:
		case MIDletProxy.KEY_O:
		case MIDletProxy.KEY_P:
		case MIDletProxy.KEY_Q:
		case MIDletProxy.KEY_R:
		case MIDletProxy.KEY_S:
		case MIDletProxy.KEY_T:
		case MIDletProxy.KEY_U:
		case MIDletProxy.KEY_V:
		case MIDletProxy.KEY_W:
		case MIDletProxy.KEY_X:
		case MIDletProxy.KEY_Y:
		case MIDletProxy.KEY_Z:
		case MIDletProxy.KEY_NUM_0:
		case MIDletProxy.KEY_NUM_1:
		case MIDletProxy.KEY_NUM_2:
		case MIDletProxy.KEY_NUM_3:
		case MIDletProxy.KEY_NUM_4:
		case MIDletProxy.KEY_NUM_5:
		case MIDletProxy.KEY_NUM_6:
		case MIDletProxy.KEY_NUM_7:
		case MIDletProxy.KEY_NUM_8:
		case MIDletProxy.KEY_NUM_9:
		case MIDletProxy.KEY_NUM_STAR:
		case MIDletProxy.KEY_NUM_PLUS:
		case MIDletProxy.KEY_NUM_SEPARATOR:
		case MIDletProxy.KEY_NUM_MINUS:
		case MIDletProxy.KEY_NUM_DECIMAL:
		case MIDletProxy.KEY_NUM_DIVIDE:
		case MIDletProxy.KEY_LSHIFT:
		case MIDletProxy.KEY_RSHIFT:
		case MIDletProxy.KEY_SEMICOLON:
		case MIDletProxy.KEY_EQUAL:
		case MIDletProxy.KEY_COMMA:
		case MIDletProxy.KEY_MINUS:
		case MIDletProxy.KEY_PERIOD:
		case MIDletProxy.KEY_SLASH:
		case MIDletProxy.KEY_GRAVE:
		case MIDletProxy.KEY_LBRACKET:
		case MIDletProxy.KEY_BACKSLASH:
		case MIDletProxy.KEY_RBRACKET:
		case MIDletProxy.KEY_APOSTROPHE:
			return true;
		}
	}

	int getPreferredContentWidth(int contentHeight, int containerClientWidth)
	{
		return 0x80;
	}

	int getPreferredContentHeight(int contentWidth)
	{
		return font.getHeight() + 4;
	}

	private void pointerHandle(int x)
	{
		int i;
		int w1;
		int w2;
		int length;
		int caretPosition;
		char[] text;
		Font font;
		TextInput input;
		synchronized(input = this.input)
		{
			font = this.font;
			text = input.getChars();
			length = input.getLength();
			label0:
			{
				for(w1 = 0, i = 1; i <= length; w1 = w2, i++)
				{
					w2 = font.charsWidth(text, 0, i);
					if(x >= w1 && x < w2)
					{
						caretPosition = x - w1 <= w2 - x ? i - 1 : i;
						break label0;
					}
				}
				caretPosition = length;
			}
			input.setCaretPosition(caretPosition);
			correctScrollPosition();
		}
	}

	private void insert(char src)
	{
		TextInput input;
		synchronized(input = this.input)
		{
			input.insert(src);
			if(input.isChanged())
			{
				correctScrollPosition();
				super.notifyStateChanged();
			}
		}
	}

	private void insert(String src)
	{
		TextInput input;
		synchronized(input = this.input)
		{
			input.insert(src);
			if(input.isChanged())
			{
				correctScrollPosition();
				super.notifyStateChanged();
			}
		}
	}

	private void correctScrollPosition()
	{
		int size;
		int charsWidth;
		int scrollPosition;
		TextInput input;
		if((charsWidth = font.charsWidth((input = this.input).getChars(),
				0, input.getCaretPosition())) < (scrollPosition = this.scrollPosition) +
				(size = super.getPreferredWidth()) / 4 ||
				charsWidth > scrollPosition + 3 * size / 4)
		{
			this.scrollPosition = Math.max(charsWidth - size / 2, 0);
		}
		notifyPaint();
	}

	private void addCommands(Command[] commands)
	{
		int i;
		int len;
		for(len = commands.length, i = 0; i < len; super.addCommand(commands[i]), i++);
	}

	private void removeCommands(Command[] commands)
	{
		int i;
		for(i = commands.length; i-- > 0; super.removeCommand(commands[i]));
	}
}
