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

public class Alert extends Container
{
	private static final class ComputeScrollBarsEvent extends WeakReference
			implements Runnable
	{
		ComputeScrollBarsEvent(Alert thisScreen)
		{
			super(thisScreen);
		}

		public void run()
		{
			Alert thisScreen;
			if((thisScreen = (Alert) get()) == null)
			{
				return;
			}
			thisScreen.computeScrollBars(thisScreen.getClientWidth());
		}
	}

	private static final class ChangeIndicatorEvent extends Object
			implements Runnable
	{
		private Displayable screen;
		private Item oldIndicator;
		private Item newIndicator;

		ChangeIndicatorEvent(Displayable screen, Item oldIndicator, Item newIndicator)
		{
			this.screen = screen;
			this.oldIndicator = oldIndicator;
			this.newIndicator = newIndicator;
		}

		public void run()
		{
			Item indicator;
			if(!screen.isReallyShown())
			{
				return;
			}
			if((indicator = oldIndicator) != null)
			{
				indicator.setVisible(false);
				try
				{
					indicator.onHide();
				}
				catch(RuntimeException e)
				{
					e.printRealStackTrace();
				}
			}
			if((indicator = newIndicator) != null)
			{
				indicator.setVisible(true);
				try
				{
					indicator.onShow();
				}
				catch(RuntimeException e)
				{
					e.printRealStackTrace();
				}
			}
		}
	}

	public static final int FOREVER = -2;
	public static final Command DISMISS_COMMAND;
	private static final Font FONT;

