using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using NUnit.Framework;
using Win32;

namespace m3uTool.Tests
{
	/// <summary>
	/// Tests for the BridgeStream class.
	/// </summary>
	[TestFixture]
	public class BridgeStreamTests
	{
		#region Private Variables
		/// <summary>
		/// Bridge stream object to use for multi-threaded read/write testing.
		/// </summary>
		private BridgeStream mBridgeStream;

		/// <summary>
		/// The string read from a threaded read process.
		/// </summary>
		private string mReadString = "";

		/// <summary>
		/// The total length of writes for a threaded writing process.
		/// </summary>
		private int mTotalWriteLength = -1;
		
		/// <summary>
		/// Some non-random text data to test with.
		/// </summary>
		private const string mTestString = @"BEOWOLF: THE AUTHOR AND HIS TIMES   Who wrote Beowulf--the oldest known epic poem written in English--is a question that has mystified readers for centuries. It's generally thought that the poem was performed orally by the poet before a live audience, and that in this way it eventually passed down to readers and listeners.  Another theory is that the poem was recited by memory by a scop, a traveling entertainer who went from court to court, singing songs and telling stories, until it was finally written down at the request of a king who wanted to hear it again.   Because there are three major battle scenes in the poem, some readers believe that Beowulf was composed by three different authors.  Other readers claim that the sections that take place in Denmark and the sections that occur after Beowulf returns to Geatland were the work of different authors.  The majority of critics agree that because of the unified structure of the poem, with its interweaving of historical information into the flow of the main narrative, the poem was most likely composed by one person.   As you read the poem try to imagine yourself in the banquet hall of a large castle, eating and drinking with your friends.  The court entertainer--much like a stand-up comedian in a nightclub--begins telling his story.  Your presence in the hall means that you're probably a member of the aristocratic class, either a descendant of the founder of a particular tribe or one of your king's followers.  (Anglo-Saxon society was divided into two main classes:  the aristocracy and the proletariat. Beowulf, as you'll see, gives us very little information about the life of the average person in Anglo-Saxon society, but concerns itself exclusively with life in the court and on the battlefield.)   Most of the stories were written and recited during this time to entertain and instruct the members of the aristocratic class. The scop assumed that his audience was familiar with the stories of ancient times.  It was his job to make them as interesting and as vivid as possible.";

		#endregion

		#region Tests

		/// <summary>
		/// Tests
		/// </summary>
		[Test]
		public void ReadWriteSingleThreadTests()
		{
			mBridgeStream = new BridgeStream();
			WriteThread();
			ReadThread();
			Assert.AreEqual(mTestString, mReadString);
		}

		/// <summary>
		/// Tests the BridgeStream by reading and writing in separate threads.
		/// </summary>
		[Test]
		public void ReadWriteMultiThreadTests()
		{
			mBridgeStream = new BridgeStream();
			
			Thread readThread = new Thread(new ThreadStart(ReadThread));
			Thread writeThread = new Thread(new ThreadStart(WriteThread));
			readThread.Start();
			writeThread.Start();

			writeThread.Join();
			readThread.Join();
			Assert.AreEqual(mTestString, mReadString);
		}

		/// <summary>
		/// Tests the BridgeStream by reading and writing in separate threads.
		/// </summary>
		[Test]
		public void ReadWriteWithSeveralStreamsMultiThreadTests()
		{
			// Test with block on
			mBridgeStream = new BridgeStream();
			mBridgeStream.MaxBufferLength = 1026;
			mBridgeStream.BlockLastReadBuffer = true;

			Thread readThread = new Thread(new ThreadStart(ReadThread));
			Thread writeThread = new Thread(new ThreadStart(WriteWithSeveralStreamsThread));
			readThread.Start();
			writeThread.Start();

			writeThread.Join();
			readThread.Join();
			Assert.AreEqual(mTotalWriteLength, mReadString.Length);

			// Test with block off
			mBridgeStream = new BridgeStream();
			mBridgeStream.BlockLastReadBuffer = false;

			readThread = new Thread(new ThreadStart(ReadThread));
			writeThread = new Thread(new ThreadStart(WriteWithSeveralStreamsThread));
			readThread.Start();
			writeThread.Start();

			writeThread.Join();
			readThread.Join();
			
			// this next method is non-deterministic - it may end up reading the entire buffer,
			// but I wouldn't bet on it.
			Debug.WriteLine(String.Format("mTotalWriteLength={0}, mReadString.Length={1}", mTotalWriteLength, mReadString.Length));
			//Assert.AreNotEqual(mTotalWriteLength, mReadString.Length);
		}

