using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using Crestron.SimplSharpPro.EthernetCommunication;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using PepperDash.Core;
using PepperDash.Essentials.AppServer.Messengers;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Room.Config;


namespace PepperDash.Essentials.Room.MobileControl
{
	public class MobileControlDdvc01RoomBridge : MobileControlBridgeBase, IDelayedConfiguration
	{
		public class BoolJoin
		{
			/// <summary>
			/// 301
			/// </summary>
			public const uint RoomIsOn = 301;

			/// <summary>
			/// 12
			/// </summary>
			public const uint PrivacyMute = 12;

			/// <summary>
			/// 41
			/// </summary>
			public const uint PromptForCode = 41;
			/// <summary>
			/// 42
			/// </summary>
			public const uint ClientJoined = 42;
			/// <summary>
			/// 51
			/// </summary>
			public const uint ActivityShare = 51;
			/// <summary>
			/// 52
			/// </summary>
			public const uint ActivityPhoneCall = 52;
			/// <summary>
			/// 53
			/// </summary>
			public const uint ActivityVideoCall = 53;

			/// <summary>
			/// 1
			/// </summary>
			public const uint MasterVolumeIsMuted = 1;
			/// <summary>
			/// 1
			/// </summary>
			public const uint MasterVolumeMuteToggle = 1;
			/// <summary>
			/// 1
			/// </summary>
			public const uint VolumeMutesJoinStart = 1;
			/// <summary>
			/// 61
			/// </summary>
			public const uint ShutdownCancel = 61;
			/// <summary>
			/// 62
			/// </summary>
			public const uint ShutdownEnd = 62;			
			/// <summary>
			/// 63
			/// </summary>
			public const uint ShutdownStart = 63;
			/// <summary>
			/// 72
			/// </summary>
			public const uint SourceHasChanged = 71;

			/// <summary>
			/// 261 - The start of the range of speed dial visibles
			/// </summary>
			public const uint SpeedDialVisibleStartJoin = 261;
			/// <summary>
			/// 501
			/// </summary>
			public const uint ConfigIsReady = 501;
			/// <summary>
			/// 502
			/// </summary>
			public const uint HideVideoConfRecents = 502;
			/// <summary>
			/// 503
			/// </summary>
			public const uint ShowCameraWhenNotInCall = 503;
			/// <summary>
			/// 504
			/// </summary>
			public const uint UseSourceEnabled = 504;
			/// <summary>
			/// 601
			/// </summary>
			public const uint SourceShareDisableJoinStart = 601;
			/// <summary>
			/// 621
			/// </summary>
			public const uint SourceIsEnabledJoinStart = 621;

		}

		public class UshortJoin
		{
			/// <summary>
			/// 1
			/// </summary>
			public const uint MasterVolumeLevel = 1;
			/// <summary>
			/// 1
			/// </summary>
			public const uint VolumeSlidersJoinStart = 1;
			/// <summary>
			/// 61
			/// </summary>
			public const uint ShutdownPromptDuration = 61;
			/// <summary>
			/// 101
			/// </summary>
			public const uint NumberOfAuxFaders = 101;
		}

		public class StringJoin
		{
			/// <summary>
			/// 1
			/// </summary>
			public const uint VolumeSliderNamesJoinStart = 1;
			/// <summary>
			/// 71
			/// </summary>
			public const uint SelectedSourceKey = 71;

			/// <summary>
			/// 241
			/// </summary>
			public const uint SpeedDialNameStartJoin = 241;

			/// <summary>
			/// 251
			/// </summary>
			public const uint SpeedDialNumberStartJoin = 251;
			
