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

public class CharacterList extends CustomSurfaceScreen
{
    private int selected;
    private char[] chars;
    private Font font;
    private final Object monitor;

    public CharacterList(String title, ScrollBarStyle style) {
        super(title, 0, true, style);
        this.chars = new char[0];
        this.font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM);
        this.monitor = new Object();
        correctScrollBarRange(0);
    }

    public CharacterList(String title, ScrollBarStyle style, char[] list) {
        super(title, 0, true, style);
        char[] chars;
        if(list != null)
        {
            int len;
            Array.copy(list, 0, chars = new char[len = list.length], 0, len);
        } else
        {
            chars = new char[0];
        }
        this.chars = chars;
        this.font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM);
        this.monitor = new Object();
        correctScrollBarRange(chars.length);
    }

    public CharacterList(String title, ScrollBarStyle style, String list) {
        super(title, 0, true, style);
        char[] chars;
        this.chars = chars = list == null ? new char[0] : list.toCharArray();
        this.font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM);
        this.monitor = new Object();
        correctScrollBarRange(chars.length);
    }

    public void setSelectedIndex(int index) {
        synchronized(monitor)
        {
            int lim;
            char[] list;
            if(index > (lim = (list = chars).length - 1)) index = lim;
            if(index < 0) index = 0;
            correctScrollBarPosition(list, selected = index);
        }
    }

    public void setList(char[] list) {
        int len;
        if(list != null)
        {
            Array.copy(list, 0, list = new char[len = list.length], 0, len);
        } else
        {
            list = new char[len = 0];
        }
        synchronized(monitor)
        {
            int lim;
            int index;
            if((index = selected) > (lim = len - 1)) index = lim;
            if(index < 0) index = 0;
            correctScrollBarRange(list.length);
            correctScrollBarPosition(chars = list, selected = index);
        }
    }

    public void setList(String list) {
        char[] clist = list == null ? new char[0] : list.toCharArray();
        synchronized(monitor)
        {
            int lim;
            int index;
            if((index = selected) > (lim = clist.length - 1)) index = lim;
            if(index < 0) index = 0;
            correctScrollBarRange(clist.length);
            correctScrollBarPosition(chars = clist, selected = index);
        }
    }

    public void setFont(Font font) {
        if(font == null) font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM);
        synchronized(monitor)
        {
            char[] list;
            this.font = font;
            correctScrollBarRange((list = chars).length);
            correctScrollBarPosition(list, selected);
        }
    }

    public char getChar(int index) {
        char[] list;
        if(index < 0 || index >= (list = chars).length)
        {
            throw new IndexOutOfBoundsException("CharacterList.getChar: аргумент index выходит из диапазона.");
        }
        return list[index];
    }

    public char getSelectedChar() {
        int lim;
        int index;
        char[] list;
        if((lim = (list = chars).length - 1) < 0)
        {
            throw new IndexOutOfBoundsException("CharacterList.getSelectedChar: список символов пуст.");
        }
        if((index = selected) > lim) index = lim;
        if(index < 0) index = 0;
        return list[index];
    }

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

    public int getSize() {
        return chars.length;
    }

    public char[] getList() {
        int len;
        char[] result;
        Array.copy(result = chars, 0, result = new char[len = result.length], 0, len);
        return result;
    }

    public Font getFont() {
        return font;
    }

    protected void paint(Graphics render) {
        int temp;
        int cols;
        int line;
        int srow;
        int frow;
        int length;
        int selected = this.selected;
        char[] chars = this.chars;
        Font font = this.font;
        cols = super.getWidth() / 32;
        line = font.getHeight() + 8;
        srow = (temp = render.getClipY()) / line;
        frow = (temp + render.getClipHeight() - 1) / line;
        length = chars.length;
        if(cols < 1) cols = 1;
        render.setFont(font);
        label0: for(int index = srow * cols, top = srow * line, row = srow; row <= frow; top += line, row++) for(int left = 0, col = 0; col < cols; index++, left += 32, col++)
        {
            if(index >= length) break label0;
            if(index != selected)
            {
                render.setColor(RasterCanvas.getSystemColor(0x07));
            } else
            {
                render.setColor(RasterCanvas.getSystemColor(0x0d));
                render.fillRect(left, top, 32, line);
                render.setColor(RasterCanvas.getSystemColor(0x0e));
            }
            render.drawChar(chars[index], left + 16, top + 4, Graphics.TOP | Graphics.HCENTER);
        }
    }

    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)
            {
                int cols;
                if((cols = super.getWidth() / 32) < 1) cols = 1;
                synchronized(monitor)
                {
                    int index;
                    if((index = selected) >= cols) index -= cols;
                    correctScrollBarPosition(chars, selected = index, cols);
                }
                break;
            }
            if(settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_DOWN) == key)
            {
                int cols;
                if((cols = super.getWidth() / 32) < 1) cols = 1;
                synchronized(monitor)
                {
                    int index;
                    char[] list;
                    if((index = selected) < ((list = chars).length - cols)) index += cols;
                    correctScrollBarPosition(list, selected = index, cols);
                }
                break;
            }
            if(settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_LEFT) == key)
            {
                synchronized(monitor)
                {
                    int index;
                    if((index = selected) > 0) index--;
                    correctScrollBarPosition(chars, selected = index);
                }
                break;
            }
            if(settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_RIGHT) == key)
            {
                synchronized(monitor)
                {
                    int lim;
                    int index;
                    char[] list;
                    if((index = selected) < (lim = (list = chars).length - 1))
                    {
                        index++;
                    } else
                    {
                        index = lim;
                    }
                    correctScrollBarPosition(list, selected = index);
                }
                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
        )
        {
            int cols = super.getWidth() / 32;
            synchronized(monitor)
            {
                int lim;
                int line = font.getHeight() + 8;
                int index = (event.getX() / 32) + (event.getY() / line) * (cols < 1 ? 1 : cols);
                char[] list;
                if(index > (lim = (list = chars).length - 1)) index = lim;
                if(index < 0) index = 0;
                correctScrollBarPosition(list, selected = index);
            }
        }
    }

    private void correctScrollBarRange(int chars) {
        int cols = super.getWidth() / 32;
        int line = font.getHeight() + 8;
        int rows = chars / (cols = cols < 1 ? 1 : cols) + (chars % cols <= 0 ? 0 : 1);
        super.getScrollBar().setRange(rows * line);
    }

    private void correctScrollBarPosition(char[] chars, int selected) {
        correctScrollBarPosition(chars, selected, super.getWidth() / 32);
    }

    private void correctScrollBarPosition(char[] chars, int selected, int cols) {
        int pos;
        int page;
        int line = font.getHeight() + 8;
        int top1 = (selected / (cols < 1 ? 1 : cols)) * 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();
        }
    }
}