		#endregion

		#region Thread methods
		/// <summary>
		/// Read function for the multithread test
		/// </summary>
		private void ReadThread()
		{
			StreamReader sr = new StreamReader(mBridgeStream);
			mReadString = ReadStringFromStreamReader(sr);
		}

		/// <summary>
		/// Write function for the multithread test
		/// </summary>
		private void WriteThread()
		{
			using (StreamWriter sw = new StreamWriter(mBridgeStream))
			{
				WriteDataToStreamWriter(sw, mTestString);
			}
		}
		
		private void WriteWithSeveralStreamsThread()
		{
			mTotalWriteLength = 0;
			
			for (int i = 0; i < 10; i++)
			{
				using (StreamWriter sw = new StreamWriter(mBridgeStream))
				{
					for (int j = 0; j < 20; j++ )
						mTotalWriteLength += WriteDataToStreamWriter(sw, mTestString);
					Thread.Sleep(100);
				}
			}
			mBridgeStream.BlockLastReadBuffer = false;
		}

		#endregion

		#region Read and Write Methods

		/// <summary>
		/// Reads the string from the passed in stream reader.
		/// </summary>
		/// <param name="sr">The stream reader.</param>
		/// <returns></returns>
		private string ReadStringFromStreamReader(StreamReader sr)
		{
			StringBuilder sb = new StringBuilder();
			char[] buffer = new char[80];
			while (!sr.EndOfStream)
			{
				int readLength = sr.Read(buffer, 0, buffer.Length);
				sb.Append(buffer, 0, readLength);
				//Debug.WriteLine("Reading...");
			}
			return sb.ToString();
		}

		/// <summary>
		/// Writes the string to stream writer.
		/// </summary>
		/// <param name="sw">The sw.</param>
		/// <param name="str">The STR.</param>
		/// <returns></returns>
		private int WriteDataToStreamWriter(StreamWriter sw, string str)
		{
			int writeSize = 127;
			for (int i = 0; i < str.Length; i += writeSize)
			{
				// select a substring of characters from the input string
				string substring = str.Substring(i, (i + writeSize < str.Length) ? writeSize : str.Length - i);
				sw.Write(substring.ToCharArray(), 0, substring.Length);
			}
			return str.Length;
		}

		#endregion

		#region Time Trial Testing

		/*
		/// <summary>
		/// Compare the length of time to use BridgeStream vs MemoryStream.
		/// </summary>
		[Test]
		public void TimeComparisonTest()
		{
			int iterations = 11024;
			int arraySize = 11024;
			byte[] bytes = new byte[arraySize];
			byte[] readBytes = new byte[arraySize];

			HiPerfTimer h = new HiPerfTimer();

			h.Start();
			BridgeStream bs = new BridgeStream();
			for (int i = 0; i < iterations; i++)
			{
				bs.Write(bytes, 0, bytes.Length);
			}
			for (int i = 0; i < iterations; i++)
			{
				bs.Read(readBytes, 0, bytes.Length);
			}
			h.Stop();
			Debug.WriteLine(h.Duration);

			h = new HiPerfTimer();
			h.Start();
			Stream s = new MemoryStream();
			for (int i = 0; i < iterations; i++)
			{
				s.Write(bytes, 0, bytes.Length);
			}
			for (int i = 0; i < iterations; i++)
			{
				s.Read(readBytes, 0, bytes.Length);
			}
			h.Stop();
			Debug.WriteLine(h.Duration);
		}
		 */
		#endregion
	}
}
