/*
	Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
	и других спецификаций для функционирования компактных приложений на языке
	Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

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

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

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

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


package malik.emulator.midp.protocol;

import java.io.*;
import java.util.*;
import javax.microedition.io.*;
import javax.wireless.messaging.*;
import malik.emulator.io.j2me.*;

public class SupportedConnections extends Object
		implements Runnable, VirtualConnectionFactory
{
	public static final int DEFAULT_PORT_FOR_RECEIVE_WIRELESS_MESSAGES = 1200;


	private InboundConnection[] inboundConnections;
	private QueueOfObjects events;
	private URLParser urlParser;

	public SupportedConnections()
	{
		this.inboundConnections = new InboundConnection[2];
		this.events = new QueueOfObjects(16);
		this.urlParser = new URLParser();
		(new Thread(this, "Обработчик событий от соединений")).start();
		openDefaultConnections();
	}

	public void closeConnection(Connection connection)
			throws IOException
	{
		int i;
		if(connection instanceof InboundConnection)
		{
			synchronized(events)
			{
				if((i = indexOfInboundConnection(connection)) >= 0)
				{
					inboundConnections[i] = null;
				}
			}
		}
	}

	public void registerConnection(String url, String midlet, String filter)
			throws IOException, ClassNotFoundException
	{
	}

	public boolean unregisterConnection(String url)
	{
		return false;
	}

	public boolean platformRequest(String url)
			throws ConnectionNotFoundException
	{
		return false;
	}

	public int checkPermission(String permission)
	{
		return PERMISSION_ALLOWED;
	}

	public String[] listConnections(boolean available)
	{
		return null;
	}

	public String getFilter(String url)
	{
		return null;
	}

	public String getMIDlet(String url)
	{
		return null;
	}

	public Connection openConnection(String url, int mode, boolean timeouts)
			throws IOException
	{
		int remotePort;
		String protocol;
		String userInfo;
		String remoteHost;
		String remotePath;
		String remoteQuery;
		URLParser parser;
		Connection result;
		(parser = urlParser).parse(url);
		protocol = parser.getProtocol();
		userInfo = parser.getUserInfo();
		remoteHost = parser.getHostName();
		remotePort = parser.getPort();
		remotePath = parser.getPath();
		remoteQuery = parser.getQuery();
		if((result = openConnection(url, protocol, userInfo, remoteHost, remotePort, remotePath,
				remoteQuery, mode, timeouts)) instanceof InboundConnection)
		{
			checkAndRegisterInboundConnection(protocol, remotePort, (InboundConnection) result);
		}
		return result;
	}

	public final void run()
	{
		Runnable event;
		QueueOfObjects events = this.events;
		do
		{
			waitEvent();
			if(events.isEmpty())
			{
				continue;
			}
			synchronized(events)
			{
				event = (Runnable) events.peek();
				events.removeTailElement();
			}
			try
			{
				event.run();
			}
			catch(Throwable e)
			{
				e.printRealStackTrace();
			}
		} while(true);
	}

	public final void setEvent(Runnable event)
	{
		QueueOfObjects events;
		synchronized(events = this.events)
		{
			events.push(event);
			events.notify();
		}
	}

	public final InboundConnection getInboundConnection(String protocol, int localPort)
	{
		int i;
		InboundConnection[] c;
		InboundConnection result;
		InboundConnection connection;
		String connectionProtocol;
		if(protocol == null)
		{
			return null;
		}
		for(result = null, i = (c = inboundConnections).length; i-- > 0; )
		{
			try
			{
				if((connection = c[i]) != null &&
						(connectionProtocol = connection.getProtocol()) != null &&
						protocol.equalsIgnoreCase(connectionProtocol) &&
						localPort == connection.getLocalPort())
				{
					result = connection;
					break;
				}
			}
			catch(IOException e)
			{
				/* проигнорировать исключение */
			}
		}
		return result;
	}

	protected void openDefaultConnections()
	{
		MessageConnection defaultMessagePort;
		try
		{
			(defaultMessagePort = (MessageConnection) openConnection("sms://:" +
					DEFAULT_PORT_FOR_RECEIVE_WIRELESS_MESSAGES, MODE_READ_WRITE, false)).
					setMessageListener(new WirelessMessageDefaultPortListener(defaultMessagePort));
		}
		catch(IOException e)
		{
			e.printRealStackTrace();
		}
	}

	protected Connection openConnection(String url, String protocol, String userInfo,
			String remoteHost, int remotePort, String remotePath, String remoteQuery,
			int mode, boolean timeouts)
			throws IOException
	{
		Connection result;
		label0:
		{
			if("file".equalsIgnoreCase(protocol))
			{
				if((mode & MODE_READ_WRITE) != MODE_READ)
				{
					throw new IOException("Connector.open: " +
							"неподдерживаемый режим открытия (протокол file).");
				}
				if((userInfo == null || userInfo.length() <= 0) &&
						(remoteHost == null || remoteHost.length() <= 0) &&
						remotePort == URLParser.EMPTY_PORT &&
						(remoteQuery == null || remoteQuery.length() <= 0))
				{
					if(remotePath == null || remotePath.length() <= 0)
					{
						remotePath = "/";
					}
					result = new malik.emulator.midp.protocol.file.ReadOnlyFileConnection(
							url, remotePath);
					break label0;
				}
				throw new IllegalArgumentException("Connector.open: " +
						"недопустимый адрес (протокол file).");
			}
			else if("resource".equalsIgnoreCase(protocol))
			{
				if((mode & MODE_READ_WRITE) != MODE_READ)
				{
					throw new IOException("Connector.open: " +
							"неподдерживаемый режим открытия (протокол resource).");
				}
				if((userInfo == null || userInfo.length() <= 0) &&
						(remoteHost == null || remoteHost.length() <= 0) &&
						remotePort == URLParser.EMPTY_PORT &&
						(remoteQuery == null || remoteQuery.length() <= 0))
				{
					if(remotePath == null || remotePath.length() <= 0)
					{
						remotePath = "/";
					}
					result = new malik.emulator.midp.protocol.resource.ReadOnlyResourceConnection(
							url, remotePath);
					break label0;
				}
				throw new IllegalArgumentException("Connector.open: " +
						"недопустимый адрес (протокол resource).");
			}
			else if("sms".equalsIgnoreCase(protocol))
			{
				if((userInfo == null || userInfo.length() <= 0) &&
						(remotePath == null || remotePath.length() <= 0) &&
						(remoteQuery == null || remoteQuery.length() <= 0))
				{
					if(remoteHost != null && remoteHost.length() > 0)
					{
						result = new malik.emulator.midp.protocol.sms.ClientMessageConnection(
								url);
						break label0;
					}
					if(remotePort >= 0)
					{
						result = new malik.emulator.midp.protocol.sms.ServerMessageConnection(
								url, remotePort);
						break label0;
					}
				}
				throw new IllegalArgumentException("Connector.open: " +
						"недопустимый адрес (протокол sms).");
			}
			else
			{
				throw new ConnectionNotFoundException("Connector.open: " +
						"неизвестный протокол " + protocol);
			}
		}
		return result;
	}

	protected final void checkAndRegisterInboundConnection(String protocol,
			int localPort, InboundConnection inboundConnection)
			throws IOException
	{
		boolean found;
		int i;
		int len;
		int freedIndex;
		InboundConnection[] c;
		InboundConnection connection;
		String connectionProtocol;
		if(protocol == null || inboundConnection == null)
		{
			return;
		}
		synchronized(events)
		{
			found = false;
			freedIndex = -1;
			for(i = len = (c = inboundConnections).length; i-- > 0; )
			{
				if((connection = c[i]) == null)
				{
					freedIndex = i;
					continue;
				}
				try
				{
					if((connectionProtocol = connection.getProtocol()) != null &&
							protocol.equalsIgnoreCase(connectionProtocol) &&
							localPort == connection.getLocalPort())
					{
						found = true;
						break;
					}
				}
				catch(IOException e)
				{
					e.printRealStackTrace();
				}
			}
			if(!found)
			{
				if(freedIndex < 0)
				{
					Array.copy(c, 0, c = inboundConnections =
							new InboundConnection[(freedIndex = len) << 1], 0, freedIndex);
				}
				c[freedIndex] = inboundConnection;
			}
		}
		if(found)
		{
			throw new IOException("Connector.open: " +
					"локальный порт " + localPort + " по протоколу " + protocol +
					" уже занят.");
		}
	}

	protected final URLParser getURLParser()
	{
		return urlParser;
	}

	private void waitEvent()
	{
		Object monitor;
		synchronized(monitor = events)
		{
			do
			{
				try
				{
					monitor.wait();
					break;
				}
				catch(InterruptedException e)
				{
					e.printRealStackTrace();
				}
			} while(true);
		}
	}

	private int indexOfInboundConnection(Connection connection)
	{
		int i;
		Connection[] c;
		for(i = (c = inboundConnections).length; i-- > 0; )
		{
			if(connection == c[i])
			{
				return i;
			}
		}
		return -1;
	}
}
