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

import javax.microedition.lcdui.*;

public class LayerManager extends Object
{
    private int viewLeft;
    private int viewTop;
    private int viewWidth;
    private int viewHeight;
    private int count;
    private Layer[] layers;
    private final Object monitor;

    public LayerManager() {
        this.viewWidth = Integer.MAX_VALUE;
        this.viewHeight = Integer.MAX_VALUE;
        this.layers = new Layer[7];
        this.monitor = new Object();
    }

    public void insert(Layer layer, int index) {
        int error;
        if(layer == null)
        {
            throw new NullPointerException("LayerManager.insert: аргумент layer равен нулевой ссылке.");
        }
        error = 0;
        synchronized(monitor)
        {
            label0:
            {
                int c;
                int len;
                int prevIndex;
                Layer[] list;
                prevIndex = Array.findb(list = layers, (len = count) - 1, layer);
                if(index < 0 || index > (prevIndex >= 0 ? len - 1 : len))
                {
                    error = 1;
                    break label0;
                }
                if(prevIndex >= 0)
                {
                    if((c = --len - prevIndex) > 0) Array.copy(list, prevIndex + 1, list, prevIndex, c);
                }
                else if(len == list.length)
                {
                    Array.copy(list, 0, list = layers = new Layer[(len << 1) + 1], 0, len);
                }
                if((c = len++ - index) > 0) Array.copy(list, index, list, index + 1, c);
                list[index] = layer;
                count = len;
            }
        }
        if(error == 1)
        {
            throw new IndexOutOfBoundsException("LayerManager.insert: аргумент index выходит из диапазона.");
        }
    }

    public void remove(Layer layer) {
        if(layer == null)
        {
            throw new NullPointerException("LayerManager.remove: аргумент layer равен нулевой ссылке.");
        }
        synchronized(monitor)
        {
            int len;
            int index;
            Layer[] list;
            if((index = Array.findb(list = layers, len = count - 1, layer)) >= 0)
            {
                int c;
                if((c = len - index) > 0) Array.copy(list, index + 1, list, index, c);
                list[count = len] = null;
            }
        }
    }

    public void append(Layer layer) {
        if(layer == null)
        {
            throw new NullPointerException("LayerManager.insert: аргумент layer равен нулевой ссылке.");
        }
        synchronized(monitor)
        {
            int c;
            int len;
            int prevIndex;
            Layer[] list;
            if((prevIndex = Array.findb(list = layers, (len = count) - 1, layer)) >= 0)
            {
                if((c = --len - prevIndex) > 0) Array.copy(list, prevIndex + 1, list, prevIndex, c);
            }
            else if(len == list.length)
            {
                Array.copy(list, 0, list = layers = new Layer[(len << 1) + 1], 0, len);
            }
            list[len++] = layer;
            count = len;
        }
    }

    public void paint(Graphics render, int left, int top) {
        int clipLeft;
        int clipTop;
        int clipWidth;
        int clipHeight;
        if(render == null)
        {
            throw new NullPointerException("LayerManager.paint: аргумент render равен нулевой ссылке.");
        }
        clipLeft = render.getClipX();
        clipTop = render.getClipY();
        clipWidth = render.getClipWidth();
        clipHeight = render.getClipHeight();
        synchronized(monitor)
        {
            int viewLeft;
            int viewTop;
            int viewWidth;
            int viewHeight;
            int viewRight = (viewLeft = this.viewLeft) + (viewWidth = this.viewWidth);
            int viewBottom = (viewTop = this.viewTop) + (viewHeight = this.viewHeight);
            render.translate(left - viewLeft, top - viewTop);
            render.clipRect(viewLeft, viewTop, viewWidth, viewHeight);
            try
            {
                Layer[] list = layers;
                for(int i = count; i-- > 0; )
                {
                    int currLeft;
                    int currTop;
                    Layer c;
                    if((c = list[i]).visibility && (currLeft = c.left) < viewRight && (currTop = c.top) < viewBottom && currLeft + c.width > viewLeft && currTop + c.height > viewTop) c.paint(render);
                }
            }
            finally
            {
                render.translate(viewLeft - left, viewTop - top);
                render.setClip(clipLeft, clipTop, clipWidth, clipHeight);
            }
        }
    }

    public void setViewWindow(int left, int top, int width, int height) {
        if(width < 0)
        {
            throw new IllegalArgumentException("LayerManager.setViewWindow: аргумент width не может быть отрицательным.");
        }
        if(height < 0)
        {
            throw new IllegalArgumentException("LayerManager.setViewWindow: аргумент height не может быть отрицательным.");
        }
        synchronized(monitor)
        {
            this.viewLeft = left;
            this.viewTop = top;
            this.viewWidth = width;
            this.viewHeight = height;
        }
    }

    public int getSize() {
        return count;
    }

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