using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Kinect;
using Microsoft.Speech.AudioFormat;
using Microsoft.Speech.Recognition;

namespace GettingConnectedWithKinect
{
	/// <summary>
	/// Interaction logic for MainWindow.xaml
	/// </summary>
	public partial class MainWindow : Window
	{
		private KinectSensor _kinect;
		private WriteableBitmap _colorBitmap;
		private WriteableBitmap _depthBitmap;
		private SpeechRecognitionEngine sre;
		private bool lastFullDepth;

		public MainWindow()
		{
			InitializeComponent();
		}

		private void Start_Click(object sender, RoutedEventArgs e)
		{
			_kinect = (from sensor in KinectSensor.KinectSensors where sensor.Status == KinectStatus.Connected select sensor).FirstOrDefault();
			if(_kinect == null)
			{
				MessageBox.Show("No Kinects found in Connected state!");
				return;
			}

			_kinect.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
			_kinect.ColorFrameReady += _kinect_ColorFrameReady;

			_kinect.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30);
			_kinect.DepthFrameReady += _kinect_DepthFrameReady;

			_kinect.SkeletonStream.Enable();
			_kinect.SkeletonFrameReady += _kinect_SkeletonFrameReady;

//			_kinect.AllFramesReady += _kinect_AllFramesReady;

			_kinect.Start();

			_kinect.AudioSource.SoundSourceAngleChanged += AudioSource_SoundSourceAngleChanged;
//			_kinect.AudioSource.Start();

			SetupSpeech();
		}

		private void StartSpeech_Click(object sender, RoutedEventArgs e)
		{
			StartSpeech();
		}

		void AudioSource_SoundSourceAngleChanged(object sender, SoundSourceAngleChangedEventArgs e)
		{
			SoundAngle.Text = "Sound: " + e.Angle + ", " + e.ConfidenceLevel;
		}

		void _kinect_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
		{
			using(ColorImageFrame frame = e.OpenColorImageFrame())
				ProcessColorFrame(frame);
		}

