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

import java.io.*;
import java.util.*;
import malik.emulator.util.*;

public final class InboundConnections extends Object
{
    public static final int TCP = 0;
    public static final int UDP = 1;

    public static final InboundConnections instance;

    static {
        instance = new InboundConnections();
    }

    final Hashtable connections;
    final RunnableQueue events;

    private InboundConnections() {
        this.connections = new Hashtable();
        this.events = new RunnableQueue();
        ((Thread) (new Thread("Обработчик событий от входящих соединений") {
            public void run() {
                for(RunnableQueue events = (InboundConnections.this).events; ; )
                {
                    Runnable event = null;
                    synchronized(events)
                    {
                        for(; ; )
                        {
                            try
                            {
                                events.wait();
                                break;
                            }
                            catch(InterruptedException e)
                            {
                                e.printRealStackTrace();
                            }
                        }
                        if(!events.isEmpty())
                        {
                            event = events.peekHeadRunnable();
                            ((Queue) events).removeHeadElement();
                        }
                    }
                    if(event != null)
                    {
                        try
                        {
                            event.run();
                        }
                        catch(RuntimeException e)
                        {
                            e.printRealStackTrace();
                        }
                    }
                }
            }
        })).start();
    }

    public void registerLocalPort(int protocol, int localPort) throws IOException {
        int error;
        if(protocol < 0 || protocol > 1)
        {
            throw new IllegalArgumentException("InboundConnections.registerLocalPort: аргумент protocol имеет недопустимое значение.");
        }
        if(localPort < 0x0000 || localPort > 0xffff)
        {
            throw new IllegalArgumentException("InboundConnections.registerLocalPort: аргумент localPort имеет недопустимое значение.");
        }
        error = 0;
        synchronized(events)
        {
            label0:
            {
                Hashtable connections = this.connections;
                Integer key = new Integer(protocol << 16 | localPort);
                if(connections.containsKey(key))
                {
                    error = 1;
                    break label0;
                }
                connections.put(key, this);
            }
        }
        if(error == 1)
        {
            throw new IOException("InboundConnections.registerLocalPort: порт уже зарезервирован.");
        }
    }

    public void registerInboundConnection(int protocol, int localPort, InboundConnection inboundConnection) {
        int error;
        if(protocol < 0 || protocol > 1)
        {
            throw new IllegalArgumentException("InboundConnections.registerInboundConnection: аргумент protocol имеет недопустимое значение.");
        }
        if(localPort < 0x0000 || localPort > 0xffff)
        {
            throw new IllegalArgumentException("InboundConnections.registerInboundConnection: аргумент localPort имеет недопустимое значение.");
        }
        if(inboundConnection == null)
        {
            throw new NullPointerException("InboundConnections.registerInboundConnection: аргумент inboundConnection равен нулевой ссылке.");
        }
        error = 0;
        synchronized(events)
        {
            label0:
            {
                Hashtable connections = this.connections;
                Integer key = new Integer(protocol << 16 | localPort);
                if(!connections.containsKey(key))
                {
                    error = 1;
                    break label0;
                }
                connections.put(key, inboundConnection);
            }
        }
        if(error == 1)
        {
            throw new IllegalStateException("InboundConnections.registerInboundConnection: заданный порт не был зарезервирован.");
        }
    }

    public void notifyReceiveData(int protocol, int localPort, final Object data) {
        RunnableQueue events;
        if(protocol < 0 || protocol > 1)
        {
            throw new IllegalArgumentException("InboundConnections.notifyReceiveData: аргумент protocol имеет недопустимое значение.");
        }
        if(localPort < 0x0000 || localPort > 0xffff)
        {
            throw new IllegalArgumentException("InboundConnections.notifyReceiveData: аргумент localPort имеет недопустимое значение.");
        }
        synchronized(events = this.events)
        {
            final Integer key = new Integer(protocol << 16 | localPort);
            events.addTailElement(new Runnable() {
                public void run() {
                    Object connection;
                    if((connection = (InboundConnections.this).connections.get(key)) instanceof InboundConnection) ((InboundConnection) connection).receiveData(data);
                }
            });
            events.notify();
        }
    }

    public void unregisterInboundConnection(int protocol, int localPort) {
        if(protocol < 0 || protocol > 1)
        {
            throw new IllegalArgumentException("InboundConnections.unregisterInboundConnection: аргумент protocol имеет недопустимое значение.");
        }
        if(localPort < 0x0000 || localPort > 0xffff)
        {
            throw new IllegalArgumentException("InboundConnections.unregisterInboundConnection: аргумент localPort имеет недопустимое значение.");
        }
        synchronized(events)
        {
            connections.remove(new Integer(protocol << 16 | localPort));
        }
    }
}
