using Ionic.Crc;
using Npgsql;
using skyscraper5.src.InteractionChannel.Model;
using skyscraper5.src.InteractionChannel.Model.Descriptors;
using skyscraper5.src.InteractionChannel.Model2;
using skyscraper5.src.Skyscraper.Scraper.Storage.Utilities;
using skyscraper8.InteractionChannel.Model2;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using skyscraper8.InteractionChannel.Model;
using skyscraper8.InteractionChannel.Model2.Descriptors;

namespace skyscraper5.Data.PostgreSql
{
	public partial class PostgresqlDataStore
	{
		private List<Tuple<ushort, uint, uint>> _tbtpCache;
		private ushort? timNid;


		public bool TestForTerminalBurstTimePlan(ushort interactiveNetworkId, uint groupId, uint logonId)
		{
			timNid = interactiveNetworkId;
			if (_tbtpCache == null)
				_tbtpCache = new List<Tuple<ushort, uint, uint>>();
			Tuple<ushort, uint, uint> coords = new Tuple<ushort, uint, uint>(interactiveNetworkId, groupId, logonId);
			if (_tbtpCache.Contains(coords))
				return true;

			bool result;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand npgsqlCommand = connection.CreateCommand();
				npgsqlCommand.CommandText = "SELECT dateadded FROM dvb_ic_tbtp WHERE network_id = @nid AND group_id = @gid AND @logon_id = @lid";
				npgsqlCommand.Parameters.AddWithValue("@nid", (int)interactiveNetworkId);
				npgsqlCommand.Parameters.AddWithValue("@gid", (long)groupId);
				npgsqlCommand.Parameters.AddWithValue("@lid", (long)logonId);
				NpgsqlDataReader dataReader = npgsqlCommand.ExecuteReader();
				result = dataReader.Read();
				dataReader.Close();
				npgsqlCommand.Dispose();
				connection.Close();
			}
			if (result)
				_tbtpCache.Add(coords);
			return result;
		}

		public void StoreTerminalBurstTimePlan(ushort interactiveNetworkId, uint gtoupId, uint superframeCount, uint frameNumber, Tbtp.TbtpFrame.BtpEntity btp)
		{
			timNid = interactiveNetworkId;
			if (_tbtpCache == null)
				_tbtpCache = new List<Tuple<ushort, uint, uint>>();
			Tuple<ushort, uint, uint> coords = new Tuple<ushort, uint, uint>(interactiveNetworkId, gtoupId, btp.LogonId);
			_tbtpCache.Add(coords);
			EnqueueTask(x => WriteTbtp(x, interactiveNetworkId, gtoupId, superframeCount, frameNumber, btp));

		}

		private void WriteTbtp(NpgsqlConnection x, ushort interactiveNetworkId, uint gtoupId, uint superframeCount, uint frameNumber, Tbtp.TbtpFrame.BtpEntity btp)
		{
			timNid = interactiveNetworkId;
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "insert into dvb_ic_tbtp (network_id, group_id, superframe_count, frame_number, logon_id, assignment_type,\r\n                         vbdc_queue_empty, start_slot, channel_id, assignment_count)\r\n" +
				"values (@nid,@gid,@scount,@fnumber,@lid,@atype,@vqemtpy,@sslot,@cid,@acount);";
			command.Parameters.AddWithValue("@nid", (int)interactiveNetworkId);
			command.Parameters.AddWithValue("@gid", (long)gtoupId);
			command.Parameters.AddWithValue("@scount", (long)superframeCount);
			command.Parameters.AddWithValue("@fnumber", (long)frameNumber);
			command.Parameters.AddWithValue("@lid", (long)btp.LogonId);
			command.Parameters.AddWithValue("@atype", (int)btp.AssignmentType);
			command.Parameters.AddWithValue("@vqempty", btp.VbdcQueueEmptyFlag);
			command.Parameters.AddWithValue("@sslot", (long)btp.StartSlot);
			command.Parameters.AddWithValue("@cid", (long?)btp.ChannelId);
			command.Parameters.AddWithValue("@acount", (long)btp.AssignmentCount);
			command.ExecuteNonQuery();
		}

		private List<Tuple<ushort, byte, ushort>> _cmtCache;
		public bool TestForCmtEntry(ushort interactiveNetworkId, Cmt.CmtEntry entry)
		{
			timNid = interactiveNetworkId;
			if (_cmtCache == null)
				_cmtCache = new List<Tuple<ushort, byte, ushort>>();
			Tuple<ushort, byte, ushort> tuple = new Tuple<ushort, byte, ushort>(interactiveNetworkId, entry.GroupId, entry.LoginId);
			if (_cmtCache.Contains(tuple))
				return true;

			bool result;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT dateadded FROM dvb_ic_cmt WHERE network_id = @nid AND group_id = @gid AND login_id = @lid";
				command.Parameters.AddWithValue("@nid", (int)interactiveNetworkId);
				command.Parameters.AddWithValue("@gid", (short)entry.GroupId);
				command.Parameters.AddWithValue("@lid", (int)entry.LoginId);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				result = dataReader.Read();
				dataReader.Close();
				command.Dispose();
				connection.Close();
			}