		void _kinect_DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)
		{
			using(DepthImageFrame frame = e.OpenDepthImageFrame())
				ProcessDepthFrame(frame);
		}

		void _kinect_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
		{
			using(SkeletonFrame frame = e.OpenSkeletonFrame())
				ProcessSkeletonFrame(frame);
		}

		void _kinect_AllFramesReady(object sender, AllFramesReadyEventArgs e)
		{
			using(ColorImageFrame colorFrame = e.OpenColorImageFrame())
				ProcessColorFrame(colorFrame);

			using(DepthImageFrame depthFrame = e.OpenDepthImageFrame())
				ProcessDepthFrame(depthFrame);

			using(SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
				ProcessSkeletonFrame(skeletonFrame);
		}

		private void ProcessColorFrame(ColorImageFrame frame)
		{
			if(frame != null)
			{
				byte[] data = new byte[frame.PixelDataLength];
				frame.CopyPixelDataTo(data);

				if(_colorBitmap == null)
				{
					_colorBitmap = new WriteableBitmap(frame.Width, frame.Height, 96, 96, PixelFormats.Bgr32, null);
					ColorImage.Source = _colorBitmap;
				}

				_colorBitmap.WritePixels(new Int32Rect(0, 0, frame.Width, frame.Height), data, frame.Width*frame.BytesPerPixel, 0);
			}
		}

		private void ProcessDepthFrame(DepthImageFrame frame)
		{
			if(frame != null)
			{
				short[] data = new short[frame.PixelDataLength];
				byte[] dataColor = new byte[frame.Width * frame.Height * 4];

				frame.CopyPixelDataTo(data);

				if(_depthBitmap == null || CheckFullDepth.IsChecked.Value != lastFullDepth)
				{
					if(CheckFullDepth.IsChecked.Value)
						_depthBitmap = new WriteableBitmap(frame.Width, frame.Height, 96, 96, PixelFormats.Gray16, null);
					else
						_depthBitmap = new WriteableBitmap(frame.Width, frame.Height, 96, 96, PixelFormats.Bgr32, null);

					DepthImage.Source = _depthBitmap;
				}

				if(CheckFullDepth.IsChecked.Value)
					_depthBitmap.WritePixels(new Int32Rect(0, 0, frame.Width, frame.Height), data, frame.Width*frame.BytesPerPixel, 0);
				else
				{
					for(int i = 0; i < data.Length; i++)
					{
						if((data[i] & DepthImageFrame.PlayerIndexBitmask) > 0)
						{
							dataColor[(i*4) + 0] = 0xff;
							dataColor[(i*4) + 1] = 0x00;
							dataColor[(i*4) + 2] = 0x00;
							dataColor[(i*4) + 3] = 0xff;
						}
					}

					_depthBitmap.WritePixels(new Int32Rect(0, 0, frame.Width, frame.Height), dataColor, frame.Width * 4, 0);				
				}
			}

			lastFullDepth = CheckFullDepth.IsChecked.Value;
		}

		private void ProcessSkeletonFrame(SkeletonFrame frame)
		{
			if(frame != null)
			{
				Skeleton[] skeletons = new Skeleton[frame.SkeletonArrayLength];
				frame.CopySkeletonDataTo(skeletons);

				Skeleton skeleton = (from s in skeletons
									 where s.TrackingState == SkeletonTrackingState.Tracked
									 select s).FirstOrDefault();

				if(skeleton == null)
					return;

				//_kinect.SkeletonStream.AppChoosesSkeletons = true;
				//_kinect.SkeletonStream.ChooseSkeletons(skeleton.TrackingId);

				KinectHelpers.SetEllipsePosition(EllipseHead, skeleton.Joints[JointType.Head], SkeletonCanvas.Width, SkeletonCanvas.Height);
				KinectHelpers.SetEllipsePosition(EllipseLeft, skeleton.Joints[JointType.HandLeft], SkeletonCanvas.Width, SkeletonCanvas.Height);
				KinectHelpers.SetEllipsePosition(EllipseRight, skeleton.Joints[JointType.HandRight], SkeletonCanvas.Width, SkeletonCanvas.Height);
			}
		}

		private void SetupSpeech()
		{
			_kinect.AudioSource.EchoCancellationMode = EchoCancellationMode.None;
			_kinect.AudioSource.AutomaticGainControlEnabled = false;
			_kinect.AudioSource.BeamAngleMode = BeamAngleMode.Adaptive;

			Func<RecognizerInfo, bool> matchingFunc = r =>
			{
				string value;
				r.AdditionalInfo.TryGetValue("Kinect", out value);
				return "True".Equals(value, StringComparison.InvariantCultureIgnoreCase) && "en-US".Equals(r.Culture.Name, StringComparison.InvariantCultureIgnoreCase);
			};
			RecognizerInfo ri = SpeechRecognitionEngine.InstalledRecognizers().Where(matchingFunc).FirstOrDefault();

			if(ri == null)
			{
				MessageBox.Show("Could not find Kinect speech recognizer");
				return;
			}

			sre = new SpeechRecognitionEngine(ri.Id);

			Choices choices = new Choices();
			choices.Add("red");
			choices.Add("green");
			choices.Add("blue");

			GrammarBuilder gb = new GrammarBuilder();
			gb.Culture = ri.Culture;
			gb.Append(choices);

			Grammar g = new Grammar(gb);

			sre.LoadGrammar(g);
			sre.SpeechRecognized += sre_SpeechRecognized;
			sre.SpeechHypothesized += sre_SpeechHypothesized;
			sre.SpeechRecognitionRejected += sre_SpeechRecognitionRejected;
		}

		public void StartSpeech()
		{
			Stream s = _kinect.AudioSource.Start();
			sre.SetInputToAudioStream(s, new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));
			sre.RecognizeAsync(RecognizeMode.Multiple);
		}

		void sre_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
		{
			Speech.Text = e.Result.Text + ", " + e.Result.Confidence;
			Speech.Background = new SolidColorBrush(Colors.Green);
		}

		void sre_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
		{
			Speech.Text = e.Result.Text + ", " + e.Result.Confidence;
			Speech.Background = new SolidColorBrush(Colors.Yellow);
		}

		void sre_SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
		{
			Speech.Text = e.Result.Text + ", " + e.Result.Confidence;
			Speech.Background = new SolidColorBrush(Colors.Red);
		}
	}
}
