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

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

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

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

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

package javax.microedition.lcdui;

import malik.emulator.application.*;
import malik.emulator.media.graphics.*;
import malik.emulator.microedition.*;
import malik.emulator.microedition.lcdui.*;
import malik.emulator.util.*;

public class Alert extends SurfaceScreen
{
    static final int ICON_WIDTH = 48;
    static final int ICON_HEIGHT = 48;
    public static final int FOREVER = -2;

    private static final Font FONT;
    public static final Command DISMISS_COMMAND;
    private static final CommandListener DEFAULT_LISTENER;

    static {
        FONT = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL);
        DISMISS_COMMAND = createDefaultCommand("Закрыть", Command.OK, Integer.MIN_VALUE);
        DEFAULT_LISTENER = new CommandListener() {
            public void commandAction(Command command, Displayable screen) {
                Display parent;
                if(command == Alert.DISMISS_COMMAND && (parent = screen.getParentDisplay()) != null) parent.hideSurfaceScreen();
            }
        };
    }

    class Helper extends SurfaceScreen.Helper
    {
        public Helper() {
        }

        protected void execute() {
            serviceSizeChanged();
            servicePlaceItems();
            serviceScroll();
            servicePaint();
        }

        protected final void servicePlaceItems() {
            (Alert.this).placeItems();
        }
    }

    private boolean placeItemsNeeded;
    private char[] buffer;
    private AlertType type;
    private Image icon;
    private Gauge indicator;
    private String textAsString;
    private final MultilinedStringBuilder textAsBuilder;

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

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

    public Alert(String title, ScrollBarStyle style, String text, Image icon, Gauge indicator, AlertType type) {
        super(title, 0, true, style);
        MultilinedStringBuilder builder;
        if(indicator != null)
        {
            int error;
            Command.Owner commands;
            if((commands = indicator.getCommands()).size() > 0 || commands.defaultCommand() != null)
            {
                throw new IllegalArgumentException("Alert: аргумент indicator имеет команды.");
            }
            if(indicator.getListener() != null)
            {
                throw new IllegalArgumentException("Alert: аргумент indicator имеет обработчик команд.");
            }
            if(indicator.hasLabel())
            {
                throw new IllegalArgumentException("Alert: аргумент indicator имеет метку.");
            }
            if(indicator.suppliedLayout() != Item.LAYOUT_DEFAULT)
            {
                throw new IllegalArgumentException("Alert: аргумент indicator имеет компоновку, отличную от Item.LAYOUT_DEFAULT.");
            }
            if(indicator.suppliedPreferredWidth() > (-1) || indicator.suppliedPreferredHeight() > (-1))
            {
                throw new IllegalArgumentException("Alert: аргумент indicator имеет заблокированный предпочитаемый размер.");
            }
            if(indicator.interactive)
            {
                throw new IllegalArgumentException("Alert: аргумент indicator является интерактивным.");
            }
            error = 0;
            synchronized(Item.OWNER_MONITOR)
            {
                label0:
                {
                    if(indicator.owner != null)
                    {
                        error = 1;
                        break label0;
                    }
                    indicator.owner = this;
                }
            }
            if(error == 1)
            {
                throw new IllegalArgumentException("Alert: аргумент indicator уже принадлежит экрану.");
            }
        }
        this.type = type;
        this.icon = icon == null ? null : Image.createImage(icon);
        this.indicator = indicator;
        this.textAsString = text;
        this.textAsBuilder = builder = new FastMultilinedStringBuilder();
        super.setCommandListener(DEFAULT_LISTENER);
        helper.setDefaultCommand(DISMISS_COMMAND);
        builder.append(text);
        placeCommands();
    }

    public void addCommand(Command command) {
        if(command == null)
        {
            throw new NullPointerException("Alert.addCommand: аргумент command равен нулевой ссылке.");
        }
        if(command != DISMISS_COMMAND)
        {
            Displayable.Helper commands = helper;
            synchronized(Command.MONITOR)
            {
                commands.setDefaultCommand(null);
                commands.addCommand(command);
            }
            placeCommands();
            requestPaintAll();
        }
    }

    public void removeCommand(Command command) {
        if(command != null && command != DISMISS_COMMAND)
        {
            Displayable.Helper commands = helper;
            synchronized(Command.MONITOR)
            {
                commands.removeCommand(command);
                if(commands.size() <= 0) commands.setDefaultCommand(DISMISS_COMMAND);
            }
            placeCommands();
            requestPaintAll();
        }
    }

    public void setCommandListener(CommandListener listener) {
        super.setCommandListener(listener == null ? DEFAULT_LISTENER : listener);
    }

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

    public void setString(String text) {
        MultilinedStringBuilder builder;
        synchronized(builder = textAsBuilder)
        {
            this.placeItemsNeeded = true;
            this.textAsString = text;
            builder.clear();
            builder.append(text);
        }
        scroll.setPosition(0);
        requestPaint(CLIENT);
    }

    public void setImage(Image icon) {
        synchronized(textAsBuilder)
        {
            this.placeItemsNeeded = true;
            this.icon = icon == null ? null : Image.createImage(icon);
        }
        requestPaint(CLIENT);
    }

    public void setIndicator(Gauge indicator) {
        if(indicator != null)
        {
            int error;
            Command.Owner commands;
            if((commands = indicator.getCommands()).size() > 0 || commands.defaultCommand() != null)
            {
                throw new IllegalArgumentException("Alert.setIndicator: аргумент indicator имеет команды.");
            }
            if(indicator.getListener() != null)
            {
                throw new IllegalArgumentException("Alert.setIndicator: аргумент indicator имеет обработчик команд.");
            }
            if(indicator.hasLabel())
            {
                throw new IllegalArgumentException("Alert.setIndicator: аргумент indicator имеет метку.");
            }
            if(indicator.suppliedLayout() != Item.LAYOUT_DEFAULT)
            {
                throw new IllegalArgumentException("Alert.setIndicator: аргумент indicator имеет компоновку, отличную от Item.LAYOUT_DEFAULT.");
            }
            if(indicator.suppliedPreferredWidth() > (-1) || indicator.suppliedPreferredHeight() > (-1))
            {
                throw new IllegalArgumentException("Alert.setIndicator: аргумент indicator имеет заблокированный предпочитаемый размер.");
            }
            if(indicator.interactive)
            {
                throw new IllegalArgumentException("Alert.setIndicator: аргумент indicator является интерактивным.");
            }
            error = 0;
            synchronized(Item.OWNER_MONITOR)
            {
                label0:
                {
                    if(indicator.owner != null)
                    {
                        error = 1;
                        break label0;
                    }
                    indicator.owner = this;
                }
            }
            if(error == 1)
            {
                throw new IllegalArgumentException("Alert.setIndicator: аргумент indicator уже принадлежит экрану.");
            }
        }
        synchronized(textAsBuilder)
        {
            Gauge previous;
            if((previous = this.indicator) != null) previous.owner = null;
            this.placeItemsNeeded = true;
            this.indicator = indicator;
        }
        requestPaint(CLIENT);
    }

    public void setType(AlertType type) {
        synchronized(textAsBuilder)
        {
            this.placeItemsNeeded = true;
            this.type = type;
        }
        requestPaint(CLIENT);
    }

    public int getDefaultTimeout() {
        return FOREVER;
    }

    public int getTimeout() {
        return FOREVER;
    }

    public String getString() {
        return textAsString;
    }

    public Image getImage() {
        return icon;
    }

    public Gauge getIndicator() {
        return indicator;
    }

    public AlertType getType() {
        return type;
    }

    void paint(ScreenGraphics render) {
        int line;
        int srow;
        int frow;
        int rectLeft;
        int rectTop;
        int rectWidth;
        int rectHeight;
        int clipTop = render.getClipY();
        int clipWidth = render.getClipWidth();
        int clipHeight = render.getClipHeight();
        int indicatorWidth = 0;
        int indicatorHeight = 0;
        Item indicator = this.indicator;
        Font font = FONT;
        Image icon = getUsedIcon();
        MultilinedStringBuilder builder;
        /* получение размеров индикатора */
        if(indicator != null)
        {
            indicator.visible = true;
            indicatorWidth = indicator.width;
            indicatorHeight = indicator.height;
        }
        /* вывод значка */
        rectLeft = 0;
        rectTop = clipTop;
        rectWidth = clipWidth;
        rectHeight = indicatorHeight > 0 ? clipHeight - indicatorHeight - 2 : clipHeight;
        if(icon != null)
        {
            render.drawStretch(icon, 0, rectTop + (rectHeight >> 1), ICON_WIDTH, ICON_HEIGHT, Graphics.LEFT | Graphics.VCENTER);
            rectLeft += ICON_WIDTH + 2;
            rectWidth -= ICON_WIDTH + 2;
        }
        /* вывод текста */
        srow = rectTop / (line = font.getHeight());
        frow = (rectTop + rectHeight - 1) / line;
        render.setFont(font);
        render.setColor(RasterCanvas.getSystemColor(0x28));
        render.setClip(0, clipTop, clipWidth, rectHeight);
        synchronized(builder = textAsBuilder)
        {
            int len;
            int lhei;
            char[] lbuf = buffer;
            lhei = font.getHeight() * (len = builder.lines());
            for(int top = rectHeight < lhei ? srow * line : (rectHeight - lhei) >> 1, index = srow; index <= frow && index < len; top += line, index++)
            {
                int lofs = builder.lineOffset(index);
                int llen = builder.lineLength(index);
                if(lbuf == null || lbuf.length < llen) lbuf = buffer = new char[StringBuilder.optimalCapacity(llen)];
                builder.copy(lofs, lofs + llen, lbuf, 0);
                render.drawChars(lbuf, 0, llen, rectLeft, top, Graphics.LEFT | Graphics.TOP);
            }
        }
        /* вывод индикатора */
        if(indicator != null)
        {
            int translateX;
            int translateY;
            rectLeft = 0;
            rectTop += rectHeight + 2;
            rectWidth = clipWidth;
            rectHeight = indicatorHeight;
            translateX = render.getTranslateX() + render.getStartPointX();
            translateY = render.getTranslateY() + render.getStartPointY();
            render.reset();
            render.translate(translateX, translateY);
            render.clipRect(rectLeft, rectTop, indicatorWidth, indicatorHeight);
            render.clipRect(rectLeft, rectTop, rectWidth, rectHeight);
            render.translate(rectLeft, rectTop);
            indicator.onPaint(render);
        }
    }

    void onSizeChanged(int width, int height) {
        synchronized(textAsBuilder)
        {
            placeItemsNeeded = true;
        }
        super.onSizeChanged(width, height);
    }

    void onClientKeyboardEvent(KeyboardEvent event) {
        if(event.getAction() != KeyboardEvent.ACTION_KEY_RELEASED)
        {
            int key = event.getKey();
            ScrollBar scrollbar = scroll;
            DeviceSettings settings = DeviceManager.getInstance().getSettings();
            if(key == settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_UP))
            {
                scrollbar.scroll(-FONT.getHeight());
            }
            else if(key == settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_DOWN))
            {
                scrollbar.scroll(+FONT.getHeight());
            }
        }
    }

    Displayable.Helper createHelper() {
        return this.new Helper();
    }

    final void placeItems() {
        boolean needed;
        int clientWidth;
        int textHeight;
        int indicatorHeight;
        Item indicator;
        MultilinedStringBuilder builder;
        synchronized(builder = textAsBuilder)
        {
            if(needed = placeItemsNeeded) placeItemsNeeded = false;
            indicator = this.indicator;
        }
        if(!needed) return;
        clientWidth = getApplicationWidth();
        if(indicator != null)
        {
            int minWidth;
            indicator.resetContentSize();
            indicator.minimumWidth = minWidth = indicator.computeMinimumWidth();
            indicator.minimumHeight = indicatorHeight = indicator.computeMinimumHeight();
            indicator.width = clientWidth < minWidth ? minWidth : clientWidth;
            indicator.height = indicatorHeight;
            indicator.onSizeChanged();
            indicatorHeight += 2;
        } else
        {
            indicatorHeight = 0;
        }
        if(getUsedIcon() != null) clientWidth -= ICON_WIDTH + 2;
        synchronized(builder)
        {
            Font font = FONT;
            builder.split(font, clientWidth);
            textHeight = font.getHeight() * builder.lines();
        }
        scroll.setRange(textHeight + indicatorHeight);
    }

    private Image getUsedIcon() {
        Image result;
        AlertType type;
        if((result = icon) == null && (type = this.type) != null) result = type.getIcon();
        return result;
    }
}