			/// <summary>
			/// 501
			/// </summary>
			public const uint ConfigRoomName = 501;
			/// <summary>
			/// 502
			/// </summary>
			public const uint ConfigHelpMessage = 502;
			/// <summary>
			/// 503
			/// </summary>
			public const uint ConfigHelpNumber = 503;
			/// <summary>
			/// 504
			/// </summary>
			public const uint ConfigRoomPhoneNumber = 504;
			/// <summary>
			/// 505
			/// </summary>
			public const uint ConfigRoomURI = 505;
			/// <summary>
			/// 401
			/// </summary>
			public const uint UserCodeToSystem = 401;
			/// <summary>
			/// 402
			/// </summary>
			public const uint ServerUrl = 402;
			/// <summary>
			/// 512
			/// </summary>
			public const uint RoomSpeedDialNamesJoinStart = 512;
			/// <summary>
			/// 516
			/// </summary>
			public const uint RoomSpeedDialNumberssJoinStart = 516;
			/// <summary>
			/// 601
			/// </summary>
			public const uint SourceNameJoinStart = 601;
			/// <summary>
			/// 621
			/// </summary>
			public const uint SourceIconJoinStart = 621;
			/// <summary>
			/// 641
			/// </summary>
			public const uint SourceKeyJoinStart = 641;
			/// <summary>
			/// 661
			/// </summary>
			public const uint SourceTypeJoinStart = 661;
			/// <summary>
			/// 761
			/// </summary>
			public const uint CameraNearNameStart = 761;
			/// <summary>
			/// 770 - presence of this name on the input will cause the camera to be added
			/// </summary>
			public const uint CameraFarName = 770;
		}

		/// <summary>
		/// Fires when config is ready to go
		/// </summary>
		public event EventHandler<EventArgs> ConfigurationIsReady;

		public ThreeSeriesTcpIpEthernetIntersystemCommunications EISC { get; private set; }

		/// <summary>
		/// 
		/// </summary>
		public bool ConfigIsLoaded { get; private set; }

		public override string RoomName
		{
			get { 
				var name = EISC.StringOutput[StringJoin.ConfigRoomName].StringValue;
				return string.IsNullOrEmpty(name) ? "Not Loaded" : name;
			}
		}

		MobileControlDdvc01DeviceBridge SourceBridge;

		Ddvc01AtcMessenger AtcMessenger;
		Ddvc01VtcMessenger VtcMessenger;


		/// <summary>
		/// 
		/// </summary>
		/// <param name="key"></param>
		/// <param name="name"></param>
		/// <param name="ipId"></param>
		public MobileControlDdvc01RoomBridge(string key, string name, uint ipId)
			: base(key, name)
		{
			try
			{
				EISC = new ThreeSeriesTcpIpEthernetIntersystemCommunications(ipId, "127.0.0.2", Global.ControlSystem);
				var reg = EISC.Register();
				if (reg != Crestron.SimplSharpPro.eDeviceRegistrationUnRegistrationResponse.Success)
					Debug.Console(0, this, "Cannot connect EISC at IPID {0}: \r{1}", ipId, reg);

				SourceBridge = new MobileControlDdvc01DeviceBridge(key + "-sourceBridge", "DDVC01 source bridge", EISC);
				DeviceManager.AddDevice(SourceBridge);
			}
			catch (Exception)
			{
				throw;
			}
		}

		/// <summary>
		/// Finish wiring up everything after all devices are created. The base class will hunt down the related
		/// parent controller and link them up.
		/// </summary>
		/// <returns></returns>
		public override bool CustomActivate()
		{
			Debug.Console(0, this, "Final activation. Setting up actions and feedbacks");
			SetupFunctions();
			SetupFeedbacks();

            var atcKey = string.Format("atc-{0}-{1}", this.Key, Parent.Key);
			AtcMessenger = new Ddvc01AtcMessenger(atcKey, EISC, "/device/audioCodec");
			AtcMessenger.RegisterWithAppServer(Parent);

            var vtcKey = string.Format("atc-{0}-{1}", this.Key, Parent.Key);
			VtcMessenger = new Ddvc01VtcMessenger(vtcKey, EISC, "/device/videoCodec");
			VtcMessenger.RegisterWithAppServer(Parent);

			EISC.SigChange += EISC_SigChange;
			EISC.OnlineStatusChange += (o, a) =>
			{
				Debug.Console(1, this, "DDVC EISC online={0}. Config is ready={1}", a.DeviceOnLine, EISC.BooleanOutput[BoolJoin.ConfigIsReady].BoolValue);
				if (a.DeviceOnLine && EISC.BooleanOutput[BoolJoin.ConfigIsReady].BoolValue)
					LoadConfigValues();
			};
			// load config if it's already there
			if (EISC.IsOnline && EISC.BooleanOutput[BoolJoin.ConfigIsReady].BoolValue) // || EISC.BooleanInput[BoolJoin.ConfigIsReady].BoolValue)
				LoadConfigValues();


			CrestronConsole.AddNewConsoleCommand(s =>
			{
				for (uint i = 1; i < 1000; i++)
				{
					if (s.ToLower().Equals("b"))
					{
						CrestronConsole.ConsoleCommandResponse("D{0,6} {1} - ", i, EISC.BooleanOutput[i].BoolValue);
					}
					else if (s.ToLower().Equals("u"))
					{
						CrestronConsole.ConsoleCommandResponse("U{0,6} {1,8} - ", i, EISC.UShortOutput[i].UShortValue);
					}
					else if (s.ToLower().Equals("s"))
					{
						var val = EISC.StringOutput[i].StringValue;
						if(!string.IsNullOrEmpty(val))
							CrestronConsole.ConsoleCommandResponse("S{0,6} {1}\r", i, EISC.StringOutput[i].StringValue);
					}

				}
			}, "mobilebridgedump", "Dumps DDVC01 bridge EISC data b,u,s", ConsoleAccessLevelEnum.AccessOperator);

			return base.CustomActivate();
		}


