using System;
using System.Collections.Generic;
using System.Text;

using System.Diagnostics;

using EA.FileTalkMessages;

namespace EA.FileTalk
{
	/// <summary>
	/// Used to download a file from the FileTalkServer
	/// </summary>
	public class GetFileTransaction : Transaction
	{
		string mFilePath;
		byte[] mByteArray;
		long mByteArrayIndex;

		/// <summary>
		/// Returns the bytes that make up a file (assuming the file is downloaded).
		/// </summary>
		public byte[] ByteArray
		{
			get { return mByteArray;}
		}

		/// <summary>
		/// Internal because only the FileTalkManager should be instantiating these
		/// </summary>
		/// <param name="fileTalkCallback"></param>
		/// <param name="transactionUID"></param>
		/// <param name="gameTalkInstance"></param>
		/// <param name="filePath"></param>
		/// <param name="fragmentSize"></param>
		/// <param name="fileIndex"></param>
		/// <param name="numBytes"></param>
		internal GetFileTransaction(FileTalkManager.MessageReceivedDelegate fileTalkCallback, 
			uint transactionUID, EA.GameTalk.GameTalkInstance gameTalkInstance,
			string filePath, uint fragmentSize, uint fileIndex, uint numBytes)
			: base(fileTalkCallback, gameTalkInstance, transactionUID)
		{
			mFilePath = filePath;
			mByteArrayIndex = 0;
			//uint fragmentSize = FileTalkManager.Instance.mFragmentSize;
			// The base Transaction MUST register the user's callback before we request
			// the file from FileTalkServer
			GetFileRequest(filePath, gameTalkInstance, fragmentSize, fileIndex, numBytes);
		}

		/// <summary>
		/// We need a way to tell transactions to release resources - somewhat like a delete in C++
		/// However, we can't rely on using a destructor - since we need to be able to control
		/// when this release/freeing occurs.  Currently the only benefit to this is to get
		/// FileNotificationTransaction to tell the FileTalkServer to release its listener(s).
		/// In actual fact - the FileTalkServer will receive a message when this client
		/// disconnects - at which point it will clear all listeners for this client anyway.  This
		/// just gives us more granular control.
		/// </summary>
		public override void Dispose() { }

		/// <summary>
		/// Send file request to FileTalkServer using GameTalkMessages through GameTalkInstance
		/// </summary>
		/// <param name="filePath"></param>
		/// <param name="gameTalkInstance"></param>
		/// <param name="fragmentSize"></param>
		/// <param name="fileIndex"></param>
		/// <param name="numBytes"></param>
		private void GetFileRequest(string filePath,EA.GameTalk.GameTalkInstance gameTalkInstance,
			uint fragmentSize, uint fileIndex, uint numBytes)
		{
			EA.GameTalkMessages.GameTalkMessage gameTalkMessage =
				new EA.GameTalkMessages.GameTalkMessage(mChannelId);
#if(DEBUG)
			// The following sends a string of the specific transaction request being made - shows up in GameTalkMonitor
			gameTalkMessage.AddKeyContentAsBinary(FileTalkMessage.MessageID.GetFileRequest.ToString(),
				BitConverter.GetBytes((int)FileTalkMessage.MessageID.GetFileRequest));
#else
			gameTalkMessage.AddKeyContentAsBinary("",
				BitConverter.GetBytes((int)FileTalkMessage.MessageID.GetFileRequest));
#endif
			gameTalkMessage.AddKeyContent(gameTalkInstance.TargetName, filePath);
			gameTalkMessage.AddKeyContentAsBinary(fragmentSize.ToString(), 
				BitConverter.GetBytes(fragmentSize));

			if (numBytes > 0)
			{
				gameTalkMessage.AddKeyContentAsBinary(fileIndex.ToString(),
					BitConverter.GetBytes(fileIndex));
				gameTalkMessage.AddKeyContentAsBinary(numBytes.ToString(),
					BitConverter.GetBytes(numBytes));
			}
			gameTalkInstance.Send(FileTalkMessage.sFileTalkServerID, gameTalkMessage);
		}

		/// <summary>
		/// GameTalk callback registered with this particular transaction's unique 
		/// channel ID (at least unique to this target) - i.e. other targets could
		/// use a transaction registered with the same channel ID
		/// </summary>
		/// <param name="message"></param>
		public override void MessageReceived(EA.GameTalkMessages.GameTalkMessage message)
		{
			// All GetFileTransaction responses from FileTalkServer must have three parts
			Debug.Assert(message.NumberOfKeys == 3, "GetFileTransaction received message with invalid content");
			// If I get anything other than a GetFileResponse - then I don't understand it
			//Debug.Assert(message.GetKey(0) == FileTalkMessage.MessageID.GetFileResponse.ToString(), "GetFileTransaction has received a non-GetFile GameTalkMessage");
			Debug.Assert(BitConverter.ToInt32(message.GetContentAsBinary(0), 0) == (int)FileTalkMessage.MessageID.GetFileResponse, "GetFileTransaction has received a non-GetFile GameTalkMessage");

			ProcessErrorCode(message.GetContentAsBinary(1));
			if (mErrorOccurred)
			{
				TransactionIsComplete();
				return;
			}

			string currentMessage = message.GetKey(2);
			if (currentMessage == "FileSize")
			{
				mByteArray = new byte[Convert.ToInt32(message.GetContentAsString(2))];
			}
			else if (currentMessage == "File")
			{
				byte [] fileFragment = message.GetContentAsBinary(2);
				Array.Copy(fileFragment, 0, mByteArray, mByteArrayIndex, fileFragment.Length);
				mByteArrayIndex += fileFragment.Length;
			}
			else if (currentMessage == "Done")
			{
				TransactionIsComplete();
			}
		}
	}
}
