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

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

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

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

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


package java.util;

public final class TimerThread extends Thread
{
	private static final Object LOCK;

	static
	{
		LOCK = new Object();
	}


	private boolean terminated;
	private int count;
	private TimerTask[] queue;
	private Object lock;

	TimerThread()
	{
		super("Поток событий таймера");
		this.queue = new TimerTask[16];
		this.lock = new Object();
	}

	public void run()
	{
		boolean mustContinue;
		boolean fixedRate;
		TimerTask task;
		Object monitor = lock;
		do
		{
			waitTimer();
			if(terminated)
			{
				break;
			}
			if((task = extract()) == null)
			{
				continue;
			}
			mustContinue = false;
			synchronized(monitor)
			{
				label0:
				{
					fixedRate = task.fixedRate;
					if(task.state != TimerTask.STATE_SCHEDULED)
					{
						task.owner = null;
						mustContinue = true;
						break label0;
					}
					task.shedExecutionTime = task.nextExecutionTime;
					if(fixedRate)
					{
						task.nextExecutionTime = System.currentTimeMillis() + task.period;
					}
					task.state = TimerTask.STATE_EXECUTING;
				}
			}
			if(mustContinue)
			{
				continue;
			}
			try
			{
				task.run();
			}
			catch(RuntimeException e)
			{
				e.printRealStackTrace();
			}
			synchronized(monitor)
			{
				label0:
				{
					if(task.state != TimerTask.STATE_EXECUTING)
					{
						task.owner = null;
						break label0;
					}
					if(!fixedRate)
					{
						task.nextExecutionTime = System.currentTimeMillis() + task.period;
					}
					if(task.periodic)
					{
						task.state = TimerTask.STATE_SCHEDULED;
						pushTask(task);
					} else
					{
						task.state = TimerTask.STATE_CANCELLED;
						task.owner = null;
					}
				}
			}
		} while(true);
		clear();
	}

	void terminate()
	{
		Object monitor;
		synchronized(monitor = lock)
		{
			terminated = true;
			monitor.notify();
		}
	}

	void schedule(TimerTask task, long time)
	{
		int error;
		Object monitor;
		error = 0;
		synchronized(LOCK)
		{
			label0:
			{
				if(task.state != TimerTask.STATE_FREE)
				{
					error = 1;
					break label0;
				}
				task.periodic = false;
				task.fixedRate = false;
				task.state = TimerTask.STATE_SCHEDULED;
				task.period = 0L;
			}
		}
		if(error == 1)
		{
			throw new IllegalStateException("Timer.schedule: " +
					"задача уже планировалась раннее.");
		}
		synchronized(monitor = lock)
		{
			task.nextExecutionTime = time;
			task.owner = this;
			pushTask(task);
			monitor.notify();
		}
	}

	void schedule(TimerTask task, long time, long period, boolean fixedRate)
	{
		int error;
		Object monitor;
		error = 0;
		synchronized(LOCK)
		{
			label0:
			{
				if(task.state != TimerTask.STATE_FREE)
				{
					error = 1;
					break label0;
				}
				task.periodic = true;
				task.fixedRate = fixedRate;
				task.state = TimerTask.STATE_SCHEDULED;
				task.period = Math.max(0L, period);
			}
		}
		if(error == 1)
		{
			throw new IllegalStateException(
					(fixedRate ? "Timer.scheduleAtFixedRate" : "Timer.schedule").concat(": " +
					"задача уже планировалась раннее."));
		}
		synchronized(monitor = lock)
		{
			task.nextExecutionTime = time;
			task.owner = this;
			pushTask(task);
			monitor.notify();
		}
	}

	boolean cancel(TimerTask task)
	{
		boolean result;
		int index;
		int count;
		TimerTask[] queue;
		Object monitor;
		synchronized(monitor = lock)
		{
			label0:
			{
				if(task.owner != this)
				{
					result = false;
					break label0;
				}
				result = task.periodic || task.state == TimerTask.STATE_SCHEDULED;
				if((index = indexOf(task)) < 0)
				{
					task.state = TimerTask.STATE_CANCELLED;
					break label0;
				}
				queue = this.queue;
				if(index < (count = this.count) - 1)
				{
					Array.copy(queue, index + 1, queue, index, count - index - 1);
				}
				queue[this.count = count - 1] = null;
				task.state = TimerTask.STATE_CANCELLED;
				task.owner = null;
				monitor.notify();
			}
		}
		return result;
	}

	private void waitTimer()
	{
		int count;
		long millis;
		TimerTask[] queue;
		Object monitor;
		synchronized(monitor = this.lock)
		{
			do
			{
				count = this.count;
				queue = this.queue;
				try
				{
					if(count > 0 && (millis = queue[count - 1].nextExecutionTime -
							System.currentTimeMillis()) > 0L)
					{
						monitor.wait(millis);
					}
					else if(count == 0)
					{
						monitor.wait();
					}
					break;
				}
				catch(InterruptedException e)
				{
					e.printRealStackTrace();
				}
			} while(true);
		}
	}

	private void clear()
	{
		int i;
		TimerTask[] queue = this.queue;
		TimerTask task;
		synchronized(lock)
		{
			for(i = count; i-- > 0; )
			{
				(task = queue[i]).state = TimerTask.STATE_CANCELLED;
				task.owner = null;
				queue[i] = null;
			}
			count = 0;
		}
	}

	private void pushTask(TimerTask task)
	{
		int i;
		int index;
		int count;
		long time;
		TimerTask[] queue;
		if((count = this.count) == (queue = this.queue).length)
		{
			Array.copy(queue, 0, queue = this.queue = new TimerTask[count << 1], 0, count);
		}
		time = task.nextExecutionTime;
		index = count;
		for(i = 0; i < count; i++)
		{
			if(queue[i].nextExecutionTime <= time)
			{
				index = i;
				break;
			}
		}
		if(index < count)
		{
			Array.copy(queue, index, queue, index + 1, count - index);
		}
		queue[index] = task;
		this.count = count + 1;
	}

	private int indexOf(TimerTask task)
	{
		int i;
		TimerTask[] queue = this.queue;
		for(i = count; i-- > 0; )
		{
			if(queue[i] == task)
			{
				return i;
			}
		}
		return -1;
	}

	private TimerTask extract()
	{
		int count;
		TimerTask[] queue;
		TimerTask result;
		synchronized(lock)
		{
			if((count = this.count) > 0 &&
					(result = (queue = this.queue)[count - 1]).nextExecutionTime <=
					System.currentTimeMillis())
			{
				queue[this.count = count - 1] = null;
			} else
			{
				result = null;
			}
		}
		return result;
	}
}