		/// <summary>
		/// Setup the actions to take place on various incoming API calls
		/// </summary>
		void SetupFunctions()
		{
			Parent.AddAction(@"/room/room1/promptForCode", new Action(() => EISC.PulseBool(BoolJoin.PromptForCode)));
			Parent.AddAction(@"/room/room1/clientJoined", new Action(() => EISC.PulseBool(BoolJoin.ClientJoined)));

			Parent.AddAction(@"/room/room1/status", new Action(SendFullStatus));

			Parent.AddAction(@"/room/room1/source", new Action<SourceSelectMessageContent>(c =>
			{
				EISC.SetString(StringJoin.SelectedSourceKey, c.SourceListItem);
				EISC.PulseBool(BoolJoin.SourceHasChanged);
			}));

			Parent.AddAction(@"/room/room1/defaultsource", new Action(() => 
				EISC.PulseBool(BoolJoin.ActivityShare)));
			Parent.AddAction(@"/room/room1/activityPhone", new Action(() =>
				EISC.PulseBool(BoolJoin.ActivityPhoneCall)));
			Parent.AddAction(@"/room/room1/activityVideo", new Action(() =>
				EISC.PulseBool(BoolJoin.ActivityVideoCall)));

			Parent.AddAction(@"/room/room1/volumes/master/level", new Action<ushort>(u => 
				EISC.SetUshort(UshortJoin.MasterVolumeLevel, u)));
			Parent.AddAction(@"/room/room1/volumes/master/muteToggle", new Action(() => 
				EISC.PulseBool(BoolJoin.MasterVolumeIsMuted)));
			Parent.AddAction(@"/room/room1/volumes/master/privacyMuteToggle", new Action(() =>
				EISC.PulseBool(BoolJoin.PrivacyMute)));


			// /xyzxyz/volumes/master/muteToggle ---> BoolInput[1]

			for (uint i = 2; i <= 7; i++)
			{
				var index = i;
				Parent.AddAction(string.Format(@"/room/room1/volumes/level-{0}/level", index), new Action<ushort>(u =>
					EISC.SetUshort(index, u)));
				Parent.AddAction(string.Format(@"/room/room1/volumes/level-{0}/muteToggle", index), new Action(() =>
					EISC.PulseBool(index)));
			}

			Parent.AddAction(@"/room/room1/shutdownStart", new Action(() =>
				EISC.PulseBool(BoolJoin.ShutdownStart)));
			Parent.AddAction(@"/room/room1/shutdownEnd", new Action(() =>
				EISC.PulseBool(BoolJoin.ShutdownEnd)));
			Parent.AddAction(@"/room/room1/shutdownCancel", new Action(() =>
				EISC.PulseBool(BoolJoin.ShutdownCancel)));
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="devKey"></param>
		void SetupSourceFunctions(string devKey)
		{
			SourceDeviceMapDictionary sourceJoinMap = new SourceDeviceMapDictionary();

			var prefix = string.Format("/device/{0}/", devKey);

			foreach (var item in sourceJoinMap)
			{
				var join = item.Value;
				Parent.AddAction(string.Format("{0}{1}", prefix, item.Key), new PressAndHoldAction(b => EISC.SetBool(join, b)));
			}
		}


		/// <summary>
		/// Links feedbacks to whatever is gonna happen!
		/// </summary>
		void SetupFeedbacks()
		{
			// Power 
			EISC.SetBoolSigAction(BoolJoin.RoomIsOn, b =>
				PostStatusMessage(new
					{
						isOn = b
					}));

			// Source change things
			EISC.SetSigTrueAction(BoolJoin.SourceHasChanged, () =>
				PostStatusMessage(new
					{
						selectedSourceKey = EISC.StringOutput[StringJoin.SelectedSourceKey].StringValue
					}));

			// Volume things
			EISC.SetUShortSigAction(UshortJoin.MasterVolumeLevel, u =>
                PostStatusMessage(new
                {
                    volumes = new
                    {
                        master = new
                        {
                            level = u
                        }
                    }
                }));

			// map MasterVolumeIsMuted join -> status/volumes/master/muted
			// 

			EISC.SetBoolSigAction(BoolJoin.MasterVolumeIsMuted, b =>
                PostStatusMessage(new
                {
                    volumes = new
                    {
                        master = new
                        {
                            muted = b
                        }
                    }
                }));
			EISC.SetBoolSigAction(BoolJoin.PrivacyMute, b => 
				PostStatusMessage(new
				{
					volumes = new 
					{
						master = new 
						{
							privacyMuted = b
						}
					}
				}));

			for (uint i = 2; i <= 7; i++)
			{
				var index = i; // local scope for lambdas
				EISC.SetUShortSigAction(index, u => // start at join 2
				{
					// need a dict in order to create the level-n property on auxFaders
					var dict = new Dictionary<string, object>();
					dict.Add("level-" + index, new { level = u });
					PostStatusMessage(new
					{
						volumes = new
						{
							auxFaders = dict,
						}
					});
				});
				EISC.SetBoolSigAction(index, b =>
				{
					// need a dict in order to create the level-n property on auxFaders
					var dict = new Dictionary<string, object>();
					dict.Add("level-" + index, new { muted = b });
					PostStatusMessage(new
					{
						volumes = new
						{
							auxFaders = dict,
						}
					});
				});
			}

			EISC.SetUShortSigAction(UshortJoin.NumberOfAuxFaders, u => 
				PostStatusMessage(new {
					volumes = new {
						numberOfAuxFaders = u,
					}
				}));

			// shutdown things
			EISC.SetSigTrueAction(BoolJoin.ShutdownCancel, new Action(() =>
				PostMessage("/room/shutdown/", new
				{
					state = "wasCancelled"
				})));
			EISC.SetSigTrueAction(BoolJoin.ShutdownEnd, new Action(() =>
				PostMessage("/room/shutdown/", new
				{
					state = "hasFinished"
				})));
			EISC.SetSigTrueAction(BoolJoin.ShutdownStart, new Action(() =>
				PostMessage("/room/shutdown/", new
				{
					state = "hasStarted",
					duration = EISC.UShortOutput[UshortJoin.ShutdownPromptDuration].UShortValue
				})));

			// Config things
			EISC.SetSigTrueAction(BoolJoin.ConfigIsReady, LoadConfigValues);

			// Activity modes
			EISC.SetSigTrueAction(BoolJoin.ActivityShare, () => UpdateActivity(1));
			EISC.SetSigTrueAction(BoolJoin.ActivityPhoneCall, () => UpdateActivity(2));
			EISC.SetSigTrueAction(BoolJoin.ActivityVideoCall, () => UpdateActivity(3));
		}


		/// <summary>
		/// Updates activity states
		/// </summary>
		void UpdateActivity(int mode)
		{
			PostStatusMessage(new
			{
				activityMode = mode,
			});
		}

		/// <summary>
		/// Reads in config values when the Simpl program is ready
		/// </summary>
		void LoadConfigValues()
		{
			Debug.Console(1, this, "Loading configuration from DDVC01 EISC bridge");
			ConfigIsLoaded = false;

			var co = ConfigReader.ConfigObject;

            co.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name;
            var version = Assembly.GetExecutingAssembly().GetName().Version;
            co.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build);

			//Room
			//if (co.Rooms == null)
			// always start fresh in case simpl changed
			co.Rooms = new List<DeviceConfig>();
			var rm = new DeviceConfig();
            if (co.Rooms.Count == 0)
            {
                Debug.Console(0, this, "Adding room to config");
                co.Rooms.Add(rm);
            }
            else
            {
                Debug.Console(0, this, "Replacing Room[0] in config");
                co.Rooms[0] = rm;
            }
			rm.Name = EISC.StringOutput[StringJoin.ConfigRoomName].StringValue;
			rm.Key = "room1";
			rm.Type = "ddvc01";

			DDVC01RoomPropertiesConfig rmProps;
			if (rm.Properties == null)
				rmProps = new DDVC01RoomPropertiesConfig();
			else
				rmProps = JsonConvert.DeserializeObject<DDVC01RoomPropertiesConfig>(rm.Properties.ToString());

			rmProps.Help = new EssentialsHelpPropertiesConfig();
			rmProps.Help.CallButtonText = EISC.StringOutput[StringJoin.ConfigHelpNumber].StringValue;
			rmProps.Help.Message = EISC.StringOutput[StringJoin.ConfigHelpMessage].StringValue;

			rmProps.Environment = new EssentialsEnvironmentPropertiesConfig(); // enabled defaults to false

			rmProps.RoomPhoneNumber = EISC.StringOutput[StringJoin.ConfigRoomPhoneNumber].StringValue;
			rmProps.RoomURI = EISC.StringOutput[StringJoin.ConfigRoomURI].StringValue;
			rmProps.SpeedDials = new List<DDVC01SpeedDial>();

			// This MAY need a check 
			rmProps.AudioCodecKey = "audioCodec";
			rmProps.VideoCodecKey = "videoCodec";

			// volume control names
			var volCount = EISC.UShortOutput[UshortJoin.NumberOfAuxFaders].UShortValue;

            //// use Volumes object or?
            //rmProps.VolumeSliderNames = new List<string>();
            //for(uint i = 701; i <= 700 + volCount; i++)
            //{
            //    rmProps.VolumeSliderNames.Add(EISC.StringInput[i].StringValue);
            //}

			// There should be Mobile Control devices in here, I think...
			if(co.Devices == null)
				co.Devices = new List<DeviceConfig>();

			// clear out previous DDVC devices
			co.Devices.RemoveAll(d => 
				d.Key.StartsWith("source-", StringComparison.OrdinalIgnoreCase)
				|| d.Key.Equals("audioCodec", StringComparison.OrdinalIgnoreCase) 
				|| d.Key.Equals("videoCodec", StringComparison.OrdinalIgnoreCase));
			
			rmProps.SourceListKey = "default";
			rm.Properties = JToken.FromObject(rmProps);

			// Source list! This might be brutal!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

			var groupMap = GetSourceGroupDictionary();

			co.SourceLists = new Dictionary<string,Dictionary<string,SourceListItem>>();
			var newSl = new Dictionary<string, SourceListItem>();
			// add "none" source if VTC present

			if (!string.IsNullOrEmpty(rmProps.VideoCodecKey))
			{
				var codecOsd = new SourceListItem()
				{
					Name = "None",
					IncludeInSourceList = true,
					Order = 1,
					Type = eSourceListItemType.Route,
					SourceKey = ""
				};
				newSl.Add("Source-None", codecOsd);
			}
			// add sources...
			for (uint i = 0; i <= 19; i++)
			{
				var name = EISC.StringOutput[StringJoin.SourceNameJoinStart + i].StringValue;
				if (EISC.BooleanOutput[BoolJoin.UseSourceEnabled].BoolValue
					&& !EISC.BooleanOutput[BoolJoin.SourceIsEnabledJoinStart + i].BoolValue)
				{
					continue;
				}		
				else if(!EISC.BooleanOutput[BoolJoin.UseSourceEnabled].BoolValue && string.IsNullOrEmpty(name))
					break;
				var icon = EISC.StringOutput[StringJoin.SourceIconJoinStart + i].StringValue;
				var key = EISC.StringOutput[StringJoin.SourceKeyJoinStart + i].StringValue;
				var type = EISC.StringOutput[StringJoin.SourceTypeJoinStart + i].StringValue;
				var disableShare = EISC.BooleanOutput[BoolJoin.SourceShareDisableJoinStart + i].BoolValue;

				Debug.Console(0, this, "Adding source {0} '{1}'", key, name);
				var newSLI = new SourceListItem{
					Icon = icon,
					Name = name,
					Order = (int)i + 10,
					SourceKey = key,
					Type = eSourceListItemType.Route,
					DisableCodecSharing = disableShare,
				};
				newSl.Add(key, newSLI);
                
				string group = "genericsource";
				if (groupMap.ContainsKey(type))
				{
					group = groupMap[type];
				}
				
				// add dev to devices list
				var devConf = new DeviceConfig {
					Group = group,
					Key = key,
					Name = name,
					Type = type
				};
				co.Devices.Add(devConf);

				if (group.ToLower().StartsWith("settopbox")) // Add others here as needed
				{
					SetupSourceFunctions(key);
				}
			}

			co.SourceLists.Add("default", newSl);

			// Build "audioCodec" config if we need
			if (!string.IsNullOrEmpty(rmProps.AudioCodecKey))
			{
				var acFavs = new List<PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem>();
				for (uint i = 0; i < 4; i++)
				{
					if (!EISC.GetBool(BoolJoin.SpeedDialVisibleStartJoin + i))
					{
						break;
					}
					acFavs.Add(new PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem()
					{
						Name = EISC.GetString(StringJoin.SpeedDialNameStartJoin + i),
						Number = EISC.GetString(StringJoin.SpeedDialNumberStartJoin + i),
						Type = PepperDash.Essentials.Devices.Common.Codec.eCodecCallType.Audio
					});
				}

				var acProps = new
				{
					favorites = acFavs
				};

				var acStr = "audioCodec";
				var acConf = new DeviceConfig()
				{
					Group = acStr,
					Key = acStr,
					Name = acStr,
					Type = acStr,
					Properties = JToken.FromObject(acProps)
				};
				co.Devices.Add(acConf);
			}

			// Build Video codec config
			if (!string.IsNullOrEmpty(rmProps.VideoCodecKey))
			{
				// No favorites, for now?
				var favs = new List<PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem>();

				// cameras
				var camsProps = new List<object>();
				for (uint i = 0; i < 9; i++)
				{
					var name = EISC.GetString(i + StringJoin.CameraNearNameStart);
					if (!string.IsNullOrEmpty(name))
					{
						camsProps.Add(new
						{
							name = name,
							selector = "camera" + (i + 1),
						});
					}
				}
				var farName = EISC.GetString(StringJoin.CameraFarName);
				if (!string.IsNullOrEmpty(farName))
				{
					camsProps.Add(new
					{
						name = farName,
						selector = "cameraFar",
					});
				}

				var props = new
				{
					favorites = favs,
					cameras = camsProps,
				};
				var str = "videoCodec";
				var conf = new DeviceConfig()
				{
					Group = str,
					Key = str,
					Name = str,
					Type = str,
					Properties = JToken.FromObject(props)
				};
				co.Devices.Add(conf);
			}

			Debug.Console(0, this, "******* CONFIG FROM DDVC: \r{0}", JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented));

