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

import java.util.*;

public class DataElement extends Object
{
    private static final class Sequence extends Object
    {
        int length;
        Object[] elements;

        public Sequence() {
            this.elements = new DataElement[3];
        }

        public synchronized void addElement(DataElement element) {
            int len;
            Object[] elems;
            if((elems = elements).length == (len = length)) Array.copy(elems, 0, elems = elements = new DataElement[(len << 1) + 1], 0, len);
            elems[len++] = element;
            length = len;
        }

        public void insertElementAt(DataElement element, int index) {
            int len;
            Object[] elems;
            if((elems = elements).length == (len = length)) Array.copy(elems, 0, elems = elements = new DataElement[(len << 1) + 1], 0, len);
            if(index < len) Array.copy(elems, index, elems, index + 1, len - index);
            elems[index] = element;
            length = len + 1;
        }

        public synchronized boolean removeElement(DataElement element) {
            int len;
            int index;
            Object[] elems;
            if((index = Array.findf(elems = elements, 0, element)) > (len = length - 1)) return false;
            if(index < len) Array.copy(elems, index + 1, elems, index, len - index);
            elems[length = len] = null;
            return true;
        }

        public Enumeration elements() {
            return new Enumeration() {
                private int index;

                public boolean hasMoreElements() {
                    return index < (Sequence.this).length;
                }

                public Object nextElement() {
                    int error = 0;
                    Object result;
                    Sequence parent;
                    synchronized(parent = Sequence.this)
                    {
                        label0:
                        {
                            int i;
                            if((i = index) >= parent.length)
                            {
                                error = 1;
                                result = null;
                                break label0;
                            }
                            result = parent.elements[i++];
                            index = i;
                        }
                    }
                    if(error == 1)
                    {
                        throw new NoSuchElementException("Enumeration.nextElement: элементов больше не осталось.");
                    }
                    return result;
                }
            };
        }
    }

    public static final int NULL = 0;
    public static final int U_INT_1 = 8;
    public static final int U_INT_2 = 9;
    public static final int U_INT_4 = 10;
    public static final int U_INT_8 = 11;
    public static final int U_INT_16 = 12;
    public static final int INT_1 = 16;
    public static final int INT_2 = 17;
    public static final int INT_4 = 18;
    public static final int INT_8 = 19;
    public static final int INT_16 = 20;
    public static final int URL = 64;
    public static final int UUID = 24;
    public static final int BOOL = 40;
    public static final int STRING = 32;
    public static final int DATSEQ = 48;
    public static final int DATALT = 56;

    private final byte type;
    private final Object value;

    public DataElement(int type) {
        Object value;
        switch(type)
        {
        case NULL:
            value = null;
            break;
        case DATSEQ:
        case DATALT:
            value = new Sequence();
            break;
        default:
            throw new IllegalArgumentException("DataElement: аргумент type имеет недопустимое значение.");
        }
        this.type = (byte) type;
        this.value = value;
    }

    public DataElement(boolean value) {
        this.type = BOOL;
        this.value = value ? Boolean.TRUE : Boolean.FALSE;
    }

    public DataElement(int type, long value) {
        switch(type)
        {
        case U_INT_1:
            if(value < 0L || value > 0xffL)
            {
                throw new IllegalArgumentException("DataElement: аргумент value выходит из диапазона.");
            }
            break;
        case U_INT_2:
            if(value < 0L || value > 0xffffL)
            {
                throw new IllegalArgumentException("DataElement: аргумент value выходит из диапазона.");
            }
            break;
        case U_INT_4:
            if(value < 0L || value > 0xffffffffL)
            {
                throw new IllegalArgumentException("DataElement: аргумент value выходит из диапазона.");
            }
            break;
        case INT_1:
            if(value < -0x80L || value > 0x7fL)
            {
                throw new IllegalArgumentException("DataElement: аргумент value выходит из диапазона.");
            }
            break;
        case INT_2:
            if(value < -0x8000L || value > 0x7fffL)
            {
                throw new IllegalArgumentException("DataElement: аргумент value выходит из диапазона.");
            }
            break;
        case INT_4:
            if(value < -0x80000000L || value > 0x7fffffffL)
            {
                throw new IllegalArgumentException("DataElement: аргумент value выходит из диапазона.");
            }
            break;
        case INT_8:
            break;
        default:
            throw new IllegalArgumentException("DataElement: аргумент type имеет недопустимое значение.");
        }
        this.type = (byte) type;
        this.value = new Long(value);
    }