			if (result)
				_cmtCache.Add(tuple);
			return result;
		}

		public void InsertCmtEntry(ushort interactiveNetworkId, Cmt.CmtEntry entry)
		{
			timNid = interactiveNetworkId;
			if (_cmtCache == null)
				_cmtCache = new List<Tuple<ushort, byte, ushort>>();
			Tuple<ushort, byte, ushort> tuple = new Tuple<ushort, byte, ushort>(interactiveNetworkId, entry.GroupId, entry.LoginId);
			_cmtCache.Add(tuple);
			EnqueueTask(x => WriteCmt(x, interactiveNetworkId, entry));
		}

		private void WriteCmt(NpgsqlConnection x, ushort interactiveNetworkId, Cmt.CmtEntry entry)
		{
			timNid = interactiveNetworkId;
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "insert into dvb_ic_cmt (network_id, group_id, login_id, slot_type, burst_time_scaling, burst_time_correction,\r\n                        power_correction, esn0, frequency_correction)\r\nvalues " +
				"(@nid,@gid,@lid,@stype,@btscaling,@btcorrection,@pcorrection,@esn,@fcorrection)";
			command.Parameters.AddWithValue("@nid", (int)interactiveNetworkId);
			command.Parameters.AddWithValue("@gid", entry.GroupId);
			command.Parameters.AddWithValue("@lid", (int)entry.LoginId);
			command.Parameters.AddWithValue("@stype", (int)entry.SlotType);
			command.Parameters.AddWithValue("@btscaling", entry.BurstTimeScaling);
			command.Parameters.AddWithValue("@btcorrection", (long?)entry.BurstTimeCorrection);
			command.Parameters.AddWithValue("@pcorrection", NpgsqlTypes.NpgsqlDbType.Integer, entry.PowerCorrection);
			command.Parameters.AddWithValue("@esn", entry.EsN0);
			command.Parameters.AddWithValue("@fcorrection", (int)entry.FrequencyCorrection);
			SetNulls(command);
			command.ExecuteNonQuery();
		}

		private Dictionary<ushort, int> _rmtTransmissionStdCache;
		public int GetRmtTransmissionStandard(ushort networkId)
		{
			timNid = networkId;
			if (_rmtTransmissionStdCache == null)
				_rmtTransmissionStdCache = new Dictionary<ushort, int>();
			if (_rmtTransmissionStdCache.ContainsKey(networkId))
				return _rmtTransmissionStdCache[networkId];

			int result = 0;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT MAX(f_transmission_standard) FROM dvb_ic_rmt_transport_stream WHERE nid = @nid";
				command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)networkId);
				NpgsqlDataReader npgsqlDataReader = command.ExecuteReader();
				if (npgsqlDataReader.Read())
				{
					if (!npgsqlDataReader.IsDBNull(0))
					{
						result = npgsqlDataReader.GetInt32(0);
					}
				}
				npgsqlDataReader.Close();
				command.Dispose();
				connection.Close();
			}
			if (result != 0)
				_rmtTransmissionStdCache.Add(networkId, result);
			return result;
		}

		private Dictionary<ushort, byte[]> _tmstCache;
		public byte[] GetTmst(ushort interactiveNetworkId)
		{
			timNid = interactiveNetworkId;
			if (_tmstCache == null)
				_tmstCache = new Dictionary<ushort, byte[]>();
			if (_tmstCache.ContainsKey(interactiveNetworkId))
				return _tmstCache[interactiveNetworkId];

			byte[] result;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT supported FROM dvb_ic_tmst WHERE network_id = @nid";
				command.Parameters.AddWithValue("@nid", (int)interactiveNetworkId);
				NpgsqlDataReader npgsqlDataReader = command.ExecuteReader();
				if (npgsqlDataReader.Read())
					result = npgsqlDataReader.GetByteArray(0);
				else
					result = null;
				npgsqlDataReader.Close();
				command.Dispose();
				connection.Close();
			}
			_tmstCache.Add(interactiveNetworkId, result);
			return result;
		}

		public void InsertTmst(ushort interactiveNetworkId, byte[] modes)
		{
			timNid = interactiveNetworkId;
			if (_tmstCache == null)
				_tmstCache = new Dictionary<ushort, byte[]>();
			if (_tmstCache.ContainsKey(interactiveNetworkId))
				if (_tmstCache[interactiveNetworkId] == null)
					_tmstCache.Remove(interactiveNetworkId);
			_tmstCache.Add(interactiveNetworkId, modes);
			EnqueueTask(x => InsertTmstEx(x, interactiveNetworkId, modes));
		}

		private void InsertTmstEx(NpgsqlConnection x, ushort interactiveNetworkId, byte[] modes)
		{
			timNid = interactiveNetworkId;
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "insert into dvb_ic_tmst (network_id, supported ) values (@nid,@data)";
			command.Parameters.AddWithValue("@nid", (int)interactiveNetworkId);
			command.Parameters.AddWithValue("@data", modes);
			int a = command.ExecuteNonQuery();
			command.Dispose();
		}

		public void UpdateTmst(ushort interactiveNetworkId, byte[] modes)
		{
			timNid = interactiveNetworkId;
			if (_tmstCache == null)
				_tmstCache = new Dictionary<ushort, byte[]>();
			_tmstCache.Remove(interactiveNetworkId);
			_tmstCache.Add(interactiveNetworkId, modes);
			EnqueueTask(x => UpdateTmstEx(x, interactiveNetworkId, modes));
		}

		private void UpdateTmstEx(NpgsqlConnection x, ushort interactiveNetworkId, byte[] modes)
		{
			timNid = interactiveNetworkId;
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "UPDATE dvb_ic_tmst\r\nSET supported = @data, version = version + 1, dateupdated = CURRENT_TIMESTAMP\r\nWHERE network_id = @nid";
			command.Parameters.AddWithValue("@nid", (int)interactiveNetworkId);
			command.Parameters.AddWithValue("@data", modes);
			int a = command.ExecuteNonQuery();
			command.Dispose();
		}


		private HashSet<DatabaseKeyRmtLinkage> _rmtLinkageCache;
		public bool TestForRmtLinkage(_0x4a_LinkageDescriptor linkage)
		{
			if (_rmtLinkageCache == null)
				_rmtLinkageCache = new HashSet<DatabaseKeyRmtLinkage>();
			DatabaseKeyRmtLinkage key = linkage.ToKey();
			if (_rmtLinkageCache.Contains(key))
				return true;

			bool result;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT dateadded FROM dvb_ic_rmt_linkage WHERE tsid=@tsid AND onid=@onid AND sid=@sid AND inid=@inid";
				command.Parameters.AddWithValue("@tsid", (int)linkage.TransportStreamId);
				command.Parameters.AddWithValue("@onid", (int)linkage.OriginalNetworkId);
				command.Parameters.AddWithValue("@sid", (int)linkage.ServiceId);
				command.Parameters.AddWithValue("@inid", (int)linkage.InteractiveNetworkId);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				result = dataReader.Read();
				dataReader.Close();
				command.Dispose();
				connection.Close();
			}
			if (result)
				_rmtLinkageCache.Add(key);
			return result;
		}

		public void InsertRmtLinkage(_0x4a_LinkageDescriptor linkage)
		{
			if (_rmtLinkageCache == null)
				_rmtLinkageCache = new HashSet<DatabaseKeyRmtLinkage>();
			DatabaseKeyRmtLinkage key = linkage.ToKey();
			if (_rmtLinkageCache.Contains(key))
				return;
			_rmtLinkageCache.Add(key);
			EnqueueTask(x => InsertRmtLinkageEx(x, linkage));
		}

		private void InsertRmtLinkageEx(NpgsqlConnection x, _0x4a_LinkageDescriptor linkage)
		{
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "insert into dvb_ic_rmt_linkage (tsid, onid, sid, linkage_type, inid, private_data) values (@tsid,@onid,@sid,@ltype,@inid,@pdata) returning uuid";
			command.Parameters.AddWithValue("@tsid", (int)linkage.TransportStreamId);
			command.Parameters.AddWithValue("@onid", (int)linkage.OriginalNetworkId);
			command.Parameters.AddWithValue("@sid", (int)linkage.ServiceId);
			command.Parameters.AddWithValue("@ltype", (int)linkage.LinkageType);
			command.Parameters.AddWithValue("@inid", (int)linkage.InteractiveNetworkId);
			command.Parameters.AddWithValue("@pdata", linkage.PrivateData);
			if (linkage.PopulationIds.Length > 0)
			{
				NpgsqlDataReader dataReader = command.ExecuteReader();
				dataReader.Read();
				object o = dataReader.GetValue(0);
				dataReader.Close();
				command.Dispose();
				Guid parent = (Guid)o;
				foreach (_0x4a_LinkageDescriptor.Population population in linkage.PopulationIds)
				{
					InsertRmtLinkagePopulation(x, parent, population);
				}
			}
			else
			{
				command.ExecuteNonQuery();
				command.Dispose();
			}
		}

		private void InsertRmtLinkagePopulation(NpgsqlConnection x, Guid parent, _0x4a_LinkageDescriptor.Population population)
		{
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "insert into dvb_ic_rmt_linkage_populations (parent, base, mask) values (@parent,@base,@mask)";
			command.Parameters.AddWithValue("@parent", NpgsqlTypes.NpgsqlDbType.Uuid, parent);
			command.Parameters.AddWithValue("@base", NpgsqlTypes.NpgsqlDbType.Integer, (int)population.Base);
			command.Parameters.AddWithValue("@mask", NpgsqlTypes.NpgsqlDbType.Integer, (int)population.Mask);
			command.ExecuteNonQuery();
		}

		private HashSet<DatabaseKeyRmtTransportStream> _rmtTransportStreamCache;
		public bool TestForRmtTransportStream(ushort networkId, Rmt.TransportStream transportStream)
		{
			timNid = networkId;
			if (_rmtTransportStreamCache == null)
				_rmtTransportStreamCache = new HashSet<DatabaseKeyRmtTransportStream>();
			DatabaseKeyRmtTransportStream key = transportStream.ToKey(networkId);
			if (_rmtTransportStreamCache.Contains(key))
				return true;

			bool result;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT dateadded FROM dvb_ic_rmt_transport_stream WHERE nid=@nid AND tsid=@tsid AND onid=@onid";
				command.Parameters.AddWithValue("@nid", (int)networkId);
				command.Parameters.AddWithValue("@tsid", (int)transportStream.TransportStreamId);
				command.Parameters.AddWithValue("@onid", (int)transportStream.OriginalNetworkId);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				result = dataReader.Read();
				dataReader.Close();
				command.Dispose();
				connection.Close();
			}
			if (result)
				_rmtTransportStreamCache.Add(key);
			return result;
		}

		public void InsertRmtTransportStream(ushort networkId, Rmt.TransportStream transportStream)
		{
			timNid = networkId;
			if (_rmtTransportStreamCache == null)
				_rmtTransportStreamCache = new HashSet<DatabaseKeyRmtTransportStream>();
			DatabaseKeyRmtTransportStream key = transportStream.ToKey(networkId);
			if (_rmtTransportStreamCache.Contains(key))
				return;
			_rmtTransportStreamCache.Add(key);
			EnqueueTask(x => InsertRmtTransportStreamEx(x, networkId, transportStream));
		}

		private void InsertRmtTransportStreamEx(NpgsqlConnection x, ushort networkId, Rmt.TransportStream transportStream)
		{
			timNid = networkId;
			_0xa9_SatelliteReturnLinkDescriptor r = transportStream.SatelliteReturnLink;
			_0xa8_SatelliteForwardLinkDescriptor f = transportStream.SatelliteForwardLink;
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "INSERT INTO dvb_ic_rmt_transport_stream (nid, tsid, onid, r_satellite_id, r_beam_id, r_gateway_id, r_orbital_position, r_cardinal_direction, r_superframe_id," +
				"r_tx_frequency_offset, r_private_data, f_satellite_id, f_beam_id, f_ncc_id, f_link_usage, f_local_link_id, f_frequency, f_orbital_position, f_cardinal_direction, f_polarization, " +
				"f_transmission_standard, f_scrambling_sequence_selector, f_roll_off, f_symbol_rate, f_fec_inner, f_input_stream_identifier, f_spreading_code_selector, f_scrambling_sequence_index, " +
				"f_private_data_bytes) VALUES " +
				"(@nid,@tsid,@onid,@rsid,@rbid,@rgid,@roposition,@rcdirection,@rsfid,@rtxfoffset,@rpdata,@fsid,@fbid,@fnccid,@flusage,@fllid,@ffrequency,@foposition,@fcdirection,@fpolarization,@ftstandard," +
				"@fssselector,@froff,@fsrate,@ffecinner,@fisidentifier,@fscselector,@fssindex,@fprbytes)";
			command.Parameters.AddWithValue("@nid", (int)networkId);
			command.Parameters.AddWithValue("@tsid", (int)transportStream.TransportStreamId);
			command.Parameters.AddWithValue("@onid", (int)transportStream.OriginalNetworkId);
			command.Parameters.Add("@rsid", NpgsqlTypes.NpgsqlDbType.Integer);
			command.Parameters.Add("@rbid", NpgsqlTypes.NpgsqlDbType.Integer);
			command.Parameters.Add("@rgid", NpgsqlTypes.NpgsqlDbType.Integer);
			command.Parameters.Add("@roposition", NpgsqlTypes.NpgsqlDbType.Integer);
			command.Parameters.Add("@rcdirection", NpgsqlTypes.NpgsqlDbType.Boolean);
			command.Parameters.Add("@rsfid", NpgsqlTypes.NpgsqlDbType.Integer);
			command.Parameters.Add("@rtxfoffset", NpgsqlTypes.NpgsqlDbType.Bigint);
			command.Parameters.Add("@rpdata", NpgsqlTypes.NpgsqlDbType.Bytea);
			if (r != null)
			{
				command.Parameters["@rsid"].Value = (int)r.SatelliteId;
				command.Parameters["@rbid"].Value = (int)r.BeamId;
				command.Parameters["@rgid"].Value = (int)r.GatewayId;
				command.Parameters["@roposition"].Value = (int)r.OrbitalPosition;
				command.Parameters["@rcdirection"].Value = r.Eastern;
				command.Parameters["@rsfid"].Value = (int)r.SuperframeId;
				command.Parameters["@rtxfoffset"].Value = (long)r.TxFrequencyOffset;
				command.Parameters["@rpdata"].Value = r.PrivateDataBytes;
			}
			command.Parameters.AddWithValue("@fsid", (int)f.SatelliteId);
			command.Parameters.AddWithValue("@fbid", (int)f.BeamId);
			command.Parameters.AddWithValue("@fnccid", (int)f.NccId);
			command.Parameters.AddWithValue("@flusage", (int)f.LinkUsage);
			command.Parameters.AddWithValue("@fllid", (int)f.LocalLinkId);
			command.Parameters.AddWithValue("@ffrequency", (long)f.Frequency);
			command.Parameters.AddWithValue("@foposition", (int)f.OrbitalPosition);
			command.Parameters.AddWithValue("@fcdirection", f.East);
			command.Parameters.AddWithValue("@fpolarization", (int)f.Polarization);
			command.Parameters.AddWithValue("@ftstandard", (int)f.TransmissionStandard);
			command.Parameters.AddWithValue("@fssselector", NpgsqlTypes.NpgsqlDbType.Boolean, f.ScramblingSequenceSelector);
			command.Parameters.AddWithValue("@froff", NpgsqlTypes.NpgsqlDbType.Double, f.RollOff);
			command.Parameters.AddWithValue("@fsrate", (int)f.SymbolRate);
			command.Parameters.AddWithValue("@ffecinner", NpgsqlTypes.NpgsqlDbType.Integer, (int?)f.FecInner);
			command.Parameters.AddWithValue("@fisidentifier", NpgsqlTypes.NpgsqlDbType.Integer, (int?)f.InputStreamIndentifier);
			command.Parameters.AddWithValue("@fscselector", NpgsqlTypes.NpgsqlDbType.Integer, f.SpreadingCodeSelector);
			command.Parameters.AddWithValue("@fssindex", f.ScramblingSequenceIndex);
			command.Parameters.AddWithValue("@fprbytes", f.PrivateDataBytes);
			SetNulls(command);
			command.ExecuteNonQuery();
		}

		private HashSet<Tuple<int, long>> _sctCache;
		public bool TestForSuperframeComposition(ushort interactiveNetworkId, Sct.Superframe superframe)
		{
			timNid = interactiveNetworkId;
			if (_sctCache == null)
				_sctCache = new HashSet<Tuple<int, long>>();
			Tuple<int, long> key = new Tuple<int, long>(interactiveNetworkId, superframe.SuperframeId);
			if (_sctCache.Contains(key))
				return true;

			bool result;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT dateadded FROM dvb_ic_sct WHERE nid = @nid AND superframe_id = @sframeid";
				command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)interactiveNetworkId);
				command.Parameters.AddWithValue("@sframeid", NpgsqlTypes.NpgsqlDbType.Bigint, (long)superframe.SuperframeId);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				result = dataReader.Read();
				dataReader.Close();
				command.Dispose();
				connection.Close();
			}

			if (result)
				_sctCache.Add(key);
			return result;
		}

		public void StoreSuperframeComposition(ushort interactiveNetworkId, Sct.Superframe superframe)
		{
			timNid = interactiveNetworkId;
			if (_sctCache == null)
				_sctCache = new HashSet<Tuple<int, long>>();
			Tuple<int, long> key = new Tuple<int, long>(interactiveNetworkId, superframe.SuperframeId);
			if (_sctCache.Contains(key))
				return;
			EnqueueTask(x => StoreSuperframeCompositionEx(x, interactiveNetworkId, superframe));
		}

		private void StoreSuperframeCompositionEx(NpgsqlConnection x, ushort interactiveNetworkId, Sct.Superframe superframe)
		{
			timNid = interactiveNetworkId;

			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "SELECT dateadded FROM dvb_ic_sct WHERE nid = @nid AND superframe_id = @sframeid";
			command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)interactiveNetworkId);
			command.Parameters.AddWithValue("@sframeid", NpgsqlTypes.NpgsqlDbType.Bigint, (long)superframe.SuperframeId);
			NpgsqlDataReader dataReader = command.ExecuteReader();
			bool alreadyExists = dataReader.Read();
			dataReader.Close();
			command.Dispose();
			if (alreadyExists)
				return;

			command = x.CreateCommand();
			command.CommandText = "insert into dvb_ic_sct (nid, superframe_id, large_timing_uncertaintiy_flag, uplink_polarization, superframe_start_time_base, superframe_start_time_ext, " +
				"superframe_duration, superframe_centre_frequency, superframe_counter) values (@nid,@sid,@ltuflag,@upolarization,@sstbase,@sstext,@sduration,@scfrequency,@scounter) RETURNING uuid";
			command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)interactiveNetworkId);
			command.Parameters.AddWithValue("@sid", NpgsqlTypes.NpgsqlDbType.Bigint, (long)superframe.SuperframeId);
			command.Parameters.AddWithValue("@ltuflag", NpgsqlTypes.NpgsqlDbType.Boolean, superframe.LargeTimingUncertainityFlag);
			command.Parameters.AddWithValue("@upolarization", NpgsqlTypes.NpgsqlDbType.Integer, (int)superframe.UplinkPolarization);
			command.Parameters.AddWithValue("@sstbase", NpgsqlTypes.NpgsqlDbType.Bigint, (long)superframe.SuperframeStartTimeBase);
			command.Parameters.AddWithValue("@sstext", NpgsqlTypes.NpgsqlDbType.Bigint, (long)superframe.SuperframeStartTimeExt);
			command.Parameters.AddWithValue("@sduration", NpgsqlTypes.NpgsqlDbType.Bigint, (long)superframe.SuperframeDuration);
			command.Parameters.AddWithValue("@scfrequency", NpgsqlTypes.NpgsqlDbType.Bigint, (long)superframe.SuperframeCentreFrequency);
			command.Parameters.AddWithValue("@scounter", NpgsqlTypes.NpgsqlDbType.Bigint, (long)superframe.SuperframeCounter);
			dataReader = command.ExecuteReader();
			dataReader.Read();
			Guid parent = dataReader.GetGuid(0);
			dataReader.Close();
			command.Dispose();
			for (int i = 0; i < superframe.Frames.Length; i++)
			{
				StoreSuperframeFrame(x, parent, superframe.Frames[i], i);
			}
		}

		private void StoreSuperframeFrame(NpgsqlConnection x, Guid parent, Sct.Superframe.Frame frame, int ordinal)
		{
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "INSERT INTO dvb_ic_sct_frame (parent, frame_id, frame_start_time, frame_centre_frequency_offset, ordinal) " +
				"VALUES (@parent,@fid,@fstime,@fcfoffset,@ordinal)";
			command.Parameters.AddWithValue("@parent", NpgsqlTypes.NpgsqlDbType.Uuid, parent);
			command.Parameters.AddWithValue("@fid", NpgsqlTypes.NpgsqlDbType.Bigint, (long)frame.FrameId);
			command.Parameters.AddWithValue("@fstime", NpgsqlTypes.NpgsqlDbType.Bigint, (long)frame.FrameStartTime);
			command.Parameters.AddWithValue("@fcfoffset", NpgsqlTypes.NpgsqlDbType.Bigint, (long)frame.FrameCentreFrequencyOffset);
			command.Parameters.AddWithValue("@ordinal", NpgsqlTypes.NpgsqlDbType.Integer, ordinal);
			command.ExecuteNonQuery();
		}

		private HashSet<Tuple<int, int>> _fctCache;
		public bool TestForFrameComposition(ushort interactiveNetworkId, Fct.Frame frame)
		{
			timNid = interactiveNetworkId;
			if (_fctCache == null)
				_fctCache = new HashSet<Tuple<int, int>>();
			Tuple<int, int> key = new Tuple<int, int>((int)interactiveNetworkId, (int)frame.FrameId);
			if (_fctCache.Contains(key))
				return true;

			bool result;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT dateadded FROM dvb_ic_fct WHERE nid = @nid AND frame_id = @fid";
				command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)interactiveNetworkId);
				command.Parameters.AddWithValue("@fid", NpgsqlTypes.NpgsqlDbType.Integer, (int)frame.FrameId);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				result = dataReader.Read();
				dataReader.Close();
				command.Dispose();
				connection.Close();
			}
			if (result)
				_fctCache.Add(key);
			return result;
		}

		public void InsertFctFrame(ushort interactiveNetworkId, Fct.Frame frame)
		{
			timNid = interactiveNetworkId;
			if (_fctCache == null)
				_fctCache = new HashSet<Tuple<int, int>>();
			Tuple<int, int> key = new Tuple<int, int>((int)interactiveNetworkId, (int)frame.FrameId);
			if (_fctCache.Contains(key))
				return;
			_fctCache.Add(key);
			EnqueueTask(x => InsertFctFrameEx(x, interactiveNetworkId, frame));
		}

		private void InsertFctFrameEx(NpgsqlConnection x, ushort interactiveNetworkId, Fct.Frame frame)
		{
			timNid = interactiveNetworkId;
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "INSERT INTO dvb_ic_fct (nid, frame_id, frame_duration, total_timeslot_count, start_timeslot_number)" +
				"VALUES (@nid,@fid,@fduration,@ttcount,@stnumber) RETURNING uuid";
			command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)interactiveNetworkId);
			command.Parameters.AddWithValue("@fid", NpgsqlTypes.NpgsqlDbType.Integer, (int)frame.FrameId);
			command.Parameters.AddWithValue("@fduration", NpgsqlTypes.NpgsqlDbType.Bigint, (long)frame.FrameDuration);
			command.Parameters.AddWithValue("@ttcount", NpgsqlTypes.NpgsqlDbType.Integer, (int)frame.TotalTimeslotCount);
			command.Parameters.AddWithValue("@stnumber", NpgsqlTypes.NpgsqlDbType.Integer, (int)frame.StartTimeslotNumber);
			NpgsqlDataReader dataReader = command.ExecuteReader();
			dataReader.Read();
			Guid guid = dataReader.GetGuid(0);
			dataReader.Close();
			command.Dispose();
			for (int i = 0; i < frame.Timeslots.Length; i++)
			{
				InsertFctFrameTimeslot(x, guid, frame.Timeslots[i], i);
			}
		}

		private void InsertFctFrameTimeslot(NpgsqlConnection x, Guid guid, Fct.Frame.Timeslot timeslot, int ordinal)
		{
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "INSERT INTO dvb_ic_fct_timeslot (parent,timeslot_id,timeslot_frequency_offset,timeslot_time_offset, repeat_count, ordinal) " +
				"VALUES (@parent,@tid,@tfoffset,@ttoffset,@rcount,@ordinal)";
			command.Parameters.AddWithValue("@parent", NpgsqlTypes.NpgsqlDbType.Uuid, guid);
			command.Parameters.AddWithValue("@tid", NpgsqlTypes.NpgsqlDbType.Integer, (int)timeslot.TimeslotId);
			command.Parameters.AddWithValue("@tfoffset", NpgsqlTypes.NpgsqlDbType.Bigint, (long)timeslot.TimeslotFrequencyOffset);
			command.Parameters.AddWithValue("@ttoffset", NpgsqlTypes.NpgsqlDbType.Bigint, (long)timeslot.TimeslotTimeOffset);
			command.Parameters.AddWithValue("@rcount", NpgsqlTypes.NpgsqlDbType.Integer, (int)timeslot.RepeatCount);
			command.Parameters.AddWithValue("@ordinal", NpgsqlTypes.NpgsqlDbType.Integer, ordinal);
			command.ExecuteNonQuery();
		}

		private HashSet<Tuple<int, int>> _sptCache;
		public bool TestForSatellitePosition(ushort interactiveNetworkId, Spt.Satellite satellite)
		{
			timNid = interactiveNetworkId;
			if (_sptCache == null)
				_sptCache = new HashSet<Tuple<int, int>>();
			Tuple<int, int> key = new Tuple<int, int>(interactiveNetworkId, satellite.Id);
			if (_sptCache.Contains(key))
				return true;

			bool result;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT dateadded FROM dvb_ic_spt WHERE network_id = @nid AND satellite_id = @sid";
				command.Parameters.AddWithValue("@nid", (int)interactiveNetworkId);
				command.Parameters.AddWithValue("@sid", (int)satellite.Id);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				result = dataReader.Read();
				dataReader.Close();
				command.Dispose();
				connection.Close();
			}
			_sptCache.Add(key);
			return result;
		}

		public void StoreSatellitePosition(ushort interactiveNetworkId, Spt.Satellite satellite)
		{
			timNid = interactiveNetworkId;
			if (_sptCache == null)
				_sptCache = new HashSet<Tuple<int, int>>();
			Tuple<int, int> key = new Tuple<int, int>(interactiveNetworkId, satellite.Id);
			if (_sptCache.Contains(key))
				return;
			_sptCache.Add(key);
			EnqueueTask(x => StoreSatellitePositionEx(x, interactiveNetworkId, satellite));
		}

		private void StoreSatellitePositionEx(NpgsqlConnection x, ushort interactiveNetworkId, Spt.Satellite satellite)
		{
			timNid = interactiveNetworkId;
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "INSERT INTO dvb_ic_spt (network_id, satellite_id, x, y, z) VALUES (@nid,@sid,@x,@y,@z)";
			command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)interactiveNetworkId);
			command.Parameters.AddWithValue("@sid", NpgsqlTypes.NpgsqlDbType.Integer, (int)satellite.Id);
			command.Parameters.AddWithValue("@x", NpgsqlTypes.NpgsqlDbType.Bigint, (long)satellite.X);
			command.Parameters.AddWithValue("@y", NpgsqlTypes.NpgsqlDbType.Bigint, (long)satellite.Y);
			command.Parameters.AddWithValue("@z", NpgsqlTypes.NpgsqlDbType.Bigint, (long)satellite.Z);
			command.ExecuteNonQuery();
		}

		public void CreateTim(PhysicalAddress mac)
		{
			if (timNid == null)
				return;
			if (_tims == null)
				_tims = new List<PhysicalAddress>();
			if (_tims.Contains(mac))
				return;

			_tims.Add(mac);
			EnqueueTask(x => CreateTimEx(x, mac, timNid.Value));
		}

		private void CreateTimEx(NpgsqlConnection x, PhysicalAddress mac, ushort nid)
		{
			NpgsqlCommand testCommand = x.CreateCommand();
			testCommand.CommandText = "SELECT dateadded FROM dvb_ic_tim WHERE mac = @mac AND nid = @nid";
            testCommand.Parameters.AddWithValue("@mac", NpgsqlTypes.NpgsqlDbType.MacAddr, mac);
            testCommand.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)nid);
            NpgsqlDataReader dataReader = testCommand.ExecuteReader();
			bool alreadyKnown = dataReader.Read();
			dataReader.Close();
			if (alreadyKnown)
				return;


            NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "INSERT INTO dvb_ic_tim (mac,nid) VALUES (@mac,@nid)";
			command.Parameters.AddWithValue("@mac", NpgsqlTypes.NpgsqlDbType.MacAddr, mac);
			command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)nid);
			command.ExecuteNonQuery();
		}

		private List<PhysicalAddress> _tims;
		public bool TestForTim(PhysicalAddress mac)
		{
			if (timNid == null)
				return true;
			if (_tims == null)
				_tims = new List<PhysicalAddress>();
			if (_tims.Contains(mac))
				return true;

			bool result;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT dateadded FROM dvb_ic_tim WHERE mac = @mac AND nid = @nid";
				command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)timNid.Value);
				command.Parameters.AddWithValue("@mac", NpgsqlTypes.NpgsqlDbType.MacAddr, mac);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				result = dataReader.Read();
				dataReader.Close();
				command.Dispose();
				connection.Close();
			}
			if (result)
				_tims.Add(mac);
			return result;
		}

		private List<PhysicalAddress> _correctedTims;
		public bool CorrectTim(PhysicalAddress mac, _0xa1_CorrectionMessageDescriptor cmd)
		{
			if (timNid == null)
				return false;
			if (_correctedTims == null)
				_correctedTims = new List<PhysicalAddress>();
			if (_correctedTims.Contains(mac))
				return false;

			_correctedTims.Add(mac);
			EnqueueTask(x => CorrectTimEx(x, mac, cmd,timNid.Value));
			return true;
		}

		private void CorrectTimEx(NpgsqlConnection x, PhysicalAddress mac, _0xa1_CorrectionMessageDescriptor cmdKnown, ushort nid)
		{
			_0xa1_CorrectionMessageDescriptor cmdNew = new _0xa1_CorrectionMessageDescriptor(CorrectionMessageSlotType.TRF, 0, null, null, null, 0);
			NpgsqlCommand selectCommand = x.CreateCommand();
			selectCommand.CommandText = "SELECT cmd_slot_type, cmd_burst_time_scaling, cmd_burst_time_correction, cmd_power_correction, cmd_esn0, cmd_frequency_correction " +
				"FROM dvb_ic_tim " +
				"WHERE mac = @mac AND nid = @nid";
			selectCommand.Parameters.AddWithValue("@mac", NpgsqlTypes.NpgsqlDbType.MacAddr, mac);
			selectCommand.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)timNid.Value);
			NpgsqlDataReader dataReader = selectCommand.ExecuteReader();
			if (dataReader.Read())
			{
				if (!dataReader.IsDBNull(0) && !dataReader.IsDBNull(1) && !dataReader.IsDBNull(5))
				{
					CorrectionMessageSlotType slotType = (CorrectionMessageSlotType)dataReader.GetInt32(0);
					int burstTimeScaling = dataReader.GetInt32(1);
					uint? burstTimeCorrection = null;
					if (!dataReader.IsDBNull(2))
						burstTimeCorrection = (uint)dataReader.GetInt64(2);
					int? powerCorrection = null;
					if (!dataReader.IsDBNull(3))
						powerCorrection = dataReader.GetInt32(3);
					int? esn0 = null;
					if (!dataReader.IsDBNull(4))
						esn0 = dataReader.GetInt32(4);
					ushort frequencyCorrection = (ushort)dataReader.GetInt32(5);
					cmdNew = new _0xa1_CorrectionMessageDescriptor(slotType, burstTimeScaling, burstTimeCorrection, powerCorrection, esn0, frequencyCorrection);
				}
			}
			dataReader.Close();
			selectCommand.Dispose();

			if (cmdKnown.Equals(cmdNew))
				return;

			NpgsqlCommand updateCommand = x.CreateCommand();
			updateCommand.CommandText = "UPDATE dvb_ic_tim " +
				"SET cmd_slot_type = @stype, cmd_burst_time_scaling = @btscaling, cmd_burst_time_correction = @btcorrection, cmd_power_correction = @pcorrection, cmd_esn0 = @cesn, " +
				"cmd_frequency_correction = @fcorrection, version = version + 1, dateupdated = CURRENT_TIMESTAMP " +
				"WHERE mac = @mac AND nid = @nid";
			updateCommand.Parameters.Add("@stype", NpgsqlTypes.NpgsqlDbType.Integer);
			updateCommand.Parameters.Add("@btscaling", NpgsqlTypes.NpgsqlDbType.Integer);
			updateCommand.Parameters.Add("@btcorrection", NpgsqlTypes.NpgsqlDbType.Bigint);
			updateCommand.Parameters.Add("@pcorrection", NpgsqlTypes.NpgsqlDbType.Integer);
			updateCommand.Parameters.Add("@esn", NpgsqlTypes.NpgsqlDbType.Integer);
			updateCommand.Parameters.Add("@fcorrection", NpgsqlTypes.NpgsqlDbType.Integer);
			updateCommand.Parameters.Add("@mac", NpgsqlTypes.NpgsqlDbType.MacAddr);
			updateCommand.Parameters["@stype"].Value = cmdNew.SlotType;
			updateCommand.Parameters["@btscaling"].Value = cmdNew.BurstTimeScaling;
			updateCommand.Parameters["@btcorrection"].Value = cmdNew.BurstTimeCorrection;
			updateCommand.Parameters["@pcorrection"].Value = cmdNew.PowerCorrection;
			updateCommand.Parameters["@esn"].Value = cmdNew.EsN0;
			updateCommand.Parameters["@fcorrection"].Value = cmdNew.FrequencyCorrection;
			updateCommand.Parameters["@mac"].Value = mac;
			updateCommand.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)timNid.Value);
			SetNulls(updateCommand);
			int a = updateCommand.ExecuteNonQuery();
			Debug.Assert(a != 0);
		}

		private List<PhysicalAddress> contentedTims;
		public bool ContentionTim(PhysicalAddress mac, _0xab_ContentionControlDescriptor ccdNew)
		{
			if (timNid == null)
				return false;
			if (contentedTims == null)
				contentedTims = new List<PhysicalAddress>();
			if (contentedTims.Contains(mac))
				return false;

			bool needUpdate = true;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT ccd_superframe_id, ccd_csc_response_timeout, ccd_csc_max_losses, ccd_max_time_before_retry " +
					"FROM dvb_ic_tim " +
					"WHERE mac = @mac AND nid = @nid";
				command.Parameters.AddWithValue("@mac", NpgsqlTypes.NpgsqlDbType.MacAddr, mac);
				command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)timNid.Value);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				if (dataReader.Read())
				{
					if (!dataReader.IsDBNull(0))
					{
						int ccdSuperframeId = dataReader.GetInt32(0);
						long ccdCscResponseTimeout = dataReader.GetInt64(1);
						int ccdCscMaxLosses = dataReader.GetInt32(2);
						long ccdMaxTimeBeforeRetry = dataReader.GetInt64(3);
						_0xab_ContentionControlDescriptor ccdInDb = new _0xab_ContentionControlDescriptor((byte)ccdSuperframeId, (uint)ccdCscResponseTimeout, (byte)ccdCscMaxLosses, (uint)ccdMaxTimeBeforeRetry);
						needUpdate = !ccdNew.Equals(ccdInDb);
					}
				}
				dataReader.Close();
				command.Dispose();
				connection.Close();
			}

			contentedTims.Add(mac);
			if (needUpdate)
			{
				EnqueueTask(x => ContentionTimEx(x, mac, timNid.Value, ccdNew));
			}
			return needUpdate;
		}

		private void ContentionTimEx(NpgsqlConnection x, PhysicalAddress mac, ushort value, _0xab_ContentionControlDescriptor ccdNew)
		{
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "UPDATE dvb_ic_tim " +
				"SET ccd_superframe_id = @sid, ccd_csc_response_timeout = @crtimeout, ccd_csc_max_losses = @cmlosses, ccd_max_time_before_retry = @mtbretry, " +
				"    version = version + 1, dateupdated = CURRENT_TIMESTAMP " +
				"WHERE mac = @mac AND nid = @nid";
			command.Parameters.AddWithValue("@sid", ccdNew.SuperframeId);
			command.Parameters.AddWithValue("@crtimeout", (long)ccdNew.CscResponseTimeout);
			command.Parameters.AddWithValue("@cmlosses", ccdNew.CscMaxLosses);
			command.Parameters.AddWithValue("@mtbretry", (long)ccdNew.MaxTimeBeforeRetry);
			command.Parameters.AddWithValue("@mac", NpgsqlTypes.NpgsqlDbType.MacAddr, mac);
			command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)timNid.Value);
			command.ExecuteNonQuery();
		}


		private List<PhysicalAddress> correctionControlledTims;
		public bool CorrectionControlTim(PhysicalAddress mac, _0xac_CorrectionControlDescriptor newCcd)
		{
			if (timNid == null)
				return false;
			if (correctionControlledTims == null)
				correctionControlledTims = new List<PhysicalAddress>();
			if (correctionControlledTims.Contains(mac))
				return false;

			bool needUpdate = true;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT ccd_acq_response_timeout, ccd_sync_response_timeout, ccd_acq_max_losses, ccd_sync_max_losses " +
					"FROM dvb_ic_tim " +
					"WHERE mac = @mac AND nid = @nid";
				command.Parameters.AddWithValue("@mac", NpgsqlTypes.NpgsqlDbType.MacAddr, mac);
				command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)timNid.Value);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				if (dataReader.Read())
				{
					if (!dataReader.IsDBNull(0))
					{
						long acqResonseTimeout = dataReader.GetInt64(0);
						long syncResponseTimeout = dataReader.GetInt64(1);
						int acqMaxLosses = dataReader.GetInt32(2);
						int syncMaxLosses = dataReader.GetInt32(3);
						
						_0xac_CorrectionControlDescriptor ccdInDb = new _0xac_CorrectionControlDescriptor((uint)acqResonseTimeout,(uint)syncResponseTimeout, (byte)acqMaxLosses, (byte)syncMaxLosses);
						needUpdate = !newCcd.Equals(ccdInDb);
					}
				}
				dataReader.Close();
				command.Dispose();
				connection.Close();
			}

			if (needUpdate)
				EnqueueTask(x => CorrectionControlTimEx(x, timNid.Value, mac, newCcd));
			return needUpdate;
		}

		private void CorrectionControlTimEx(NpgsqlConnection x, ushort value, PhysicalAddress mac, _0xac_CorrectionControlDescriptor newCcd)
		{
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "UPDATE dvb_ic_tim " +
				"SET ccd_acq_response_timeout = @artimeout, ccd_sync_response_timeout = @srtimeout, ccd_acq_max_losses = @amlosses, ccd_sync_max_losses = @smlosses, version = version + 1, dateupdated = CURRENT_TIMESTAMP " +
				"WHERE mac = @mac AND nid = @nid";
			command.Parameters.AddWithValue("@artimeout", (long)newCcd.AcqResponseTimeout);
			command.Parameters.AddWithValue("@srtimeout", (long)newCcd.SyncResponseTimeout);
			command.Parameters.AddWithValue("@amlosses", newCcd.AcqMaxLosses);
			command.Parameters.AddWithValue("@smlosses", newCcd.SyncMaxLosses);
			command.Parameters.AddWithValue("@mac", NpgsqlTypes.NpgsqlDbType.MacAddr, mac);
			command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)timNid.Value);
			command.ExecuteNonQuery();
		}

		private Dictionary<Tuple<ushort, PhysicalAddress>, byte[]> _networkLayerInfoTims;
		public bool NetworkLayerInfoTim(PhysicalAddress mac, _0xa0_NetworkLayerInfoDescriptor nlid, DateTime timestamp)
		{
			if (timNid == null)
				return false;
			if (_networkLayerInfoTims == null)
				_networkLayerInfoTims = new Dictionary<Tuple<ushort, PhysicalAddress>, byte[]>();
			Tuple<ushort, PhysicalAddress> coordinate = new Tuple<ushort, PhysicalAddress>(timNid.Value, mac);
			if (!_networkLayerInfoTims.ContainsKey(coordinate))
			{
				byte[] newValue = new byte[0];
				using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
				{
					connection.Open();
					NpgsqlCommand command = connection.CreateCommand();
					command.CommandText = "SELECT nli_message \r\nFROM dvb_ic_tim \r\nWHERE mac = @mac AND nid = @nid\r\n";
					command.Parameters.AddWithValue("@mac", NpgsqlTypes.NpgsqlDbType.MacAddr, mac);
					command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)timNid.Value);
					NpgsqlDataReader dataReader = command.ExecuteReader();
					if (dataReader.Read())
					{
						if (!dataReader.IsDBNull(0))
						{
							newValue = dataReader.GetByteArray(0);
						}
					}
					dataReader.Close();
					command.Dispose();
					connection.Close();
				}
				_networkLayerInfoTims.Add(coordinate, newValue);
			}

			byte[] known = _networkLayerInfoTims[coordinate];
			byte[] candi = nlid.MessageBuffer;
			if (!AreArraysEqual(known,candi))
			{
				EnqueueTask(x => NetworkLayerInfoTimEx(x, coordinate, nlid));
				return true;
			}
			return false;
		}

		public bool TestForRcs2Nit(RcsNit nit)
		{
			throw new NotImplementedException();
		}

		public void InsertRcs2Nit(RcsNit nit)
		{
			throw new NotImplementedException();
		}

		public bool UpdateRcs2Tdt(ushort interactiveNetworkId, DateTime tdtTimestamp)
		{
			throw new NotImplementedException();
		}

		public bool TestForTmst2(ushort interactiveNetworkId, Tmst2.TransmissionMode mode)
		{
			throw new NotImplementedException();
		}

		public void InsertTmst2(ushort interactiveNetworkId, Tmst2.TransmissionMode mode)
		{
			throw new NotImplementedException();
		}

		public bool TestForTimFramePayloadFormat(ushort networkId, _0xb7_FramePayloadFormatDescriptor.TransmissionContext transmissionContext)
		{
			throw new NotImplementedException();
		}

		public void InsertTimFramePayloadFormat(ushort networkId, _0xb7_FramePayloadFormatDescriptor.TransmissionContext transmissionContext)
		{
			throw new NotImplementedException();
		}

		public bool TestForTimCorrectionMessageExtension(PhysicalAddress macAddress)
		{
			throw new NotImplementedException();
		}

		public void InsertTimCorrectionMessageExtension(PhysicalAddress macAddress,
			_0xb1_CorrectionMessageExtensionDescriptor descriptor)
		{
			throw new NotImplementedException();
		}

		public bool TestForTimControlAssignment(PhysicalAddress macAddress)
		{
			throw new NotImplementedException();
		}

		public void InsertTimControlAssignment(PhysicalAddress macAddress, _0xa4_SyncAssignDescriptor descriptor)
		{
			throw new NotImplementedException();
		}

		public bool TestForTimSatelliteReturnLink(PhysicalAddress macAddress)
		{
			throw new NotImplementedException();
		}

		public void InsertTimSatelliteReturnLink(PhysicalAddress macAddress, _0xa9_SatelliteReturnLinkDescriptor descriptor)
		{
			throw new NotImplementedException();
		}

		public bool TestForLowerLayerService(PhysicalAddress macAddress)
		{
			throw new NotImplementedException();
		}

		public void InsertTimLowerLayerService(PhysicalAddress macAddress, _0xbb_LowerLayerServiceDescriptor descriptor)
		{
			throw new NotImplementedException();
		}

		public bool TestForHigherLayerServiceInitalization(PhysicalAddress macAddress, _0xc4_HigherLayersInitializeDescriptor.Layer2Interface layer2Interface)
		{
			throw new NotImplementedException();
		}

		public void InsertHigherLayerServiceInitalization(PhysicalAddress macAddress, _0xc4_HigherLayersInitializeDescriptor.Layer2Interface layer2Interface)
		{
			throw new NotImplementedException();
		}

		public bool TestForTimLogonResponse(PhysicalAddress macAddress)
		{
			throw new NotImplementedException();
		}

		public void InsertTimLogonResponse(PhysicalAddress macAddress, _0xb9_LogonResponseDescriptor descriptor)
		{
			throw new NotImplementedException();
		}

		public bool TestForTimForwardInteractionPath(PhysicalAddress macAddress, _0xad_ForwardInteractionPathDescriptor.ForwardInteractionPath forwardInteractionPath)
		{
			throw new NotImplementedException();
		}

		public void InsertTimForwardInteractionPath(PhysicalAddress macAddress, _0xad_ForwardInteractionPathDescriptor.ForwardInteractionPath forwardInteractionPath)
		{
			throw new NotImplementedException();
		}

		private bool AreArraysEqual(byte[] l, byte[] r)
		{
			if (l.Length != r.Length)
				return false;

			for (int i = 0; i < l.Length; i++)
			{
				if (l[i] != r[i])
					return false;
			}
			return true;
		}

		private void NetworkLayerInfoTimEx(NpgsqlConnection x, Tuple<ushort, PhysicalAddress> coordinate, _0xa0_NetworkLayerInfoDescriptor nlid)
		{
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "UPDATE dvb_ic_tim\r\nSET nli_message = @message, version = version + 1, dateupdated = CURRENT_TIMESTAMP\r\nWHERE mac = @mac AND nid = @nid";
			command.Parameters.AddWithValue("@message", NpgsqlTypes.NpgsqlDbType.Bytea, nlid.MessageBuffer);
			command.Parameters.AddWithValue("@mac", NpgsqlTypes.NpgsqlDbType.MacAddr, coordinate.Item2);
			command.Parameters.AddWithValue("@nid", NpgsqlTypes.NpgsqlDbType.Integer, (int)coordinate.Item1);
			command.ExecuteNonQuery();
		}


	}
}