			var handler = ConfigurationIsReady;
			if (handler != null)
			{
				handler(this, new EventArgs());
			}

			ConfigIsLoaded = true;
		}

		/// <summary>
		/// 
		/// </summary>
		void SendFullStatus()
		{
			if (ConfigIsLoaded)
			{
                var count = EISC.UShortOutput[UshortJoin.NumberOfAuxFaders].UShortValue;

                Debug.Console(1, this, "The Fader Count is : {0}", count);

                // build volumes object, serialize and put in content of method below

                // Create auxFaders
				var auxFaderDict = new Dictionary<string, Volume>();
				for (uint i = 2; i <= count; i++)
				{
					auxFaderDict.Add("level-" + i,
						new Volume("level-" + i,
							EISC.UShortOutput[i].UShortValue,
							EISC.BooleanOutput[i].BoolValue,
							EISC.StringOutput[i].StringValue,
							true,
							"someting.png"));
				}

                var volumes = new Volumes();

                volumes.Master = new Volume("master", 
                                EISC.UShortOutput[UshortJoin.MasterVolumeLevel].UShortValue,
                                EISC.BooleanOutput[BoolJoin.MasterVolumeIsMuted].BoolValue,
                  				EISC.StringOutput[1].StringValue,
				                true,
				                "something.png");
				volumes.Master.HasPrivacyMute = true;
				volumes.Master.PrivacyMuted = EISC.BooleanOutput[BoolJoin.PrivacyMute].BoolValue;

                volumes.AuxFaders = auxFaderDict;
				volumes.NumberOfAuxFaders = EISC.UShortInput[UshortJoin.NumberOfAuxFaders].UShortValue;

                PostStatusMessage(new
                    {
						activityMode = GetActivityMode(),
                        isOn = EISC.BooleanOutput[BoolJoin.RoomIsOn].BoolValue,
                        selectedSourceKey = EISC.StringOutput[StringJoin.SelectedSourceKey].StringValue,
                        volumes = volumes
                    });			
			}
			else
			{
				PostStatusMessage(new
				{
					error = "systemNotReady"
				});
			}
		}

