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

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

public class StringList extends CustomSurfaceScreen implements TextMenu
{
    private static final int INITIAL_SCROLL = 32;

    private int selected;
    private int scroll;
    private int count;
    private String[] elements;
    private Font font;
    private final Object monitor;

    public StringList(String title, ScrollBarStyle style) {
        this(title, style, null);
    }

    public StringList(String title, ScrollBarStyle style, String[] list) {
        super(title, 0, true, style);
        int length;
        String[] elements = new String[(length = list == null ? 0 : list.length) <= 0x0f ? 0x0f : StringBuilder.optimalCapacity(length)];
        Font font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL);
        for(int i = length; i-- > 0; )
        {
            String s;
            elements[i] = (s = list[i]) == null ? "" : s;
        }
        this.scroll = INITIAL_SCROLL;
        this.count = length;
        this.elements = elements;
        this.font = font;
        this.monitor = new Object();
        super.getScrollBar().setRange(font.getHeight() * length);
    }

    public void insert(int elementIndex, String text) {
        if(text == null) text = "";
        synchronized(monitor)
        {
            int sel;
            int len;
            String[] list;
            if(elementIndex > (len = count)) elementIndex = len;
            if(elementIndex < 0) elementIndex = 0;
            if(len == (list = elements).length) Array.copy(list, 0, list = elements = new String[(len << 1) + 1], 0, len);
            if(elementIndex < len) Array.copy(list, elementIndex, list, elementIndex + 1, len - elementIndex);
            list[elementIndex] = text;
            count = ++len;
            sel = selected;
            if(len > 1 && sel >= elementIndex) selected = ++sel;
            super.getScrollBar().setRange(font.getHeight() * len);
            correctScrollBarPosition(sel);
        }
    }

    public void delete(int elementIndex) {
        int error = 0;
        synchronized(monitor)
        {
            label0:
            {
                int sel;
                int len;
                String[] list;
                if(elementIndex < 0 || elementIndex > (len = count - 1))
                {
                    error = 1;
                    break label0;
                }
                list = elements;
                if(elementIndex < len) Array.copy(list, elementIndex + 1, list, elementIndex, len - elementIndex);
                list[count = len] = null;
                sel = selected;
                if(len > 0)
                {
                    if(sel == elementIndex)
                    {
                        if(sel >= len) sel = len - 1;
                        selected = sel;
                        scroll = INITIAL_SCROLL;
                    }
                    else if(sel > elementIndex)
                    {
                        selected = --sel;
                    }
                }
                super.getScrollBar().setRange(font.getHeight() * len);
                correctScrollBarPosition(sel);
            }
        }
        if(error == 1)
        {
            throw new IndexOutOfBoundsException("StringList.delete: аргумент elementIndex выходит из диапазона.");
        }
    }

    public void deleteAll() {
        synchronized(monitor)
        {
            Array.fill(elements, 0, count, null);
            selected = 0;
            scroll = INITIAL_SCROLL;
            count = 0;
            super.getScrollBar().setRange(0);
        }
    }

    public void setSelectedIndex(int elementIndex) {
        synchronized(monitor)
        {
            int lim;
            if(elementIndex > (lim = count - 1)) elementIndex = lim;
            if(elementIndex < 0) elementIndex = 0;
            correctScrollBarPosition(selected = elementIndex);
            scroll = INITIAL_SCROLL;
        }
    }

    public void setString(int elementIndex, String text) {
        int error = 0;
        if(text == null) text = "";
        synchronized(monitor)
        {
            label0:
            {
                if(elementIndex < 0 || elementIndex >= count)
                {
                    error = 1;
                    break label0;
                }
                elements[elementIndex] = text;
                repaint();
            }
        }
        if(error == 1)
        {
            throw new IndexOutOfBoundsException("StringList.setString: аргумент elementIndex выходит из диапазона.");
        }
    }

    public void setFont(Font font) {
        if(font == null) font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL);
        synchronized(monitor)
        {
            this.font = font;
            super.getScrollBar().setRange(font.getHeight() * count);
            correctScrollBarPosition(selected);
        }
    }

    public int append(String text) {
        int result;
        if(text == null) text = "";
        synchronized(monitor)
        {
            int len;
            String[] list;
            if((result = len = count) == (list = elements).length) Array.copy(list, 0, list = elements = new String[(len << 1) + 1], 0, len);
            list[len++] = text;
            count = len;
            super.getScrollBar().setRange(font.getHeight() * len);
        }
        return result;
    }

    public int getSize() {
        return count;
    }

    public int getSelectedIndex() {
        return count <= 0 ? -1 : selected;
    }

    public String getSelectedString() {
        int error = 0;
        String result;
        synchronized(monitor)
        {
            label0:
            {
                if(count <= 0)
                {
                    error = 1;
                    result = null;
                    break label0;
                }
                result = elements[selected];
            }
        }
        if(error == 1)
        {
            throw new IndexOutOfBoundsException("StringList.getSelectedString: список пуст.");
        }
        return result;
    }

    public String getString(int elementIndex) {
        int error = 0;
        String result;
        synchronized(monitor)
        {
            label0:
            {
                if(elementIndex < 0 || elementIndex >= count)
                {
                    error = 1;
                    result = null;
                    break label0;
                }
                result = elements[elementIndex];
            }
        }
        if(error == 1)
        {
            throw new IndexOutOfBoundsException("StringList.getString: аргумент elementIndex выходит из диапазона.");
        }
        return result;
    }

    public Font getFont() {
        return font;
    }

    protected void paint(Graphics render) {
        int width = super.getWidth();
        int clipLeft = render.getClipX();
        int clipTop = render.getClipY();
        int clipWidth = render.getClipWidth();
        int clipHeight = render.getClipHeight();
        synchronized(monitor)
        {
            int line;
            int srow;
            int frow;
            int length = this.count;
            int selected = this.selected;
            String[] elements = this.elements;
            Font font = this.font;
            srow = clipTop / (line = font.getHeight());
            frow = (clipTop + clipHeight - 1) / line;
            render.setFont(font);
            for(int top = srow * line, index = srow; index <= frow && index < length; top += line, index++)
            {
                String element;
                if((element = elements[index]) == null) element = "";
                if(index == selected)
                {
                    int pos;
                    int wid = font.stringWidth(element);
                    int scroll = this.scroll;
                    render.setColor(RasterCanvas.getSystemColor(0x0d));
                    render.fillRect(0, top, width, line);
                    render.setColor(RasterCanvas.getSystemColor(0x0e));
                    render.setClip(2, top, pos = width - 4, line);
                    render.drawString(element, wid > pos ? scroll + 2 : 2, top, 0);
                    render.setClip(clipLeft, clipTop, clipWidth, clipHeight);
                    if((scroll -= 2) < -wid || scroll >= pos) scroll = pos - 2;
                    if(wid > pos) repaint();
                    this.scroll = scroll;
                    continue;
                }
                render.setColor(RasterCanvas.getSystemColor(0x07));
                render.drawString(MultilinedStringBuilder.truncate(element, font, width - 4), 2, top, 0);
            }
        }
    }

    protected void keyboardNotify(KeyboardEvent event) {
        int key = event.getKey();
        DeviceSettings settings = DeviceManager.getInstance().getSettings();
        switch(event.getAction())
        {
        case KeyboardEvent.ACTION_KEY_PRESSED:
        case KeyboardEvent.ACTION_KEY_REPEATED:
            if(settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_UP) == key)
            {
                synchronized(monitor)
                {
                    int len;
                    if((len = count) > 0)
                    {
                        int sel = (selected + len - 1) % len;
                        correctScrollBarPosition(selected = sel);
                        scroll = INITIAL_SCROLL;
                    }
                }
                break;
            }
            if(settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_DOWN) == key)
            {
                synchronized(monitor)
                {
                    int len;
                    if((len = count) > 0)
                    {
                        int sel = (selected + 1) % len;
                        correctScrollBarPosition(selected = sel);
                        scroll = INITIAL_SCROLL;
                    }
                }
                break;
            }
            break;
        }
    }

    protected void pointerNotify(PointerEvent event) {
        int action = event.getAction();
        if(
            event.isButtonPressed(PointerEvent.BUTTON_MAIN) || (action == PointerEvent.ACTION_BUTTON_RELEASED ||
            action == PointerEvent.ACTION_POINTER_RELEASED) && event.getButton() == PointerEvent.BUTTON_MAIN
        ) synchronized(monitor)
        {
            int lim;
            int line = font.getHeight();
            int index = event.getY() / line;
            if(index > (lim = count - 1)) index = lim;
            if(index < 0) index = 0;
            correctScrollBarPosition(selected = index);
            if(action != PointerEvent.ACTION_BUTTON_RELEASED && action != PointerEvent.ACTION_POINTER_RELEASED) scroll = INITIAL_SCROLL;
        }
    }

    private void correctScrollBarPosition(int selected) {
        int pos;
        int page;
        int line = font.getHeight();
        int top1 = selected * line;
        int top2 = line + top1;
        ScrollBar scroll = super.getScrollBar();
        if(top1 < (pos = scroll.getPosition()))
        {
            scroll.setPosition(top1);
        }
        else if(top2 > pos + (page = scroll.getPage()))
        {
            scroll.setPosition(top2 - page);
        }
        else
        {
            repaint();
        }
    }
}
