﻿using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System;
using EncodeToJPEG;
using System.IO;

public class ImageFlow: MonoBehaviour {
	/// <summary>
	/// The gameobject in the Uniquitous scene.
	/// </summary>
	GameObject ObjectAcceptInputs;

	/// <summary>
	/// record the time before the next screen capture.
	/// </summary>
	float timeToScreenCapture;

	/// <summary>
	/// The value used to prevent the screen capture and frame sending in OnPostRender() from executing too fast
	/// </summary>
	float targetFramerate = 15.0f;

	/// <summary>
	/// Game image transmission status
	/// </summary>
	bool isSending = false;
	bool isSendingGUI = false;

	/// <summary>
	/// used to store the captured screen
	/// </summary>
	Texture2D screenTexture;

	/// <summary>
	/// used to store the pixels data of the captured screen
	/// </summary>
	byte[] pixels;

	/// <summary>
	/// The image data sent.
	/// </summary>
	byte[] dataSent;

	/// <summary>
	/// used to store the frame number
	/// </summary>
	byte[] intBytes;
	byte[] result;

	int screenWidth;
	int screenHeight;
	int frameNum;

	/// <summary>
	/// JPEG encoding quality factor
	/// </summary>
	private int Q;
	
	// Use this for initialization
	void Start () {
		result = new byte[4];

		if(GameObject.Find("GameObject").ToString() != null){
			ObjectAcceptInputs = GameObject.Find("GameObject");
			Q = ObjectAcceptInputs.GetComponent<InputFlow>().qualityFactor; // reference the quality factor value set by the user from the InputFlow script

		}
				
	}

	
	public bool IsSending()
	{
		return isSending;	
	}
	
	public void StartSending(bool sendGUI)
	{
		if(!isSending)
		{
			isSendingGUI = sendGUI;
			isSending = true;
			
			screenWidth = Screen.width;
			screenHeight = Screen.height;
			
			if (screenWidth % 2 != 0)
				screenWidth -= 1;
			
			if (screenHeight % 2 != 0)
				screenHeight -= 1;
			
			CleanUp();

			/*screen capture*/
			screenTexture = new Texture2D(screenWidth, screenHeight, TextureFormat.RGB24, false);
			pixels = new byte[screenWidth * screenHeight * sizeof(Int32)];
			
			/*send game image after Unity GUI rendered*/
			if (isSendingGUI)
			{
				StartCoroutine(SendVideoWithGUI());	
			}
			
			
		}
	}

	/// <summary>
	/// Stop game image transmission
	/// </summary>
	public void StopSending()
	{
		if(isSending)
		{  
			isSending = false;		
			
			CleanUp();
			
		}
	}
	
	
	void CleanUp()
	{		
		timeToScreenCapture = 0.0f;
	}

	/// <summary>
	/// Coroutine used to compress and send game image after the Unity GUI rendered.
	/// </summary>
	IEnumerator SendVideoWithGUI()
	{
		while (isSending && isSendingGUI)
		{

			yield return new WaitForEndOfFrame();
			screenTexture.ReadPixels(new Rect(0, 0, screenWidth, screenHeight), 0, 0, false);
			screenTexture.Apply(false,false);

			JPGEncoder NewEncoder = new JPGEncoder(screenTexture,Q); // compress the captured screen with the JPEG encoder

			/*if JPEG encoding for a frame is not done, the coroutine pauses and returns the control to the Unity main thread*/
			while(!NewEncoder.isDone){
				yield return null;
			}

			/*executes after the JPEG encoding for a frame is done*/
			pixels = NewEncoder.GetBytes();

			sendFrame(); // send compressed image data with the RPC
		
		}
	}

	/// <summary>
	/// Coroutine used to compress and send the game image before the GUI rendered
	/// </summary>
	IEnumerator OnPostRender(){
		if(isSending && !isSendingGUI)
		{

			timeToScreenCapture += Time.deltaTime;
			int numScreens = 0;
			while(timeToScreenCapture > (1.0f / (float)targetFramerate) )
			{
				timeToScreenCapture -= (1.0f / (float)targetFramerate);
				numScreens++;
			}
			
			Debug.Log("number screens" + numScreens);
			if(numScreens > 0)
			{  
				Debug.Log("sent a frame" + Time.realtimeSinceStartup);
				screenTexture.ReadPixels(new Rect(0, 0, screenWidth, screenHeight), 0, 0, false);
				screenTexture.Apply(false,false);

				JPGEncoder NewEncoder = new JPGEncoder(screenTexture,Q);// compress the captured screen with the JPEG encoder
				
				/*if JPEG encoding for a frame is not done, the coroutine pauses and returns the control to the Unity main thread*/		
				while(!NewEncoder.isDone){
					yield return null;
				}

				/*executes after the JPEG encoding for a frame is done*/
				pixels = NewEncoder.GetBytes();

					
				for(int i=0; i<numScreens; ++i){
					sendFrame(); // send compressed image data with the RPC
				}
				
					
				
			}	
		}
		
	}

	/// <summary>
	/// Used to sent out the game image data from the Uniquitous server
	/// </summary>
	void sendFrame(){
		if (pixels.Length> 0){
			frameNum++;
			//Debug.Log("Frame "+frameNum+" sent.");
			
			/*concatenate the frame number and the compressed game image data*/
			intBytes = BitConverter.GetBytes(frameNum);
			if (BitConverter.IsLittleEndian)
				Array.Reverse(intBytes);
			result = intBytes;
			dataSent = new byte[pixels.Length + 4];
			pixels.CopyTo(dataSent, 0);
			result.CopyTo(dataSent, pixels.Length);
			
			uLink.NetworkView.Get(ObjectAcceptInputs).UnreliableRPC("ReceiveImages", uLink.RPCMode.Others, dataSent);
			
			
		}
		else{
			Debug.LogError("Bad length of bytes to send.");
		}

	}
	
	
	
}