		/// <summary>
		/// Returns the activity mode int
		/// </summary>
		/// <returns></returns>
		int GetActivityMode()
		{
			if (EISC.BooleanOutput[BoolJoin.ActivityPhoneCall].BoolValue) return 2;
			else if (EISC.BooleanOutput[BoolJoin.ActivityShare].BoolValue) return 1;
			else if (EISC.BooleanOutput[BoolJoin.ActivityVideoCall].BoolValue) return 3;
			return 0;
		}

		/// <summary>
		/// Helper for posting status message
		/// </summary>
		/// <param name="contentObject">The contents of the content object</param>
		void PostStatusMessage(object contentObject)
		{
			Parent.SendMessageToServer(JObject.FromObject(new
				{
					type = "/room/status/",
					content = contentObject
				}));
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="messageType"></param>
		/// <param name="contentObject"></param>
		void PostMessage(string messageType, object contentObject)
		{
			Parent.SendMessageToServer(JObject.FromObject(new
			{
				type = messageType,
				content = contentObject
			}));
		}


		/// <summary>
		/// 
		/// </summary>
		/// <param name="currentDevice"></param>
		/// <param name="args"></param>
		void EISC_SigChange(object currentDevice, Crestron.SimplSharpPro.SigEventArgs args)
		{
			if (Debug.Level >= 1)
				Debug.Console(1, this, "DDVC EISC change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);
			var uo = args.Sig.UserObject;
			if (uo is Action<bool>)
				(uo as Action<bool>)(args.Sig.BoolValue);
			else if (uo is Action<ushort>)
				(uo as Action<ushort>)(args.Sig.UShortValue);
			else if (uo is Action<string>)
				(uo as Action<string>)(args.Sig.StringValue);
		}

		/// <summary>
		/// Returns the mapping of types to groups, for setting up devices.
		/// </summary>
		/// <returns></returns>
		Dictionary<string, string> GetSourceGroupDictionary()
		{
			//type, group
			var d = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
			{
				{ "laptop", "pc" },
				{ "pc", "pc" },
				{ "wireless", "genericsource" },
				{ "iptv", "settopbox" }

			};
			return d;
		}

		/// <summary>
		/// updates the usercode from server
		/// </summary>
		protected override void UserCodeChange()
		{
			Debug.Console(1, this, "Server user code changed: {0}", UserCode);
			EISC.StringInput[StringJoin.UserCodeToSystem].StringValue = UserCode;
			EISC.StringInput[StringJoin.ServerUrl].StringValue = Parent.Config.ClientAppUrl;
		}
	}
}