﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System;

using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;

public class StreamAudio : MonoBehaviour {
		
	bool isCapturingAudio = false;
	bool isDebugging = false;

	TcpListener audioServer;
	TcpClient audioSocket;
	NetworkStream audioStream;
	Process audioProcess;
	
	private Byte[] audioByteBuffer;	
	private GCHandle audioHandle;
	private IntPtr audioPointer = IntPtr.Zero;
	
	string ffmpeg() 
	{
	#if UNITY_EDITOR
		return (Application.platform == RuntimePlatform.WindowsEditor) ? Application.streamingAssetsPath + "/ffmpeg/windows/ffmpeg.exe" : Application.streamingAssetsPath + "/ffmpeg/macosx/ffmpeg.command";
	#elif UNITY_STANDALONE_WIN
		return Application.streamingAssetsPath + "/ffmpeg/windows/ffmpeg.exe";
	#elif UNITY_STANDALONE_OSX
		return Application.streamingAssetsPath + "/ffmpeg/macosx/ffmpeg.command";
	#endif
	
	}

	/// <summary>
	/// Implemented to capture a chunk of game audio data every 20 ms
	/// </summary>
	void OnAudioFilterRead(float[] data, int channels)
    {
    	if (isCapturingAudio)
		{
			Marshal.Copy(data, 0, audioPointer, 2048);	
			audioStream.Write(audioByteBuffer, 0, audioByteBuffer.Length); //write the audio date from the buffer into the network stream
        }
    }

	
	public void StartAudio(bool debug = false)
	{
		
			isDebugging = debug;
			
			if(GetComponent(typeof(AudioListener)))
			{
			    /*set up the local host IP*/
				audioServer = new System.Net.Sockets.TcpListener(IPAddress.Parse("127.0.0.1"), 1235); 
				audioServer.Start();

			/*start the FFMPEG process to get audio stream from local host and sent out the audio data to the client (130.215.29.90:5006)*/
				/*audioProcess = StartProcess(ffmpeg(), "-f f32le -ar " + AudioSettings.outputSampleRate + 
					" -ac 2 -i tcp://127.0.0.1:1235 -vn -acodec libmp3lame -f mulaw udp://"+uLink.Network.player.ipAddress+":5008");*/
			    audioProcess = StartProcess(ffmpeg(), "-f f32le -ar " + AudioSettings.outputSampleRate + 
					" -ac 2 -i tcp://127.0.0.1:1235 -vn -probesize 100 -tune zerolatency -acodec libmp3lame -f mulaw udp://130.215.29.90:5006");
				
				audioProcess.EnableRaisingEvents = true;
				audioProcess.Exited += ForcedExit;
				
				audioByteBuffer = new Byte[8192];
				audioHandle = GCHandle.Alloc(audioByteBuffer, GCHandleType.Pinned);
				audioPointer = audioHandle.AddrOfPinnedObject();
				
			   /*local host gets the audio data from the network stream*/
				audioSocket = SetupSocket(audioServer);
				audioStream = audioSocket.GetStream();
				
				isCapturingAudio = true;
			}
			else
			{
				UnityEngine.Debug.LogError("AudioListeners is missing, no audio will be captured");	
			}
			
			
	}

	/*set up TCP socket connection*/
	TcpClient SetupSocket(TcpListener tcpServer)
	{
		TcpClient socket = tcpServer.AcceptTcpClient();
		
		int maxBufferSize = Int32.MaxValue;
		while(true)
		{
		 	try
			{ 
				socket.Client.SendBufferSize = maxBufferSize;
				break;
		    }
		    catch
			{
				maxBufferSize = maxBufferSize / 10;
		    }
		}
		return socket;
	}

	/// <summary>
	/// Raises the GU event.
	/// </summary>
	void OnGUI()
	{
	 if(uLink.Network.isServer){
        if(isCapturingAudio)
		{
			if(GUI.Button(new Rect(Screen.width * 0.5f - 100.0f, 1.0f, 80.0f, 30.0f), "StopAudio"))  //stop the audio streaming
			StopAudio();
		}
		else{
		    if(GUI.Button(new Rect(Screen.width * 0.5f - 100.0f, 1.0f, 80.0f, 30.0f), "StartAudio")) //start the audio streaming
		    StartAudio(true);	    
		}
		
	 }
	}


	/// <summary>
	/// Function used to stop audio data stream
	/// </summary>
	public void StopAudio()
	{
		
			isCapturingAudio = false;		
			if(audioSocket != null && audioSocket.Connected)
			{
				audioSocket.Client.Disconnect(false);
			
				audioSocket.Close();
				audioServer.Stop();
				audioProcess.Close();
				
				
			}
			
	}
	
	private void ForcedExit(object sender, System.EventArgs e)
	{
		if (isCapturingAudio)
		{
			// For some unknown reason the process exits when the debugger is attached..
			UnityEngine.Debug.LogError("Exporting exited by force, detach bugger if attached.");
			
			StopAudio();
		}
	}	
	
	
	public void DataReceived(object e, DataReceivedEventArgs outLine)
	{
	   print(DateTime.Now + " - " + outLine.Data);
	}


	/// <summary>
	/// Start a process.
	/// </summary>
	Process StartProcess(string processName, string arguments)
	{
		if (!File.Exists(processName))
		{
			UnityEngine.Debug.LogError("ffmpeg is located in wrong location, please make sure that the following path is correct: " + processName);
			return null;
		}
		else
		{
			Process process = new Process();
			process.StartInfo.FileName = processName;
			process.StartInfo.Arguments = arguments;
			
			if (isDebugging)
			{
			 	process.StartInfo.RedirectStandardOutput = true;
				process.StartInfo.RedirectStandardError = true;
				process.OutputDataReceived += new DataReceivedEventHandler(DataReceived);
				process.ErrorDataReceived += new DataReceivedEventHandler(DataReceived);
			}
	
			process.StartInfo.CreateNoWindow = true;		
			process.StartInfo.UseShellExecute = false;
			process.Start();
			
			if (isDebugging)
			{
				process.BeginErrorReadLine();
				process.BeginOutputReadLine();
			}
			
			return process;
		}
	}
}