    public DataElement(int type, Object value) {
        byte[] copy;
        switch(type)
        {
        case U_INT_8:
            if(!(value instanceof byte[]) || (copy = (byte[]) value).length != 8)
            {
                throw new IllegalArgumentException("DataElement: аргумент value имеет недопустимое значение.");
            }
            Array.copy(copy, 0, copy = new byte[8], 0, 8);
            value = copy;
            break;
        case U_INT_16:
        case INT_16:
            if(!(value instanceof byte[]) || (copy = (byte[]) value).length != 16)
            {
                throw new IllegalArgumentException("DataElement: аргумент value имеет недопустимое значение.");
            }
            Array.copy(copy, 0, copy = new byte[16], 0, 16);
            value = copy;
            break;
        case URL:
        case STRING:
            if(!(value instanceof String))
            {
                throw new IllegalArgumentException("DataElement: аргумент value имеет недопустимое значение.");
            }
            break;
        case UUID:
            if(!(value instanceof UUID))
            {
                throw new IllegalArgumentException("DataElement: аргумент value имеет недопустимое значение.");
            }
            break;
        default:
            throw new IllegalArgumentException("DataElement: аргумент type имеет недопустимое значение.");
        }
        this.type = (byte) type;
        this.value = value;
    }

    public void addElement(DataElement element) {
        Object value;
        if(!((value = this.value) instanceof Sequence))
        {
            throw new ClassCastException("DataElement.addElement: этот элемент данных не является типа DATSEQ или DATALT.");
        }
        if(element == null)
        {
            throw new NullPointerException("DataElement.addElement: аргумент element равен нулевой ссылке.");
        }
        ((Sequence) value).addElement(element);
    }

    public void insertElementAt(DataElement element, int index) {
        int error;
        Object value;
        if(!((value = this.value) instanceof Sequence))
        {
            throw new ClassCastException("DataElement.insertElementAt: этот элемент данных не является типа DATSEQ или DATALT.");
        }
        if(element == null)
        {
            throw new NullPointerException("DataElement.insertElementAt: аргумент element равен нулевой ссылке.");
        }
        error = 0;
        synchronized(value)
        {
            label0:
            {
                Sequence seq;
                if(index < 0 || index > (seq = (Sequence) value).length)
                {
                    error = 1;
                    break label0;
                }
                seq.insertElementAt(element, index);
            }
        }
        if(error == 1)
        {
            throw new IndexOutOfBoundsException("DataElement.insertElementAt: аргумент index выходит из диапазона.");
        }
    }

    public boolean removeElement(DataElement element) {
        Object value;
        if(!((value = this.value) instanceof Sequence))
        {
            throw new ClassCastException("DataElement.removeElement: этот элемент данных не является типа DATSEQ или DATALT.");
        }
        if(element == null)
        {
            throw new NullPointerException("DataElement.removeElement: аргумент element равен нулевой ссылке.");
        }
        return ((Sequence) value).removeElement(element);
    }

    public boolean getBoolean() {
        Object value;
        if((value = this.value) instanceof Boolean)
        {
            return ((Boolean) value).booleanValue();
        }
        throw new ClassCastException("DataElement.getBoolean: этот элемент данных не является типа BOOL.");
    }

    public int getSize() {
        Object value;
        if((value = this.value) instanceof Sequence) return ((Sequence) value).length;
        throw new ClassCastException("DataElement.getSize: этот элемент данных не является типа DATSEQ или DATALT.");
    }

    public int getDataType() {
        return type;
    }

    public long getLong() {
        Object value;
        if((value = this.value) instanceof Long) return ((Long) value).longValue();
        throw new ClassCastException("DataElement.getLong: этот элемент данных не является типа U_INT_1, U_INT_2, U_INT_4, INT_1, INT_2, INT_4 или INT_8.");
    }

    public Object getValue() {
        Object value;
        if((value = this.value) instanceof byte[])
        {
            int len;
            byte[] result;
            Array.copy(result = (byte[]) value, 0, result = new byte[len = result.length], 0, len);
            return result;
        }
        if(value instanceof Sequence) return ((Sequence) value).elements();
        if(value instanceof String || value instanceof UUID) return value;
        throw new ClassCastException("DataElement.getValue: этот элемент данных не является типа U_INT_8, U_INT_16, INT_16, URL, UUID, STRING, DATSEQ или DATALT.");
    }
}