	static
	{
		DISMISS_COMMAND = new Command("Закрыть", Command.OK, Integer.MIN_VALUE);
		FONT = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL);
	}


	private boolean stayComputeScrollBars;
	private boolean hasCommandListener;
	private String text;
	private Image icon;
	private AlertType type;
	private Gauge indicator;
	private Runnable computeScrollBarsEvent;
	private TextOutputMultilined lines;

	public Alert(String title)
	{
		this(false, title, null, null, null, null, null, null, null, null);
	}

	public Alert(String title, String text, Image icon, AlertType type)
	{
		this(false, title, null, null, null, null, text, icon, type, null);
	}

	public Alert(String title, Ticker ticker,
			Command[] commands, Command dismissCommand, CommandListener listener,
			String text, Image icon, AlertType type, Gauge indicator)
	{
		this(true, title, ticker, commands, dismissCommand, listener, text, icon, type, indicator);
	}

	private Alert(boolean system, String title, Ticker ticker,
			Command[] commands, Command dismissCommand, CommandListener listener,
			String text, Image icon, AlertType type, Gauge indicator)
	{
		super(system, title, ticker, commands,
				dismissCommand == null && (commands == null || commands.length <= 0) ?
				DISMISS_COMMAND : dismissCommand, listener, VERTICAL);
		int error;
		TextOutputMultilined lines;
		if(indicator != null)
		{
			error = 0;
			synchronized(getOwningLock())
			{
				label0:
				{
					if(indicator.getOwner() != null)
					{
						error = 1;
						break label0;
					}
					if(!indicator.isMatchForAlert())
					{
						error = 2;
						break label0;
					}
					indicator.setOwner(this);
				}
			}
			switch(error)
			{
			case 1:
				throw new IllegalStateException("Alert: " +
						"индикатор уже принадлежит контейнеру.");
			case 2:
				throw new IllegalArgumentException("Alert: " +
						"индикатор не соответствует требованиям " +
						"для размещения его на диалоговом экране.");
			}
		}
		this.hasCommandListener = listener != null;
		this.icon = icon != null ? Image.createImage(icon) : null;
		this.text = text;
		this.type = type;
		this.indicator = indicator;
		this.computeScrollBarsEvent = new ComputeScrollBarsEvent(this);
		this.lines = lines = new TextOutputMultilinedExt(0);
		lines.setText(text);
	}

	public void addCommand(Command command)
	{
		if(command == DISMISS_COMMAND)
		{
			return;
		}
		super.removeCommand(DISMISS_COMMAND);
		super.addCommand(command);
	}

	public void removeCommand(Command command)
	{
		boolean needAddDismissCommand;
		CommandEnumeration enumeration;
		if(command == DISMISS_COMMAND)
		{
			return;
		}
		super.removeCommand(command);
		synchronized((enumeration = getCommands()).getMonitor())
		{
			needAddDismissCommand =
					enumeration.getCommandsCount() == 0 &&
					enumeration.getDefaultCommand() == null;
		}
		if(needAddDismissCommand)
		{
			super.addCommand(DISMISS_COMMAND);
		}
	}

	public void setCommandListener(CommandListener listener)
	{
		this.hasCommandListener = listener != null;
		super.setCommandListener(listener);
	}

	public void setTimeout(int timeout)
	{
		if(timeout <= 0 && timeout != FOREVER)
		{
			throw new IllegalArgumentException("Alert.setTimeout: " +
					"недопустимое значение параметра timeout.");
		}
	}

	public void setType(AlertType type)
	{
		synchronized(lines)
		{
			this.type = type;
			callSeriallyComputeScrollBars();
			callSeriallyPaintScreen(CLIENT);
		}
	}

	public void setImage(Image icon)
	{
		synchronized(lines)
		{
			this.icon = icon != null ? Image.createImage(icon) : null;
			callSeriallyComputeScrollBars();
			callSeriallyPaintScreen(CLIENT);
		}
	}

	public void setIndicator(Gauge indicator)
	{
		int error;
		Gauge oldIndicator;
		error = 0;
		synchronized(lines)
		{
			label0:
			{
				oldIndicator = this.indicator;
				synchronized(getOwningLock())
				{
					label1:
					{
						if(indicator != null)
						{
							if(indicator.getOwner() != null)
							{
								error = 1;
								break label1;
							}
							if(!indicator.isMatchForAlert())
							{
								error = 2;
								break label1;
							}
							indicator.setOwner(this);
						}
						if(oldIndicator != null)
						{
							oldIndicator.setOwner(null);
						}
					}
				}
				if(error != 0)
				{
					break label0;
				}
				this.indicator = indicator;
				callSeriallyComputeScrollBars();
				callSerially(new ChangeIndicatorEvent(this, oldIndicator, indicator));
				callSeriallyPaintScreen(CLIENT);
			}
		}
		switch(error)
		{
		case 1:
			throw new IllegalStateException("Alert.setIndicator: " +
					"индикатор уже принадлежит контейнеру.");
		case 2:
			throw new IllegalArgumentException("Alert.setIndicator: " +
					"индикатор не соответствует требованиям " +
					"для размещения его на диалоговом экране.");
		}
	}

	public void setString(String text)
	{
		TextOutputMultilined lines;
		synchronized(lines = this.lines)
		{
			lines.setText(text);
			this.text = text;
			callSeriallyComputeScrollBars();
			callSeriallyPaintScreen(CLIENT);
		}
	}

	public int getDefaultTimeout()
	{
		return FOREVER;
	}

	public int getTimeout()
	{
		return FOREVER;
	}

	public AlertType getType()
	{
		return type;
	}

	public Image getImage()
	{
		return icon;
	}

	public Gauge getIndicator()
	{
		return indicator;
	}

	public String getString()
	{
		return text;
	}

	void paint(GraphicsClipRestricted render, ScrollBar horzScrollBar, ScrollBar vertScrollBar)
	{
		int l;
		int t;
		int s;
		int h;
		int ih;
		int tx;
		int ty;
		int ct;
		int cb;
		int i;
		int len;
		char[] chars;
		Image icon;
		Item indicator;
		TextOutputMultilined lines;
		render.setFont(FONT);
		render.setColor(RasterCanvas.getSystemColor(0x28));
		synchronized(lines = this.lines)
		{
			l = 0;
			s = FONT.getHeight();
			h = getClientHeight();
			ih = 0;
			tx = horzScrollBar.getPosition();
			ty = vertScrollBar.getPosition();
			cb = (ct = render.getClipY()) + render.getClipHeight();
			if((indicator = this.indicator) != null)
			{
				ih = indicator.getHeight();
				h -= ih;
				cb -= ih;
			}
			if((icon = getOutputIcon()) != null)
			{
				render.drawStretchRegion(icon, 0, 0, icon.getWidth(), icon.getHeight(), 0,
						tx, ((ct + cb) / 2), 48, 48, Graphics.LEFT | Graphics.VCENTER);
				l = 50;
			}
			l += tx;
			len = lines.getLinesCount();
			chars = lines.getChars();
			t = (Math.max(h, vertScrollBar.getRange() - ih) - (s * len)) / 2;
			for(i = 0; i < len; t += s, i++)
			{
				if(t < cb && t + s > ct)
				{
					render.drawChars(chars,
							lines.getLineStart(i), lines.getLineLength(i), l, t, 0);
				}
			}
			if(indicator != null)
			{
				render.translate(tx, ty + h);
				render.restrictClipRect(0, 0, indicator.getWidth(), ih);
				indicator.onPaint(render);
			}
		}
	}

	void onShow()
	{
		Item indicator;
		AlertType type;
		super.onShow();
		if((type = this.type) != null && type.isSystem())
		{
			type.playSound(MIDletProxy.getInstance().getEmulatorScreen());
		}
		if((indicator = this.indicator) != null)
		{
			indicator.setVisible(true);
			try
			{
				indicator.onShow();
			}
			catch(RuntimeException e)
			{
				e.printRealStackTrace();
			}
		}
	}

	void onHide()
	{
		Item indicator;
		if((indicator = this.indicator) != null)
		{
			indicator.setVisible(false);
			try
			{
				indicator.onHide();
			}
			catch(RuntimeException e)
			{
				e.printRealStackTrace();
			}
		}
		super.onHide();
	}

	void onSizeChanged(int width, int height)
	{
		computeScrollBars(width);
		super.onSizeChanged(width, height);
	}

	void onKeyPressed(int key, int charCode)
	{
		MIDletProxy proxy = MIDletProxy.getInstance();
		if(key == proxy.getKeyUsedAs(MIDletProxy.DEVICE_KEY_UP))
		{
			scroll(0, -getVertScrollBar().getPage() / 4);
			return;
		}
		if(key == proxy.getKeyUsedAs(MIDletProxy.DEVICE_KEY_DOWN))
		{
			scroll(0, getVertScrollBar().getPage() / 4);
			return;
		}
		super.onKeyPressed(key, charCode);
	}

	void onCommandAction(Command command)
	{
		Display display;
		if((!hasCommandListener) && command == DISMISS_COMMAND)
		{
			(display = MIDletProxy.getInstance().getEmulatorScreen()).setCurrent(
					display.getScreenAfterAlert());
			return;
		}
		super.onCommandAction(command);
	}

	void notifyPlaceItems()
	{
	}

	final void setCurrent(Item item, Display display)
	{
		throw new IllegalStateException("Display.setCurrentItem: " +
				"постановка фокуса ввода невозможна - " +
				"заданный элемент находится на диалоговом экране.");
	}

	private void computeScrollBars(int containerClientWidth)
	{
		int iconWidth;
		int indHeight;
		Item indicator;
		TextOutputMultilined lines;
		synchronized(lines = this.lines)
		{
			stayComputeScrollBars = false;
			iconWidth = getOutputIcon() != null ? 50 : 0;
			indHeight = 0;
			if((indicator = this.indicator) != null)
			{
				indicator.computeSizes(containerClientWidth);
				indHeight = indicator.getHeight();
			}
			lines.split(FONT, containerClientWidth - iconWidth);
			getVertScrollBar().setRange(FONT.getHeight() * lines.getLinesCount() + indHeight);
		}
	}

	private void callSeriallyComputeScrollBars()
	{
		if(stayComputeScrollBars)
		{
			return;
		}
		stayComputeScrollBars = true;
		callSerially(computeScrollBarsEvent);
	}

	private Image getOutputIcon()
	{
		Image icon;
		AlertType type;
		return (icon = this.icon) != null ? icon :
				((type = this.type) != null ? type.getIcon() : null);
	}
}
