/*
    Редактор шрифтов UFN
    Эта программа создана специально для программы Малик Эмулятор.

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

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

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

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

package ru.malik.elaborarer.ufneditor;

import com.nokia.mid.ui.*;
import java.util.*;
import javax.microedition.lcdui.*;
import malik.emulator.fileformats.font.*;
import malik.emulator.fileformats.font.UnicodeRasterFont.*;

public final class GlyphList extends FontCustomItem
{
    private static final int COLOR_SELECTED = 0xaa008800;
    private static final int COLOR_SHADOW   = 0x55202020;
    private static final int COLOR_GLYPH    = 0xff003300;

    private static final Image DIGITS;

    static {
        Image digits;
        try
        {
            digits = Image.createImage("/digits.png");
        }
        catch(Exception e)
        {
            digits = Image.createImage(16, 1);
        }
        DIGITS = digits;
    }

    private boolean focused;
    private int cols;
    private int selected;
    private int[] codes;

    public GlyphList(UnicodeRasterFont font) {
        super(font);
    }

    public void notifyFontChanged() {
        invalidate();
    }

    public void fillGlyphCodes(Gauge progressBar) {
        int i = 0;
        int len;
        int[] codes;
        UnicodeRasterFont font = this.font;
        progressBar.setValue(0);
        progressBar.setMaxValue((len = font.getGlyphsCount()) <= 0 ? 1 : len);
        this.codes = codes = len > 0 ? new int[len] : null;
        if(len > 0)
        {
            for(Enumeration e = font.glyphs(); e.hasMoreElements(); progressBar.setValue(++i)) codes[i] = ((Glyph) e.nextElement()).getCharacterCode();
            if(selected >= len) selected = len - 1;
        } else
        {
            selected = 0;
        }
        invalidate();
    }

    public void setSelectedCode(int code) {
        int[] codes;
        if((codes = this.codes) != null) for(int result, limit1 = 0, limit2 = codes.length - 1; limit1 <= limit2; )
        {
            int guess;
            if((guess = codes[result = (limit1 + limit2) >> 1] - code) < 0)
            {
                limit1 = result + 1;
            }
            else if(guess > 0)
            {
                limit2 = result - 1;
            }
            else
            {
                selected = result;
                invalidate();
                break;
            }
        }
    }

    public boolean isEmpty() {
        return codes == null;
    }

    public int getSelectedCode() {
        int selected;
        int[] codes;
        return (codes = this.codes) != null && (selected = this.selected) >= 0 && selected < codes.length ? codes[selected] : 0;
    }

    protected void paint(Graphics renderA, int contentW, int contentH) {
        int len;
        int cols;
        int right;
        int clipW;
        int clipH;
        int clipL = renderA.getClipX();
        int clipT = renderA.getClipY();
        int clipR = clipL + (clipW = renderA.getClipWidth()) - 1;
        int clipB = clipT + (clipH = renderA.getClipHeight()) - 1;
        int cellW = getMinContentWidth();
        int cellH = getMinContentHeight();
        int[] codes;
        Image digits = DIGITS;
        UnicodeRasterFont font = this.font;
        DirectGraphics renderB = DirectUtils.getDirectGraphics(renderA);
        if((cols = contentW / cellW) < 1) cols = 1;
        if((clipR > (right = cols * cellW - 1))) clipW = (clipR = right) - clipL + 1;
        if(clipL <= clipR && (codes = this.codes) != null && (len = codes.length) > 0)
        {
            int gh;
            int index = focused ? selected : -1;
            int index0 = (clipT / cellH) * cols + (clipL / cellW);
            int index1 = (clipB / cellH) * cols + (clipR / cellW);
            int digitW = digits.getWidth() >> 4;
            int digitH = digits.getHeight();
            int bp = (gh = font.getHeight()) - font.getBaselineHeight() + 1;
            for(int i = index0, cellL = (i % cols) * cellW, cellT = (i / cols) * cellH; i < len && i <= index1; i++)
            {
                if(cellL <= clipR && cellT <= clipB && cellL + cellW > clipL && cellT + cellH > clipT)
                {
                    int code;
                    Glyph glyph;
                    if(i == index)
                    {
                        renderB.setARGBColor(COLOR_SELECTED);
                        renderA.fillRoundRect(cellL, cellT, cellW, cellH, 2, 2);
                    }
                    if((code = codes[i]) >= 0x0000 && code <= 0xffff)
                    {
                        for(int x = (cellL + 4) + (digitW << 1), y = (cellT + cellH) - (digitH + 1), s = 12; s >= 0; x += digitW, s -= 4)
                        {
                            renderA.drawRegion(digits, ((code >> s) & 0x0f) * digitW, 0, digitW, digitH, 0, x, y, 0);
                        }
                    }
                    else if(code >= 0x010000 && code <= 0xffffff)
                    {
                        for(int x = (cellL + 4) + digitW, y = (cellT + cellH) - (digitH + 1), s = 20; s >= 0; x += digitW, s -= 4)
                        {
                            renderA.drawRegion(digits, ((code >> s) & 0x0f) * digitW, 0, digitW, digitH, 0, x, y, 0);
                        }
                    }
                    else
                    {
                        for(int x = (cellL + 4), y = (cellT + cellH) - (digitH + 1), s = 28; s >= 0; x += digitW, s -= 4)
                        {
                            renderA.drawRegion(digits, ((code >> s) & 0x0f) * digitW, 0, digitW, digitH, 0, x, y, 0);
                        }
                    }
                    if((glyph = font.getGlyph(code)) != null)
                    {
                        int gw = glyph.getWidth();
                        int rl = glyph.getRawLeft();
                        int rb = glyph.getRawBottom();
                        int rw = glyph.getRawWidth();
                        int rh = glyph.getRawHeight();
                        if(rw > 0 && rh > 0)
                        {
                            int x = cellL + rl + ((cellW - gw) >> 1);
                            int y = cellT + bp - rh - rb;
                            int[] pixels = new int[rw * rh];
                            renderA.clipRect(cellL + 4, cellT + 1, cellW - 8, gh + 1);
                            glyph.getPixels(pixels, 0, rw, COLOR_SHADOW);
                            renderA.drawRGB(pixels, 0, rw, x + 1, y + 1, rw, rh, true);
                            glyph.getPixels(pixels, 0, rw, COLOR_GLYPH);
                            renderA.drawRGB(pixels, 0, rw, x, y, rw, rh, true);
                            renderA.setClip(clipL, clipT, clipW, clipH);
                        }
                    }
                }
                if((cellL += cellW) > right)
                {
                    cellL = 0;
                    cellT += cellH;
                }
            }
        }
        this.cols = cols;
    }

    protected void pointerPressed(int x, int y) {
        pointerHandle(x, y);
    }

    protected void pointerDragged(int x, int y) {
        pointerHandle(x, y);
    }

    protected void pointerReleased(int x, int y) {
        pointerHandle(x, y);
    }

    protected void traverseOut() {
        focused = false;
        repaint();
    }

    protected boolean traverse(int direction, int viewportWidth, int viewportHeight, int[] visibleRectangle) {
        boolean result;
        int len;
        int row;
        int col;
        int rows;
        int cols = this.cols;
        int index = selected;
        int[] codes;
        if(cols < 1) cols = 1;
        len = (codes = this.codes) != null ? codes.length : 0;
        rows = (len / cols) + (len % cols > 0 ? 1 : 0);
        col = index % cols;
        row = index / cols;
        if(focused)
        {
            switch(direction)
            {
            case Canvas.UP:
                if(result = row > 0) row--;
                break;
            case Canvas.DOWN:
                if(result = row < rows - 1) row++;
                break;
            case Canvas.LEFT:
                if(result = col > 0) col--;
                break;
            case Canvas.RIGHT:
                if(result = col < cols - 1) col++;
                break;
            default:
                result = true;
                break;
            }
        } else
        {
            focused = result = true;
            switch(direction)
            {
            case Canvas.DOWN:
            case Canvas.RIGHT:
                row = col = 0;
                break;
            case Canvas.UP:
            case Canvas.LEFT:
                row = rows - 1;
                col = cols - 1;
                break;
            }
        }
        if(result)
        {
            int newindex;
            int cellW = getMinContentWidth();
            int cellH = getMinContentHeight();
            if((newindex = col + row * cols) >= len) newindex = len - 1;
            if(newindex < 0) newindex = 0;
            if(index != (selected = newindex))
            {
                repaint();
                super.notifyStateChanged();
            }
            visibleRectangle[0] = (newindex % cols) * cellW;
            visibleRectangle[1] = (newindex / cols) * cellH;
            visibleRectangle[2] = cellW;
            visibleRectangle[3] = cellH;
            return true;
        }
        return false;
    }

    protected int getMinContentWidth() {
        return ((DIGITS.getWidth() >> 4) << 3) + 8;
    }

    protected int getMinContentHeight() {
        return DIGITS.getHeight() + font.getHeight() + 4;
    }

    protected int getPrefContentWidth(int contentHeight) {
        int len;
        int cols;
        int rows;
        int[] codes;
        len = (codes = this.codes) != null ? codes.length : 0;
        if(contentHeight < 0) return 0;
        if((rows = contentHeight / getMinContentHeight()) < 1) rows = 1;
        for(cols = len / rows; cols * rows < len; cols++);
        return cols * getMinContentWidth();
    }

    protected int getPrefContentHeight(int contentWidth) {
        int len;
        int cols;
        int[] codes;
        len = (codes = this.codes) != null ? codes.length : 0;
        if(contentWidth < 0) return 0;
        if((cols = contentWidth / getMinContentWidth()) < 1) cols = 1;
        return ((len / cols) + (len % cols > 0 ? 1 : 0)) * getMinContentHeight();
    }

    private void pointerHandle(int x, int y) {
        int len;
        int row;
        int col;
        int cols = this.cols;
        int newindex;
        int[] codes;
        if(cols < 1) cols = 1;
        len = (codes = this.codes) != null ? codes.length : 0;
        col = x / getMinContentWidth();
        row = y / getMinContentHeight();
        if((newindex = col + row * cols) >= len) newindex = len - 1;
        if(newindex < 0) newindex = 0;
        if(selected != (selected = newindex))
        {
            invalidate();
            super.notifyStateChanged();
        }
    }
}
