/*
	Реализация спецификаций 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.*;

public abstract class ScrollingScreen extends Screen
{
	private static final class ScrollBar extends WeakReference
			implements javax.microedition.lcdui.ScrollBar
	{
		private static int correctPosition(int position, int page, int range)
		{
			return Math.max(0, Math.min(range - page, position));
		}


		private boolean visible;
		private int kind;
		private int range;
		private int position;

		public ScrollBar(ScrollingScreen thisScreen, boolean visible, int kind)
		{
			super(thisScreen);
			this.visible = visible;
			this.kind = kind;
		}

		public void repaintOwner()
		{
			ScrollingScreen thisScreen;
			if((thisScreen = (ScrollingScreen) get()) == null)
			{
				return;
			}
			thisScreen.callSeriallyPaintScreen(ScrollingScreen.CLIENT);
		}

		public void hide()
		{
			setVisible(false);
		}

		public void show()
		{
			setVisible(true);
		}

		public void setVisible(boolean visible)
		{
			ScrollingScreen thisScreen;
			if(this.visible == visible)
			{
				return;
			}
			this.visible = visible;
			if((thisScreen = (ScrollingScreen) get()) == null)
			{
				return;
			}
			thisScreen.callSeriallySizeChanged();
			(kind == HORIZONTAL ? thisScreen.vertScrollBar :
					thisScreen.horzScrollBar).recomputePosition(thisScreen);
			thisScreen.callSeriallyPaintScreen(CLIENT);
		}

		public void setPosition(int position)
		{
			ScrollingScreen thisScreen;
			if((thisScreen = (ScrollingScreen) get()) == null)
			{
				this.position = 0;
				return;
			}
			if(this.position == (position = correctPosition(position, kind == HORIZONTAL ?
					thisScreen.getClientWidth() : thisScreen.getClientHeight(), this.range)))
			{
				return;
			}
			this.position = position;
			thisScreen.callSeriallyScroll();
		}

		public void setRange(int range)
		{
			ScrollingScreen thisScreen;
			if(range < 0)
			{
				range = 0;
			}
			if(this.range == range)
			{
				return;
			}
			this.range = range;
			if((thisScreen = (ScrollingScreen) get()) == null)
			{
				this.position = 0;
				return;
			}
			recomputePosition(thisScreen, range);
			thisScreen.callSeriallyPaintScreen(CLIENT);
		}

		public void scroll(int delta)
		{
			setPosition(position + delta);
		}

		public boolean isVisible()
		{
			return visible;
		}

		public int getPosition()
		{
			return position;
		}

		public int getRange()
		{
			return range;
		}

		public int getPage()
		{
			ScrollingScreen thisScreen;
			return (thisScreen = (ScrollingScreen) get()) != null ? (kind == HORIZONTAL ?
					thisScreen.getClientWidth() : thisScreen.getClientHeight()) : range;
		}

		private void recomputePosition(ScrollingScreen scr)
		{
			int position;
			if((position = this.position) == (this.position = correctPosition(position,
					kind == HORIZONTAL ? scr.getClientWidth() : scr.getClientHeight(), range)))
			{
				return;
			}
			scr.callSeriallyScroll();
		}

		private void recomputePosition(ScrollingScreen scr, int range)
		{
			int position;
			if((position = this.position) == (this.position = correctPosition(position,
					kind == HORIZONTAL ? scr.getClientWidth() : scr.getClientHeight(), range)))
			{
				return;
			}
			scr.callSeriallyScroll();
		}
	}

	private static final class ScrollEvent extends Object
			implements Runnable
	{
		private ScrollingScreen thisScreen;

		ScrollEvent(ScrollingScreen thisScreen)
		{
			this.thisScreen = thisScreen;
		}

		public void run()
		{
			ScrollingScreen thisScreen;
			(thisScreen = this.thisScreen).stayScroll = false;
			if(!thisScreen.isReallyShown())
			{
				thisScreen.needScroll = true;
				return;
			}
			thisScreen.needScroll = false;
			thisScreen.onScroll(thisScreen.getHorzScrollBar(), thisScreen.getVertScrollBar());
			thisScreen.callSeriallyPaintScreen(CLIENT);
		}
	}

	static final int NONE = 0;
	static final int HORIZONTAL = 1;
	static final int VERTICAL = 2;
	static final int BOTH = 3;
	private static final int FOCUSED_CLIENT_OUT = -1;
	private static final int FOCUSED_NONE = 0;
	private static final int FOCUSED_CLIENT = 1;
	private static final int FOCUSED_HORZ_SCROLL_BAR = 2;
	private static final int FOCUSED_VERT_SCROLL_BAR = 3;

	private static ScrollBarStyle getScrollBarStyle()
	{
		ScrollBarStyle result;
		try
		{
			result = (ScrollBarStyle) Class.forName(System.getSystemProperty(
					"malik.emulator.microedition.scrollbar.style")).newInstance();
		}
		catch(Exception e)
		{
			result = new ScrollBarDefault();
		}
		return result;
	}


	private boolean needScroll;
	private boolean stayScroll;
	private boolean shiftPressed;
	private int focused;
	private int hbutton;
	private ScrollBar horzScrollBar;
	private ScrollBar vertScrollBar;
	private ScrollBarStyle style;

	ScrollingScreen(boolean system, boolean fullScreenMode, String title, Ticker ticker,
			Command[] commands, Command defaultCommand, CommandListener listener,
			int scrollbars)
	{
		this(system, fullScreenMode, title, ticker, commands, defaultCommand, listener,
				scrollbars, getScrollBarStyle());
	}

	ScrollingScreen(boolean system, boolean fullScreenMode, String title, Ticker ticker,
			Command[] commands, Command defaultCommand, CommandListener listener,
			int scrollbars, ScrollBarStyle style)
	{
		super(system, fullScreenMode, title, ticker, commands, defaultCommand, listener);
		this.needScroll = true;
		this.horzScrollBar = new ScrollBar(this, (scrollbars & HORIZONTAL) != 0, HORIZONTAL);
		this.vertScrollBar = new ScrollBar(this, (scrollbars & VERTICAL) != 0, VERTICAL);
		this.style = style == null ? new ScrollBarDefault() : style;
	}

	public final void scroll(int deltaX, int deltaY)
	{
		horzScrollBar.scroll(deltaX);
		vertScrollBar.scroll(deltaY);
	}

	public final void scrollTo(int x, int y)
	{
		horzScrollBar.setPosition(x);
		vertScrollBar.setPosition(y);
	}

	public final javax.microedition.lcdui.ScrollBar getHorzScrollBar()
	{
		return horzScrollBar;
	}

	public final javax.microedition.lcdui.ScrollBar getVertScrollBar()
	{
		return vertScrollBar;
	}

	abstract void paint(Graphics render, javax.microedition.lcdui.ScrollBar horzScrollBar,
			javax.microedition.lcdui.ScrollBar vertScrollBar);

	void onShow()
	{
		super.onShow();
		if(needScroll)
		{
			callSeriallyScroll();
		}
		focused = FOCUSED_NONE;
		shiftPressed = false;
	}

	void onHide()
	{
		shiftPressed = false;
		focused = FOCUSED_NONE;
		super.onHide();
	}

	void onKeyPressed(int key, int charCode)
	{
		if((!super.keyHandling(key)) && (key == MIDletProxy.KEY_SHIFT ||
				key == MIDletProxy.KEY_LSHIFT || key == MIDletProxy.KEY_RSHIFT))
		{
			shiftPressed = true;
		}
		super.onKeyPressed(key, charCode);
	}

	void onKeyReleased(int key)
	{
		if((!super.keyHandling(key)) && (key == MIDletProxy.KEY_SHIFT ||
				key == MIDletProxy.KEY_LSHIFT || key == MIDletProxy.KEY_RSHIFT))
		{
			shiftPressed = false;
		}
		super.onKeyReleased(key);
	}

	boolean keyHandling(int key)
	{
		return key == MIDletProxy.KEY_SHIFT || key == MIDletProxy.KEY_LSHIFT ||
				key == MIDletProxy.KEY_RSHIFT || super.keyHandling(key);
	}

	boolean coordinateHandling(int x, int y)
	{
		int l;
		int t;
		Display display = MIDletProxy.getInstance().getEmulatorScreen();
		return x >= (l = display.getGUIElementLeft(this, GUI_ELEMENT_CLIENT)) &&
				x < l + display.getGUIElementWidth(this, GUI_ELEMENT_CLIENT) &&
				y >= (t = display.getGUIElementTop(this, GUI_ELEMENT_CLIENT)) &&
				y < t + display.getGUIElementHeight(this, GUI_ELEMENT_CLIENT) ||
				super.coordinateHandling(x, y);
	}

	int getMarginRight()
	{
		return super.getMarginRight() + (vertScrollBar.isVisible() ? getVertScrollBarWidth() : 0);
	}

	int getMarginBottom()
	{
		return super.getMarginBottom() + (horzScrollBar.isVisible() ? getHorzScrollBarWidth() : 0);
	}

	void paintHorzScrollBar(Graphics clientRender, javax.microedition.lcdui.ScrollBar scrollbar,
			int length, int width)
	{
		style.paintHorzScrollBar(clientRender, scrollbar, length, width);
	}

	void paintVertScrollBar(Graphics clientRender, javax.microedition.lcdui.ScrollBar scrollbar,
			int length, int width)
	{
		style.paintVertScrollBar(clientRender, scrollbar, length, width);
	}

	void onScroll(javax.microedition.lcdui.ScrollBar horzScrollBar,
			javax.microedition.lcdui.ScrollBar vertScrollBar)
	{
	}

	void onClientPointerPressed(int x, int y, int button)
	{
	}

	void onClientPointerDragged(int x, int y)
	{
	}

	void onClientPointerReleased(int x, int y, int button)
	{
	}

	void onHorzScrollBarPointerPressed(javax.microedition.lcdui.ScrollBar scrollbar,
			int length, int width, int x, int y, int button)
	{
		style.onHorzScrollBarPointerPressed(scrollbar, length, width, x, y, button);
	}

	void onHorzScrollBarPointerDragged(javax.microedition.lcdui.ScrollBar scrollbar,
			int length, int width, int x, int y)
	{
		style.onHorzScrollBarPointerDragged(scrollbar, length, width, x, y);
	}

	void onHorzScrollBarPointerReleased(javax.microedition.lcdui.ScrollBar scrollbar,
			int length, int width, int x, int y, int button)
	{
		style.onHorzScrollBarPointerReleased(scrollbar, length, width, x, y, button);
	}

	void onVertScrollBarPointerPressed(javax.microedition.lcdui.ScrollBar scrollbar,
			int length, int width, int x, int y, int button)
	{
		style.onVertScrollBarPointerPressed(scrollbar, length, width, x, y, button);
	}

	void onVertScrollBarPointerDragged(javax.microedition.lcdui.ScrollBar scrollbar,
			int length, int width, int x, int y)
	{
		style.onVertScrollBarPointerDragged(scrollbar, length, width, x, y);
	}

	void onVertScrollBarPointerReleased(javax.microedition.lcdui.ScrollBar scrollbar,
			int length, int width, int x, int y, int button)
	{
		style.onVertScrollBarPointerReleased(scrollbar, length, width, x, y, button);
	}

	int getHorzScrollBarWidth()
	{
		return style.getHorzScrollBarWidth();
	}

	int getVertScrollBarWidth()
	{
		return style.getVertScrollBarWidth();
	}

	final void paint(Graphics render)
	{
		int horzPos;
		int vertPos;
		int clientLeft;
		int clientTop;
		int clientWidth;
		int clientHeight;
		int horzWidth;
		int vertWidth;
		ScrollBar horz = horzScrollBar;
		ScrollBar vert = vertScrollBar;
		horzPos = horz.getPosition();
		vertPos = vert.getPosition();
		clientLeft = getClientLeft();
		clientTop = getClientTop();
		clientWidth = getClientWidth();
		clientHeight = getClientHeight();
		horzWidth = getHorzScrollBarWidth();
		vertWidth = getVertScrollBarWidth();
		try
		{
			render.translate(-horzPos, -vertPos);
			render.setClip(horzPos, vertPos, clientWidth, clientHeight);
			paint(render, horz, vert);
		}
		catch(RuntimeException e)
		{
			e.printRealStackTrace();
		}
		if(horz.isVisible())
		{
			try
			{
				render.reset();
				render.translate(clientLeft, clientTop + clientHeight);
				render.setClip(0, 0, clientWidth, horzWidth);
				paintHorzScrollBar(render, horz, clientWidth, horzWidth);
			}
			catch(RuntimeException e)
			{
				e.printRealStackTrace();
			}
		}
		if(vert.isVisible())
		{
			try
			{
				render.reset();
				render.translate(clientLeft + clientWidth, clientTop);
				render.setClip(0, 0, vertWidth, clientHeight);
				paintVertScrollBar(render, vert, clientHeight, vertWidth);
			}
			catch(RuntimeException e)
			{
				e.printRealStackTrace();
			}
		}
	}

	final void onPointerPressed(int x, int y, int button)
	{
		int l;
		int t;
		int horzWidth;
		int vertWidth;
		int clientLeft;
		int clientTop;
		int clientWidth;
		int clientHeight;
		int clientRight;
		int clientBottom;
		Display display;
		ScrollBar horz = horzScrollBar;
		ScrollBar vert = vertScrollBar;
		ScrollBar scrollbar = shiftPressed ? horz : (vert.isVisible() ? vert : horz);
		switch(button)
		{
		case MIDletProxy.BUTTON_WHEEL_UP:
			scrollbar.scroll(-scrollbar.getPage() / 4);
			return;
		case MIDletProxy.BUTTON_WHEEL_DOWN:
			scrollbar.scroll(scrollbar.getPage() / 4);
			return;
		}
		display = MIDletProxy.getInstance().getEmulatorScreen();
		if(focused == FOCUSED_NONE && (!super.coordinateHandling(x, y)) &&
				x >= (l = display.getGUIElementLeft(this, GUI_ELEMENT_CLIENT)) &&
				x < l + display.getGUIElementWidth(this, GUI_ELEMENT_CLIENT) &&
				y >= (t = display.getGUIElementTop(this, GUI_ELEMENT_CLIENT)) &&
				y < t + display.getGUIElementHeight(this, GUI_ELEMENT_CLIENT))
		{
			hbutton = button;
			x -= l;
			y -= t;
			horzWidth = getHorzScrollBarWidth();
			vertWidth = getVertScrollBarWidth();
			clientRight = (clientLeft = getClientLeft()) + (clientWidth = getClientWidth());
			clientBottom = (clientTop = getClientTop()) + (clientHeight = getClientHeight());
			if(x >= clientLeft && x < clientRight && y >= clientTop && y < clientBottom)
			{
				focused = FOCUSED_CLIENT;
				onClientPointerPressed(
						x - clientLeft + horz.getPosition(),
						y - clientTop + vert.getPosition(), button);
				return;
			}
			if((scrollbar = horz).isVisible() &&
					x >= clientLeft && x < clientRight &&
					y >= clientBottom && y < clientBottom + horzWidth)
			{
				focused = FOCUSED_HORZ_SCROLL_BAR;
				onHorzScrollBarPointerPressed(scrollbar,
						clientWidth, horzWidth,
						x - clientLeft, y - clientBottom, button);
				return;
			}
			if((scrollbar = vert).isVisible() &&
					y >= clientTop && y < clientBottom &&
					x >= clientRight && x < clientRight + vertWidth)
			{
				focused = FOCUSED_VERT_SCROLL_BAR;
				onVertScrollBarPointerPressed(scrollbar,
						clientHeight, vertWidth,
						x - clientRight, y - clientTop, button);
				return;
			}
			focused = FOCUSED_CLIENT_OUT;
			return;
		}
		super.onPointerPressed(x, y, button);
	}

	final void onPointerDragged(int x, int y)
	{
		int f;
		int clientLeft;
		int clientTop;
		int clientWidth;
		int clientHeight;
		int clientRight;
		int clientBottom;
		Display display;
		if((f = focused) != FOCUSED_NONE)
		{
			display = MIDletProxy.getInstance().getEmulatorScreen();
			x -= display.getGUIElementLeft(this, GUI_ELEMENT_CLIENT);
			y -= display.getGUIElementTop(this, GUI_ELEMENT_CLIENT);
			clientRight = (clientLeft = getClientLeft()) + (clientWidth = getClientWidth());
			clientBottom = (clientTop = getClientTop()) + (clientHeight = getClientHeight());
			switch(f)
			{
			default:
				return;
			case FOCUSED_CLIENT:
				onClientPointerDragged(
						x - clientLeft + horzScrollBar.getPosition(),
						y - clientTop + vertScrollBar.getPosition());
				return;
			case FOCUSED_HORZ_SCROLL_BAR:
				onHorzScrollBarPointerDragged(horzScrollBar,
						clientWidth, getHorzScrollBarWidth(),
						x - clientLeft, y - clientBottom);
				return;
			case FOCUSED_VERT_SCROLL_BAR:
				onVertScrollBarPointerDragged(vertScrollBar,
						clientHeight, getVertScrollBarWidth(),
						x - clientRight, y - clientTop);
				return;
			}
		}
		super.onPointerDragged(x, y);
	}

	final void onPointerReleased(int x, int y, int button)
	{
		int f;
		int clientLeft;
		int clientTop;
		int clientWidth;
		int clientHeight;
		int clientRight;
		int clientBottom;
		Display display;
		if((f = focused) != FOCUSED_NONE && hbutton == button)
		{
			focused = FOCUSED_NONE;
			display = MIDletProxy.getInstance().getEmulatorScreen();
			x -= display.getGUIElementLeft(this, GUI_ELEMENT_CLIENT);
			y -= display.getGUIElementTop(this, GUI_ELEMENT_CLIENT);
			clientRight = (clientLeft = getClientLeft()) + (clientWidth = getClientWidth());
			clientBottom = (clientTop = getClientTop()) + (clientHeight = getClientHeight());
			switch(f)
			{
			default:
				return;
			case FOCUSED_CLIENT:
				onClientPointerReleased(
						x - clientLeft + horzScrollBar.getPosition(),
						y - clientTop + vertScrollBar.getPosition(), button);
				return;
			case FOCUSED_HORZ_SCROLL_BAR:
				onHorzScrollBarPointerReleased(horzScrollBar,
						clientWidth, getHorzScrollBarWidth(),
						x - clientLeft, y - clientBottom, button);
				return;
			case FOCUSED_VERT_SCROLL_BAR:
				onVertScrollBarPointerReleased(vertScrollBar,
						clientHeight, getVertScrollBarWidth(),
						x - clientRight, y - clientTop, button);
				return;
			}
		}
		super.onPointerReleased(x, y, button);
	}

	final void callSeriallyScroll()
	{
		if(stayScroll)
		{
			return;
		}
		stayScroll = true;
		callSerially(new ScrollEvent(this));
	}
}
