﻿using System;
using System.Collections.Generic;
using System.Threading;

/*
 * This file is part of lanterna (http://code.google.com/p/lanterna/).
 *
 * lanterna is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright (C) 2010-2017 Martin Berglund
 */
namespace com.googlecode.lanterna.gui2
{

	/// <summary>
	/// Abstract implementation of <seealso cref="TextGUIThread"/> with common logic for both available concrete implementations.
	/// </summary>
	public abstract class AbstractTextGUIThread : TextGUIThread
	{

		protected internal readonly TextGUI textGUI;
		protected internal readonly LinkedList<ThreadStart> customTasks;
		protected internal ExceptionHandler exceptionHandler;

		/// <summary>
		/// Sets up this <seealso cref="AbstractTextGUIThread"/> for operations on the supplies <seealso cref="TextGUI"/> </summary>
		/// <param name="textGUI"> Text GUI this <seealso cref="TextGUIThread"/> implementations will be operating on </param>
		public AbstractTextGUIThread(TextGUI textGUI)
		{
			this.exceptionHandler = new ExceptionHandlerAnonymousInnerClass(this);
			this.textGUI = textGUI;
			this.customTasks = new LinkedBlockingQueue<ThreadStart>();
		}

		private class ExceptionHandlerAnonymousInnerClass : ExceptionHandler
		{
			private readonly AbstractTextGUIThread outerInstance;

			public ExceptionHandlerAnonymousInnerClass(AbstractTextGUIThread outerInstance)
			{
				this.outerInstance = outerInstance;
			}

			public override bool onIOException(IOException e)
			{
				Console.WriteLine(e.ToString());
				Console.Write(e.StackTrace);
				return true;
			}

			public override bool onRuntimeException(Exception e)
			{
				Console.WriteLine(e.ToString());
				Console.Write(e.StackTrace);
				return true;
			}
		}

//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: @Override public void invokeLater(Runnable runnable) throws IllegalStateException
		public override void invokeLater(ThreadStart runnable)
		{
			customTasks.AddLast(runnable);
		}

		public override ExceptionHandler ExceptionHandler
		{
			set
			{
				if (value == null)
				{
					throw new System.ArgumentException("Cannot call setExceptionHandler(null)");
				}
				this.exceptionHandler = value;
			}
		}

//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: @Override public synchronized boolean processEventsAndUpdate() throws java.io.IOException
		public override bool processEventsAndUpdate()
		{
			lock (this)
			{
				if (Thread != Thread.CurrentThread)
				{
					throw new System.InvalidOperationException("Calling processEventAndUpdate outside of GUI thread");
				}
				textGUI.processInput();
				while (customTasks.Count > 0)
				{
					ThreadStart r = customTasks.RemoveFirst();
					if (r != null)
					{
						r.run();
					}
				}
				if (textGUI.PendingUpdate)
				{
					textGUI.updateScreen();
					return true;
				}
				return false;
			}
		}

//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: @Override public void invokeAndWait(final Runnable runnable) throws IllegalStateException, InterruptedException
//JAVA TO C# CONVERTER WARNING: 'final' parameters are not available in .NET:
		public override void invokeAndWait(ThreadStart runnable)
		{
			Thread guiThread = Thread;
			if (guiThread == null || Thread.CurrentThread == guiThread)
			{
				runnable.run();
			}
			else
			{
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final java.util.concurrent.CountDownLatch countDownLatch = new java.util.concurrent.CountDownLatch(1);
				CountDownLatch countDownLatch = new CountDownLatch(1);
				invokeLater(() =>
			{
				try
				{
					runnable.run();
				}
				finally
				{
					countDownLatch.countDown();
				}
			});
				countDownLatch.await();
			}
		}
	}

}