using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Npgsql;
using NpgsqlTypes;
using skyscraper5.Dvb.Descriptors;
using skyscraper5.Dvb.Descriptors.Extension;
using skyscraper5.Dvb.Psi.Model;
using skyscraper5.Skyscraper.Scraper.Storage.Utilities;

namespace skyscraper5.Data.PostgreSql
{
	public partial class PostgresqlDataStore
	{
		private HashSet<DatabaseKeyNitNetwork> knownNitNetworks;
		public bool TestForNitNetwork(NitNetwork nitNetwork)
		{
			if (knownNitNetworks == null)
				knownNitNetworks = new HashSet<DatabaseKeyNitNetwork>();

			DatabaseKeyNitNetwork key = new DatabaseKeyNitNetwork(nitNetwork.NetworkId);
			if (knownNitNetworks.Contains(key))
				return true;

			bool result = false;
			using (NpgsqlConnection conn = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				conn.Open();
				NpgsqlCommand command = conn.CreateCommand();
				command.CommandText = "SELECT dateadded FROM dvb_nit WHERE id = @id";
				command.Parameters.AddParameter("@id", NpgsqlDbType.Integer, nitNetwork.NetworkId);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				result = dataReader.Read();
				dataReader.Close();
				dataReader.Dispose();
				command.Dispose();
				conn.Close();
			}

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

		public void StoreNitNetwork(NitNetwork nitNetwork)
		{
			if (_knownUpdatedNitNetworks == null)
				_knownUpdatedNitNetworks = new HashSet<DatabaseKeyNitNetwork>();

			DatabaseKeyNitNetwork key = new DatabaseKeyNitNetwork(nitNetwork.NetworkId);
			EnqueueTask(x => WriteNit(x, nitNetwork));
			knownNitNetworks.Add(key);
			_knownUpdatedNitNetworks.Add(key);
		}

		private void WriteNit(NpgsqlConnection connection, NitNetwork nitNetwork)
		{
			NpgsqlCommand command = connection.CreateCommand();
			command.CommandText =
				"INSERT INTO dvb_nit (id, name, private_data_specifier_id, xait_pid, region_name_country_code, region_name_language_code, region_country_code, min_polling_interval, uri, uri_linkage_type) " +
				"VALUES (@id, @name, @private_data_specifier_id, @xait_pid, @region_name_country_code, @region_name_language_code, @region_country_code, @min_polling_interval, @uri, @uri_linkage_type)";
			command.Parameters.AddParameter("@id", NpgsqlDbType.Integer, nitNetwork.NetworkId);
			string name = nitNetwork.Name;
			if (name != null)
				name = name.Replace("\0", "");
			command.Parameters.AddParameter("@name", NpgsqlDbType.Text, name);
			command.Parameters.AddParameter("@private_data_specifier_id", NpgsqlDbType.Bigint, (long?)nitNetwork.PrivateDataSpecifierId);
			command.Parameters.AddParameter("@xait_pid", NpgsqlDbType.Integer, (int?)nitNetwork.XaitPid);
			command.Parameters.AddParameter("@region_name_country_code", NpgsqlDbType.Varchar, nitNetwork.RegionCountryCode);
			command.Parameters.AddParameter("@region_name_language_code", NpgsqlDbType.Varchar, nitNetwork.RegionNameLanguageCode);
			command.Parameters.AddParameter("@region_country_code", NpgsqlDbType.Varchar, nitNetwork.RegionCountryCode);
			command.Parameters.AddParameter("@min_polling_interval", NpgsqlDbType.Integer, (int?)nitNetwork.MinPollingInterval);
			command.Parameters.AddParameter("@uri", NpgsqlDbType.Text, nitNetwork.Uri);
			command.Parameters.AddParameter("@uri_linkage_type", NpgsqlDbType.Integer, (int?)nitNetwork.UriLinkageType);
			command.ExecuteNonQuery();

			WriteNitCells(connection, nitNetwork);
			WriteNitLinkages(connection, nitNetwork);
			WriteNitMessage(connection, nitNetwork);
			WriteNitMultilingualNetworkNames(connection, nitNetwork);
			WriteNitRegionNames(connection, nitNetwork);
			WriteNitRegions(connection, nitNetwork);
			WriteNitServiceList(connection, nitNetwork);
		}

		private void WriteNitServiceList(NpgsqlConnection connection, NitNetwork nitNetwork)
		{
			if (nitNetwork.ServiceList == null)
				return;

			foreach (ServiceListDescriptor.Service service in nitNetwork.ServiceList)
			{
				throw new NotImplementedException();
			}
		}

		private void WriteNitRegions(NpgsqlConnection connection, NitNetwork nitNetwork)
		{
			if (nitNetwork.Regions == null) 
				return;

			foreach (TargetRegionDescriptor.TargetRegion region in nitNetwork.Regions)
			{
				throw new NotImplementedException();
			}
		}

		private void WriteNitRegionNames(NpgsqlConnection connection, NitNetwork nitNetwork)
		{
			if (nitNetwork.RegionNames == null)
				return;

			foreach (TargetRegionNameDescriptor.TargetRegionName name in nitNetwork.RegionNames)
			{
				throw new NotImplementedException();
			}
		}

		private void WriteNitMultilingualNetworkNames(NpgsqlConnection connection, NitNetwork nitNetwork)
		{
			if (nitNetwork.MultilingualNetworkName == null)
				return;

			NpgsqlCommand cmd = connection.CreateCommand();
			cmd.CommandText = "insert into dvb_nit_multilingual_network_names (id, k, v) values (@id,@k,@v)";
			cmd.Parameters.AddWithValue("@id", NpgsqlDbType.Integer, (int)nitNetwork.NetworkId);
			cmd.Parameters.Add("@k", NpgsqlDbType.Varchar);
            cmd.Parameters.Add("@v", NpgsqlDbType.Text);

            foreach (KeyValuePair<string, string> keyValuePair in nitNetwork.MultilingualNetworkName)
			{
				cmd.Parameters["@k"].Value = keyValuePair.Key;
                cmd.Parameters["@v"].Value = keyValuePair.Value;
				cmd.ExecuteNonQuery();
            }
		}

		private void WriteNitMessage(NpgsqlConnection connection, NitNetwork nitNetwork)
		{
			if (nitNetwork.Messages == null)
				return;

			foreach (MessageDescriptor message in nitNetwork.Messages)
			{
				throw new NotImplementedException();
			}
		}

		private void WriteNitLinkages(NpgsqlConnection connection, NitNetwork nitNetwork)
		{
			if (nitNetwork.Linkages == null)
				return;

			NpgsqlCommand command = connection.CreateCommand();
			command.CommandText =
				"insert into dvb_nit_linkages (id, ordinal, l_tsid, l_onid, l_sid, linkage_type, handover_type, handover_origin_type, handover_network_id, handover_initial_service_id, target_event_id, target_event_listed, target_event_simulcasted, table_type, private_data_bytes, bouquet_id) " +
				"values " +
				"(@id,@ordinal,@l_tsid,@l_onid,@l_sid,@linkage_type,@handover_type,@handover_origin_type,@handover_network_id,@handover_initial_service_id,@target_event_id,@target_event_listed,@target_event_simulcasted,@table_type,@private_data_bytes,@bouquet_id)";
			command.Parameters.AddParameter("@id", NpgsqlDbType.Integer, nitNetwork.NetworkId);
			command.Parameters.Add("@ordinal", NpgsqlDbType.Integer);
			command.Parameters.Add("@l_tsid", NpgsqlDbType.Integer);
			command.Parameters.Add("@l_onid", NpgsqlDbType.Integer);
			command.Parameters.Add("@l_sid", NpgsqlDbType.Integer);
			command.Parameters.Add("@linkage_type", NpgsqlDbType.Integer);
			command.Parameters.Add("@handover_type", NpgsqlDbType.Integer);
			command.Parameters.Add("@handover_origin_type", NpgsqlDbType.Boolean);
			command.Parameters.Add("@handover_network_id", NpgsqlDbType.Integer);
			command.Parameters.Add("@handover_initial_service_id", NpgsqlDbType.Integer);
			command.Parameters.Add("@target_event_id", NpgsqlDbType.Integer);
			command.Parameters.Add("@target_event_listed", NpgsqlDbType.Boolean);
			command.Parameters.Add("@target_event_simulcasted", NpgsqlDbType.Boolean);
			//@table_type,@private_data_bytes,@bouquet_id)";
			command.Parameters.Add("@table_type", NpgsqlDbType.Integer);
			command.Parameters.Add("@private_data_bytes", NpgsqlDbType.Bytea);
			command.Parameters.Add("@bouquet_id", NpgsqlDbType.Integer);

			for (int i = 0; i < nitNetwork.Linkages.Count; i++)
			{
				LinkageDescriptor linkage = nitNetwork.Linkages[i];
				command.Parameters["@id"].Value = (int)nitNetwork.NetworkId;
				command.Parameters["@ordinal"].Value = i;
				command.Parameters["@l_tsid"].Value = (int)linkage.TransportStreamId;
				command.Parameters["@l_onid"].Value = (int)linkage.OriginalNetworkId;
				command.Parameters["@l_sid"].Value = (int)linkage.ServiceId;
				command.Parameters["@linkage_type"].Value = (int)linkage.LinkageType;
				command.Parameters["@handover_type"].Value = linkage.HandoverType.HasValue ? linkage.HandoverType.Value : DBNull.Value;
				command.Parameters["@handover_origin_type"].Value = linkage.HandoverOriginType.HasValue ? linkage.HandoverOriginType.Value : DBNull.Value;
				command.Parameters["@handover_network_id"].Value = linkage.HandoverNetworkId.HasValue ? linkage.HandoverNetworkId.Value : DBNull.Value;
				command.Parameters["@handover_initial_service_id"].Value = linkage.HandoverInitialServiceId.HasValue ? linkage.HandoverInitialServiceId.Value : DBNull.Value;
				command.Parameters["@target_event_id"].Value = linkage.TargetEventId.HasValue ? linkage.TargetEventId.Value : DBNull.Value;
				command.Parameters["@target_event_listed"].Value = linkage.TargetEventListed.HasValue ? linkage.TargetEventListed.Value : DBNull.Value;
				command.Parameters["@target_event_simulcasted"].Value = linkage.TargetEventSimulcasted.HasValue ? linkage.TargetEventSimulcasted.Value : DBNull.Value;
				command.Parameters["@table_type"].Value = linkage.TableType.HasValue ? (int)linkage.TableType.Value : DBNull.Value;
				command.Parameters["@private_data_bytes"].Value = linkage.PrivateDataBytes != null ? linkage.PrivateDataBytes : DBNull.Value;
				command.Parameters["@bouquet_id"].Value = linkage.BouquetId.HasValue ? linkage.BouquetId : DBNull.Value;
				command.ExecuteNonQuery();

				WriteNitLinkageExtendedEventLinkages(connection, nitNetwork, linkage);
				WriteNitLinkageIpmacLinkages(connection, nitNetwork, i);
				WriteNitLinkageSsuLinkStructure(connection, nitNetwork, i);
			}
		}

		private void WriteNitLinkageSsuLinkStructure(NpgsqlConnection connection, NitNetwork nitNetwork, int ordinal)
		{
			LinkageDescriptor linkage = nitNetwork.Linkages[ordinal];
			if (linkage.SystemSoftwareUpdateLinkStructure == null)
				return;

			NpgsqlCommand command = connection.CreateCommand();
			command.CommandText =
				"INSERT INTO dvb_nit_linkages_ssu_link_structure (id, ordinal, subordinal, oui, selector) " +
				"VALUES (@id, @ordinal, @subordinal, @oui, @selector)";
			command.Parameters.AddWithValue("@id", NpgsqlDbType.Integer, (int)nitNetwork.NetworkId);
			command.Parameters.AddWithValue("@ordinal", NpgsqlDbType.Integer, ordinal);
			command.Parameters.Add("@subordinal", NpgsqlDbType.Integer);
			command.Parameters.Add("@oui", NpgsqlDbType.Varchar);
			command.Parameters.Add("@selector", NpgsqlDbType.Bytea);
			for (int i = 0; i < linkage.SystemSoftwareUpdateLinkStructure.Count; i++)
			{
				LinkageDescriptor.OuiPrivateData ssuLinkStructure = linkage.SystemSoftwareUpdateLinkStructure[i];
				command.Parameters["@subordinal"].Value = i;
				command.Parameters["@oui"].Value = BitConverter.ToString(ssuLinkStructure.OUI);
				command.Parameters["@selector"].Value = ssuLinkStructure.Selector;
				command.ExecuteNonQuery();
			}
		}

		private void WriteNitLinkageIpmacLinkages(NpgsqlConnection connection, NitNetwork nitNetwork, int ordinal)
		{
			LinkageDescriptor linkage = nitNetwork.Linkages[ordinal];
			if (linkage.IpMacNotificationLinkages == null)
				return;

			foreach (LinkageDescriptor.IpMacNotificationLinkage ipMacNotificationLinkage in linkage.IpMacNotificationLinkages)
			{
				throw new NotImplementedException();
			}
		}

		private void WriteNitLinkageExtendedEventLinkages(NpgsqlConnection connection, NitNetwork nitNetwork, LinkageDescriptor linkage)
		{
			if (linkage.ExtendedEventLinkages == null)
				return;

			for (int i = 0; i < linkage.ExtendedEventLinkages.Length; i++)
			{
				LinkageDescriptor.ExtendedEventLinkageInfo extendedEventLinkage = linkage.ExtendedEventLinkages[i];
				throw new NotImplementedException();
			}
		}

		private void WriteNitCells(NpgsqlConnection connection, NitNetwork nitNetwork)
		{
			if (nitNetwork.Cells == null)
				return;

			NpgsqlCommand npgsqlCommand = connection.CreateCommand();
			npgsqlCommand.CommandText = "insert into dvb_nit_cells (id, cell_id, cell_lat, cell_lon, extent_lat, extent_lon) values (@id,@cellid,@cellat,@celllon,@extlat,@extlon)";
			npgsqlCommand.Parameters.AddWithValue("@id", (int)nitNetwork.NetworkId);
			npgsqlCommand.Parameters.Add("@cellid", NpgsqlDbType.Integer);
			npgsqlCommand.Parameters.Add("@cellat", NpgsqlDbType.Integer);
			npgsqlCommand.Parameters.Add("@celllon", NpgsqlDbType.Integer);
			npgsqlCommand.Parameters.Add("@extlat", NpgsqlDbType.Bigint);
			npgsqlCommand.Parameters.Add("@extlon", NpgsqlDbType.Bigint);
			foreach (CellListDescriptor.Cell cell in nitNetwork.Cells)
			{
				npgsqlCommand.Parameters["@cellid"].Value = (int)cell.CellId;
				npgsqlCommand.Parameters["@cellat"].Value = (int)cell.CellLatitude;
				npgsqlCommand.Parameters["@celllon"].Value = (int)cell.CellLongitude;
				npgsqlCommand.Parameters["@extlat"].Value = (long)cell.ExtentOfLatitude;
				npgsqlCommand.Parameters["@extlon"].Value = (long)cell.ExtentOfLongitude;
				npgsqlCommand.ExecuteNonQuery();

				if (cell.Subcells != null)
				{
					if (cell.Subcells.Length > 0)
					{
						WriteNitSubcells(connection, nitNetwork.NetworkId, cell.CellId, cell.Subcells);
					}
				}
			}
		}

		private void WriteNitSubcells(NpgsqlConnection connection, int networkId, int cellId, CellListDescriptor.Subcell[] subcells)
		{
			NpgsqlCommand command = connection.CreateCommand();
			command.CommandText = "insert into dvb_nit_cells_subcells (id, cell_id, cell_id_extension, subcell_lat, subcell_lon, subcell_extent_lat, subcell_extent_lon) " +
				"values (@nid,@cellid,@cellex,@subcelllat,@subcellon,@sbucellextlat,@subcell_extent_lon)";
			command.Parameters.AddWithValue("@nid", networkId);
			command.Parameters.AddWithValue("@cellid", cellId);
			command.Parameters.Add("@cellex", NpgsqlDbType.Integer);
			command.Parameters.Add("@subcelllat", NpgsqlDbType.Integer);
			command.Parameters.Add("@subcellon", NpgsqlDbType.Integer);
			command.Parameters.Add("@sbucellextlat", NpgsqlDbType.Integer);
			command.Parameters.Add("@subcell_extent_lon", NpgsqlDbType.Integer);
			foreach(CellListDescriptor.Subcell subcell in subcells)
			{
				command.Parameters["@cellex"].Value = (int)subcell.CellIdExtension;
				command.Parameters["@subcelllat"].Value = (int)subcell.SubcellLatitude;
				command.Parameters["@subcellon"].Value = (int)subcell.SubcellLongitude;
				command.Parameters["@sbucellextlat"].Value = (int)subcell.CellIdExtension;
				command.Parameters["@subcell_extent_lon"].Value = (int)subcell.CellIdExtension;
				command.ExecuteNonQuery();
			}
		}

		private HashSet<DatabaseKeyNitTs> _knownNitTs;
		public bool TestForNitTransportStream(ushort networkId, NitTransportStream transportStream)
		{
			if (_knownNitTs == null)
				_knownNitTs = new HashSet<DatabaseKeyNitTs>();

			DatabaseKeyNitTs key = new DatabaseKeyNitTs(networkId, transportStream.TransportStreamId);
			if (_knownNitTs.Contains(key))
				return true;

			bool result = false;
			using (NpgsqlConnection conn = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				conn.Open();
				NpgsqlCommand command = conn.CreateCommand();
				command.CommandText =
					"SELECT dateadded FROM dvb_nit_transport_stream WHERE nid = @nid AND tsid = @tsid";
				command.Parameters.AddWithValue("@nid", NpgsqlDbType.Integer, (int)networkId);
				command.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, (int)transportStream.TransportStreamId);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				result = dataReader.Read();
				dataReader.Close();
				command.Dispose();
			}

			if (result)
				_knownNitTs.Add(key);

			return result;
		}

		public void StoreNitTransportStream(ushort networkId, NitTransportStream transportStream)
		{
			EnqueueTask(x => WriteNitTransportStream(x, networkId, transportStream));
			DatabaseKeyNitTs ts = new DatabaseKeyNitTs(networkId, transportStream.TransportStreamId);
			_knownNitTs.Add(ts);
			if (_knownUpdatedNitTransportStream == null)
				return;
			_knownUpdatedNitTransportStream.Add(ts);
		}

		private void WriteNitTransportStream(NpgsqlConnection conn, ushort networkId, NitTransportStream transportStream)
		{
			if (TestForNitTransportStream(networkId,transportStream))
			{
				return;
			}
			NpgsqlCommand command = conn.CreateCommand();
			command.CommandText =
				"insert into dvb_nit_transport_stream (nid, tsid, onid, east, fec_inner, frequency, orbital_position, polarization, roll_off, s2, symbol_rate, scrambling_sequence_index, input_stream_identifier, timeslice_number, ts_gs_mode, private_data_specifier_id, tfs_flag, bandwidth, guard_interval, other_frequency_flag, plp_id, siso_miso, t2_system_id, transmission_mode, coding_type, modulation_type, fec_outer, code_rate_hp_stream, code_rate_lp_stream, hierarchy_information, mpe_fec_indicator, priority, time_slicing_indicator, network_name, target_region_country_code) " +
				"values " +
				"(@nid,@tsid,@onid,@east,@fec_inner,@frequency,@orbital_position,@polarization,@roll_off,@s2,@symbol_rate,@scrambling_sequence_index,@input_stream_identifier,@timeslice_number,@ts_gs_mode,@private_data_specifier_id,\r\n                                      @tfs_flag, @bandwidth, @guard_interval, @other_frequency_flag, @plp_id, @siso_miso,\r\n                                      @t2_system_id, @transmission_mode, @coding_type, @modulation_type, @fec_outer,\r\n                                      @code_rate_hp_stream, @code_rate_lp_stream, @hierarchy_information,\r\n                                      @mpe_fec_indicator, @priority, @time_slicing_indicator, @network_name,\r\n                                      @target_region_country_code);";
			command.Parameters.AddParameter("@nid", NpgsqlDbType.Integer, (int)networkId);
			command.Parameters.AddParameter("@tsid", NpgsqlDbType.Integer, transportStream.TransportStreamId);
			command.Parameters.AddParameter("@onid", NpgsqlDbType.Integer, transportStream.OriginalNetworkId);
			command.Parameters.AddParameter("@east", NpgsqlDbType.Boolean, transportStream.East);
			command.Parameters.AddParameter("@fec_inner", NpgsqlDbType.Integer, (int?)transportStream.FecInner);
			command.Parameters.AddParameter("@frequency", NpgsqlDbType.Bigint, transportStream.Frequency);
			command.Parameters.AddParameter("@orbital_position", NpgsqlDbType.Real, transportStream.OrbitalPosition);
			command.Parameters.AddParameter("@polarization", NpgsqlDbType.Integer, (int?)transportStream.Polarization);
			command.Parameters.AddParameter("@roll_off", NpgsqlDbType.Real, transportStream.RollOff);
			command.Parameters.AddParameter("@s2", NpgsqlDbType.Boolean, transportStream.S2);
			command.Parameters.AddParameter("@symbol_rate", NpgsqlDbType.Bigint, transportStream.SymbolRate);
			command.Parameters.AddParameter("@scrambling_sequence_index", NpgsqlDbType.Integer, transportStream.ScramblingSequenceIndex);
			command.Parameters.AddParameter("@input_stream_identifier", NpgsqlDbType.Integer, transportStream.InputStreamIdentifier);
			command.Parameters.AddParameter("@timeslice_number", NpgsqlDbType.Integer, transportStream.TimesliceNumber);
			command.Parameters.AddParameter("@ts_gs_mode", NpgsqlDbType.Integer, (int?)transportStream.TsGsMode);
			//@private_data_specifier_id,@tfs_flag,@bandwidth,@guard_interval,@other_frequency_flag,@plp_id,@siso_miso,@t2_system_id,@transmission_mode,@coding_type,@modulation_type,@fec_outer,@code_rate_hp_stream,
			command.Parameters.AddParameter("@private_data_specifier_id", NpgsqlDbType.Bigint, (long?)transportStream.PrivateDataSpecifierId);
			command.Parameters.AddParameter("@tfs_flag", NpgsqlDbType.Boolean, transportStream.TfsFlag);
			command.Parameters.AddParameter("@bandwidth", NpgsqlDbType.Integer, transportStream.Bandwidth);
			command.Parameters.AddParameter("@guard_interval", NpgsqlDbType.Integer, transportStream.GuardInterval);
			command.Parameters.AddParameter("@other_frequency_flag", NpgsqlDbType.Boolean, transportStream.OtherFrequencyFlag);
			command.Parameters.AddParameter("@plp_id", NpgsqlDbType.Integer, (int?)transportStream.PlpId);
			command.Parameters.AddParameter("@siso_miso", NpgsqlDbType.Integer, transportStream.SisoMiso);
			command.Parameters.AddParameter("@t2_system_id", NpgsqlDbType.Integer, (int?)transportStream.T2SystemId);
			command.Parameters.AddParameter("@transmission_mode", NpgsqlDbType.Integer, transportStream.TransmissionMode);
			command.Parameters.AddParameter("@coding_type", NpgsqlDbType.Integer, (int?)transportStream.CodingType);
			command.Parameters.AddParameter("@modulation_type", NpgsqlDbType.Integer, transportStream.ModulationType);
			command.Parameters.AddParameter("@fec_outer", NpgsqlDbType.Integer, (int?)transportStream.FecOuter);
			command.Parameters.AddParameter("@code_rate_hp_stream", NpgsqlDbType.Integer, (int?)transportStream.CodeRateHpStream);
			//@code_rate_lp_stream,@hierarchy_information,@mpe_fec_indicator,@priority,@time_slicing_indicator,@network_name,@target_region_country_code)";
			command.Parameters.AddParameter("@code_rate_lp_stream", NpgsqlDbType.Integer, (int?)transportStream.CodeRateLpStream);
			command.Parameters.AddParameter("@hierarchy_information", NpgsqlDbType.Integer, (int?)transportStream.HierarchyInformation);
			command.Parameters.AddParameter("@mpe_fec_indicator", NpgsqlDbType.Boolean, transportStream.MpeFecIndicator);
			command.Parameters.AddParameter("@priority", NpgsqlDbType.Boolean, transportStream.Priority);
			command.Parameters.AddParameter("@time_slicing_indicator", NpgsqlDbType.Integer, (int?)transportStream.TimesliceNumber);
			command.Parameters.AddParameter("@network_name", NpgsqlDbType.Text, transportStream.NetworkName);
			command.Parameters.AddParameter("@target_region_country_code", NpgsqlDbType.Varchar, transportStream.TargetRegionCountryCode);
			command.Parameters.CheckNulls();
			command.ExecuteNonQuery();

			InsertNitTransportStreamCellFrequencies(conn, networkId, transportStream);
			InsertNitTransportStreamCellInfos(conn, networkId, transportStream);
			InsertNitTransportStreamCells(conn, networkId, transportStream);
			InsertNitTransportStreamCentreFrequencies(conn, networkId, transportStream);
			InsertNitTransportStreamLinkages(conn, networkId, transportStream);
			InsertNitTransportStreamServiceList(conn, networkId, transportStream);
			InsertNitTransportStreamTargetRegions(conn, networkId, transportStream);
		}

		private void InsertNitTransportStreamTargetRegions(NpgsqlConnection conn, ushort networkId, NitTransportStream transportStream)
		{
			if (transportStream.TargetRegions == null)
				return;

			foreach (TargetRegionDescriptor.TargetRegion targetRegion in transportStream.TargetRegions)
			{
				throw new NotImplementedException();
			}
		}

		private void InsertNitTransportStreamServiceList(NpgsqlConnection conn, ushort networkId, NitTransportStream transportStream)
		{
			if (transportStream.Services == null)
				return;
			if (transportStream.Services.Services == null)
				return;

			NpgsqlCommand command = conn.CreateCommand();
			command.CommandText = "INSERT INTO dvb_nit_transport_stream_service_list (tsid, nid, sid, service_type)" +
			                      "VALUES (@tsid, @nid, @sid, @service_type)";
			command.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, (int)transportStream.TransportStreamId);
			command.Parameters.AddWithValue("@nid", NpgsqlDbType.Integer, (int)networkId);
			command.Parameters.Add("@sid", NpgsqlDbType.Integer);
			command.Parameters.Add("@service_type", NpgsqlDbType.Integer);
			foreach (ServiceListDescriptor.Service service in transportStream.Services.Services)
			{
				command.Parameters["@sid"].Value = (int)service.ServiceId;
				command.Parameters["@service_type"].Value = (int)service.ServiceType;
				command.ExecuteNonQuery();
			}
		}

		private void InsertNitTransportStreamLinkages(NpgsqlConnection conn, ushort networkId, NitTransportStream transportStream)
		{
			if (transportStream.Linkages == null)
				return;

			for (int i = 0; i < transportStream.Linkages.Count; i++)
			{
				LinkageDescriptor linkage = transportStream.Linkages[i];
				throw new NotImplementedException();
			}
		}

		private void InsertNitTransportStreamCentreFrequencies(NpgsqlConnection conn, ushort networkId, NitTransportStream transportStream)
		{
			if (transportStream.CentreFrequencies == null)
				return;

			foreach (uint transportStreamCentreFrequency in transportStream.CentreFrequencies)
			{
				throw new NotImplementedException();
			}
		}

		private void InsertNitTransportStreamCells(NpgsqlConnection conn, ushort networkId, NitTransportStream transportStream)
		{
			if (transportStream.Cells == null)
				return;

			foreach (CellListDescriptor.Cell cell in transportStream.Cells)
			{
				throw new NotImplementedException();
			}
		}

		private void InsertNitTransportStreamCellInfos(NpgsqlConnection conn, ushort networkId, NitTransportStream transportStream)
		{
			if (transportStream.CellInfos == null)
				return;

            NpgsqlCommand command = conn.CreateCommand();
			command.CommandText = "insert into dvb_nit_transport_stream_cell_infos (onid, tsid, cell_id)\r\nvalues (@onid, @tsid, @cell_id)\r\n";
			command.Parameters.AddWithValue("@onid", NpgsqlDbType.Integer, (int)networkId);
			command.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, (int)transportStream.TransportStreamId);
			command.Parameters.Add("@cell_id", NpgsqlDbType.Integer);
            foreach (T2DeliverySystemDescriptor.CellInfo cellInfo in transportStream.CellInfos)
			{
				command.Parameters["@cell_id"].Value = (int)cellInfo.CellId;
				command.ExecuteNonQuery();

				InsertNitTransportStreamCellInfoSubcellInfos(conn, networkId, transportStream.TransportStreamId, cellInfo);
                InsertNitTransportStreamCellInfoCentreFrequencies(conn, networkId, transportStream.TransportStreamId, cellInfo);
            }
		}

        private void InsertNitTransportStreamCellInfoCentreFrequencies(NpgsqlConnection conn, ushort networkId, ushort transportStreamId, T2DeliverySystemDescriptor.CellInfo cellInfo)
        {
			if (cellInfo.CentreFrequencies == null)
				return;

            NpgsqlCommand cmd = conn.CreateCommand();
			cmd.CommandText = "insert into dvb_nit_transport_stream_cell_frequencies (nid, tsid, cell_id, frequency, ordinal)\r\nvalues (@nid,@tsid,@cell_id,@frequency,@ordinal)";
			cmd.Parameters.AddWithValue("@nid", NpgsqlDbType.Integer, (int)networkId);
			cmd.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, (int)transportStreamId);
			cmd.Parameters.AddWithValue("@cell_id", NpgsqlDbType.Integer, (int)cellInfo.CellId);
			cmd.Parameters.Add("@frequency", NpgsqlDbType.Bigint);
            cmd.Parameters.Add("@ordinal", NpgsqlDbType.Integer);
            for (int i = 0; i < cellInfo.CentreFrequencies.Length; i++)
			{
				cmd.Parameters["@frequency"].Value = (long)cellInfo.CentreFrequencies[i];
				cmd.Parameters["@ordinal"].Value = i;
				cmd.ExecuteNonQuery();
            }
        }

        private void InsertNitTransportStreamCellInfoSubcellInfos(NpgsqlConnection conn, ushort networkId, ushort transportStreamId, T2DeliverySystemDescriptor.CellInfo cellInfo)
		{
			if (cellInfo.SubcellInfos == null)
				return;

			foreach (T2DeliverySystemDescriptor.SubcellInfo m in cellInfo.SubcellInfos)
			{
				throw new NotImplementedException();
			}
		}

		private void InsertNitTransportStreamCellFrequencies(NpgsqlConnection conn, ushort networkId, NitTransportStream transportStream)
		{
			if (transportStream.CellFrequencies == null)
				return;

			foreach (CellFrequencyLinkDescriptor.Cell cell in transportStream.CellFrequencies)
			{
				throw new NotImplementedException();
			}
		}

		private HashSet<DatabaseKeyNitNetwork> _knownUpdatedNitNetworks;
		public bool UpdateNitNetwork(NitNetwork nitNetwork)
		{
			if (_knownUpdatedNitNetworks == null)
				_knownUpdatedNitNetworks = new HashSet<DatabaseKeyNitNetwork>();
			nitNetwork.Sanitize();
			DatabaseKeyNitNetwork key = new DatabaseKeyNitNetwork(nitNetwork.NetworkId);
			if (_knownUpdatedNitNetworks.Contains(key))
				return false;
			EnqueueTask(x => WriteUpdateNitNetwork(x, nitNetwork));
			_knownUpdatedNitNetworks.Add(key);
			return true;
		}

		private void WriteUpdateNitNetwork(NpgsqlConnection connection, NitNetwork newer)
		{
			NitNetwork older = SelectNitNetwork(connection, newer.NetworkId);
			if (!older.NeedsUpdate(newer))
				return;

			NpgsqlCommand command = connection.CreateCommand();
			command.CommandText =
				"update dvb_nit\r\nset name = @name,\r\n    private_data_specifier_id = @private_data_specifier_id,\r\n    xait_pid = @xait_pid,\r\n    region_name_country_code = @region_name_country_code,\r\n    region_name_language_code = @region_name_language_code,\r\n    region_country_code = @region_country_code,\r\n    min_polling_interval = @min_polling_interval,\r\n    uri = @uri,\r\n    uri_linkage_type = @uri_linkage_type,\r\n    updated_counter = updated_counter + 1,\r\n    updated_timestamp = CURRENT_TIMESTAMP\r\nwhere id = @id";
			command.Parameters.AddParameter("@id", NpgsqlDbType.Integer, newer.NetworkId);
			command.Parameters.AddParameter("@name", NpgsqlDbType.Text, newer.Name);
			command.Parameters.AddParameter("@private_data_specifier_id", NpgsqlDbType.Bigint, (long?)newer.PrivateDataSpecifierId);
			command.Parameters.AddParameter("@xait_pid", NpgsqlDbType.Integer, (int?)newer.XaitPid);
			command.Parameters.AddParameter("@region_name_country_code", NpgsqlDbType.Varchar, newer.RegionCountryCode);
			command.Parameters.AddParameter("@region_name_language_code", NpgsqlDbType.Varchar, newer.RegionNameLanguageCode);
			command.Parameters.AddParameter("@region_country_code", NpgsqlDbType.Varchar, newer.RegionCountryCode);
			command.Parameters.AddParameter("@min_polling_interval", NpgsqlDbType.Integer, (int?)newer.MinPollingInterval);
			command.Parameters.AddParameter("@uri", NpgsqlDbType.Text, newer.Uri);
			command.Parameters.AddParameter("@uri_linkage_type", NpgsqlDbType.Integer, (int?)newer.UriLinkageType);
			command.ExecuteNonQuery();
		}

		private NitNetwork SelectNitNetwork(NpgsqlConnection connection, int targetId)
		{
			NpgsqlCommand command = connection.CreateCommand();
			command.CommandText = "SELECT * FROM dvb_nit WHERE id = @targetId";
			command.Parameters.AddWithValue("@targetId", NpgsqlDbType.Integer, targetId);
			NpgsqlDataReader dataReader = command.ExecuteReader();
			NitNetwork result = null;
			if (dataReader.Read())
			{
				ushort id = (ushort)dataReader.GetInt32(0);
				DateTime dateadded = dataReader.GetDateTime(1);
				result = new NitNetwork(id);
				result.Name = dataReader.IsDBNull(2) ? null : dataReader.GetString(2);
				result.PrivateDataSpecifierId = dataReader.IsDBNull(3) ? null : (uint)dataReader.GetInt64(3);
				result.XaitPid = dataReader.IsDBNull(4) ? null : (ushort)dataReader.GetInt32(4);
				result.RegionNameCountryCode = dataReader.IsDBNull(5) ? null : dataReader.GetString(5);
				result.RegionNameLanguageCode = dataReader.IsDBNull(6) ? null : dataReader.GetString(6);
				result.RegionCountryCode = dataReader.IsDBNull(7) ? null : dataReader.GetString(7);
				result.MinPollingInterval = dataReader.IsDBNull(8) ? null : (ushort)dataReader.GetInt32(8);
				result.Uri = dataReader.IsDBNull(9) ? null : dataReader.GetString(9);
				result.UriLinkageType = dataReader.IsDBNull(10) ? null : (byte)dataReader.GetInt32(10);
			}
			dataReader.Close();
			command.Dispose();
			return result;
		}

		private HashSet<DatabaseKeyNitTs> _knownUpdatedNitTransportStream;
		public bool UpdateNitTransportStream(ushort networkId, NitTransportStream transportStream)
		{
			if (_knownUpdatedNitTransportStream == null)
				_knownUpdatedNitTransportStream = new HashSet<DatabaseKeyNitTs>();

			DatabaseKeyNitTs key = new DatabaseKeyNitTs(networkId, transportStream.TransportStreamId);
			if (_knownUpdatedNitTransportStream.Contains(key))
				return false;

			EnqueueTask(x => WriteNitUpdateTransportStream(x, networkId, transportStream));
			_knownUpdatedNitTransportStream.Add(key);
			return true;
		}

		private void WriteNitUpdateTransportStream(NpgsqlConnection connection, ushort networkId, NitTransportStream transportStream)
		{
			NitTransportStream older = SelectNitTransportStream(connection, networkId, transportStream.TransportStreamId);
			if (older == null)
				return;
			if (!older.NeedUpdate(transportStream))
				return;

			NpgsqlCommand command = connection.CreateCommand();
			command.CommandText =
				"update dvb_nit_transport_stream\r\nset onid = @onid,\r\n    east = @east,\r\n    fec_inner = @fec_inner,\r\n    frequency = @frequency,\r\n    orbital_position = @orbital_position,\r\n    polarization = @polarization,\r\n    roll_off = @roll_off,\r\n    s2 = @s2,\r\n    symbol_rate = @symbol_rate,\r\n    scrambling_sequence_index = @scrambling_sequence_index,\r\n    input_stream_identifier = @input_stream_identifier,\r\n    timeslice_number = @timeslice_number,\r\n    ts_gs_mode = @ts_gs_mode,\r\n    private_data_specifier_id = @private_data_specifier_id,\r\n    tfs_flag = @tfs_flag,\r\n    bandwidth = @bandwidth,\r\n    guard_interval = @guard_interval,\r\n    other_frequency_flag = @other_frequency_flag,\r\n    plp_id = @plp_id,\r\n    siso_miso = @siso_miso,\r\n    t2_system_id = @t2_system_id,\r\n    transmission_mode = @transmission_mode,\r\n    coding_type = @coding_type,\r\n    modulation_type = @modulation_type,\r\n    fec_outer = @fec_outer,\r\n    code_rate_hp_stream = @code_rate_hp_stream,\r\n    code_rate_lp_stream = @code_rate_lp_stream,\r\n    hierarchy_information = @hierarchy_information,\r\n    mpe_fec_indicator = @mpe_fec_indicator,\r\n    priority = @priority,\r\n    time_slicing_indicator = @time_slicing_indicator,\r\n    network_name = @network_name,\r\n    target_region_country_code = @target_region_country_code\r\nwhere nid = @nid \r\nand tsid = @tsid";
			command.Parameters.AddParameter("@nid", NpgsqlDbType.Integer, (int)networkId);
			command.Parameters.AddParameter("@tsid", NpgsqlDbType.Integer, transportStream.TransportStreamId);
			command.Parameters.AddParameter("@onid", NpgsqlDbType.Integer, transportStream.OriginalNetworkId);
			command.Parameters.AddParameter("@east", NpgsqlDbType.Boolean, transportStream.East);
			command.Parameters.AddParameter("@fec_inner", NpgsqlDbType.Integer, (int?)transportStream.FecInner);
			command.Parameters.AddParameter("@frequency", NpgsqlDbType.Bigint, transportStream.Frequency);
			command.Parameters.AddParameter("@orbital_position", NpgsqlDbType.Real, transportStream.OrbitalPosition);
			command.Parameters.AddParameter("@polarization", NpgsqlDbType.Integer, (int?)transportStream.Polarization);
			command.Parameters.AddParameter("@roll_off", NpgsqlDbType.Real, transportStream.RollOff);
			command.Parameters.AddParameter("@s2", NpgsqlDbType.Boolean, transportStream.S2);
			command.Parameters.AddParameter("@symbol_rate", NpgsqlDbType.Bigint, transportStream.SymbolRate);
			command.Parameters.AddParameter("@scrambling_sequence_index", NpgsqlDbType.Integer, transportStream.ScramblingSequenceIndex);
			command.Parameters.AddParameter("@input_stream_identifier", NpgsqlDbType.Integer, transportStream.InputStreamIdentifier);
			command.Parameters.AddParameter("@timeslice_number", NpgsqlDbType.Integer, transportStream.TimesliceNumber);
			command.Parameters.AddParameter("@ts_gs_mode", NpgsqlDbType.Integer, (int?)transportStream.TsGsMode);
			//@private_data_specifier_id,@tfs_flag,@bandwidth,@guard_interval,@other_frequency_flag,@plp_id,@siso_miso,@t2_system_id,@transmission_mode,@coding_type,@modulation_type,@fec_outer,@code_rate_hp_stream,
			command.Parameters.AddParameter("@private_data_specifier_id", NpgsqlDbType.Bigint, (long?)transportStream.PrivateDataSpecifierId);
			command.Parameters.AddParameter("@tfs_flag", NpgsqlDbType.Boolean, transportStream.TfsFlag);
			command.Parameters.AddParameter("@bandwidth", NpgsqlDbType.Integer, transportStream.Bandwidth);
			command.Parameters.AddParameter("@guard_interval", NpgsqlDbType.Integer, transportStream.GuardInterval);
			command.Parameters.AddParameter("@other_frequency_flag", NpgsqlDbType.Boolean, transportStream.OtherFrequencyFlag);
			command.Parameters.AddParameter("@plp_id", NpgsqlDbType.Integer, (int?)transportStream.PlpId);
			command.Parameters.AddParameter("@siso_miso", NpgsqlDbType.Integer, transportStream.SisoMiso);
			command.Parameters.AddParameter("@t2_system_id", NpgsqlDbType.Integer, (int?)transportStream.T2SystemId);
			command.Parameters.AddParameter("@transmission_mode", NpgsqlDbType.Integer, transportStream.TransmissionMode);
			command.Parameters.AddParameter("@coding_type", NpgsqlDbType.Integer, (int?)transportStream.CodingType);
			command.Parameters.AddParameter("@modulation_type", NpgsqlDbType.Integer, transportStream.ModulationType);
			command.Parameters.AddParameter("@fec_outer", NpgsqlDbType.Integer, (int?)transportStream.FecOuter);
			command.Parameters.AddParameter("@code_rate_hp_stream", NpgsqlDbType.Integer, (int?)transportStream.CodeRateHpStream);
			//@code_rate_lp_stream,@hierarchy_information,@mpe_fec_indicator,@priority,@time_slicing_indicator,@network_name,@target_region_country_code)";
			command.Parameters.AddParameter("@code_rate_lp_stream", NpgsqlDbType.Integer, (int?)transportStream.CodeRateLpStream);
			command.Parameters.AddParameter("@hierarchy_information", NpgsqlDbType.Integer, (int?)transportStream.HierarchyInformation);
			command.Parameters.AddParameter("@mpe_fec_indicator", NpgsqlDbType.Boolean, transportStream.MpeFecIndicator);
			command.Parameters.AddParameter("@priority", NpgsqlDbType.Boolean, transportStream.Priority);
			command.Parameters.AddParameter("@time_slicing_indicator", NpgsqlDbType.Integer, (int?)transportStream.TimesliceNumber);
			command.Parameters.AddParameter("@network_name", NpgsqlDbType.Text, transportStream.NetworkName);
			command.Parameters.AddParameter("@target_region_country_code", NpgsqlDbType.Varchar, transportStream.TargetRegionCountryCode);
			command.Parameters.CheckNulls();
			command.ExecuteNonQuery();

		}

		private NitTransportStream SelectNitTransportStream(NpgsqlConnection connection, ushort networkId, ushort transportStreamId)
		{
			NpgsqlCommand command = connection.CreateCommand();
			command.CommandText = "SELECT * FROM dvb_nit_transport_stream WHERE nid = @nid AND tsid = @tsid";
			command.Parameters.AddParameter("@nid", NpgsqlDbType.Integer, (int)networkId);
			command.Parameters.AddParameter("@tsid", NpgsqlDbType.Integer, (int)transportStreamId);
			NpgsqlDataReader dataReader = command.ExecuteReader();
			NitTransportStream result = null;
			if (dataReader.Read())
			{
				ushort nid = (ushort)dataReader.GetInt32(0);
				ushort tsid = (ushort)dataReader.GetInt32(1);
				DateTime dateadded = (DateTime)dataReader.GetDateTime(2);
				ushort onid = (ushort)dataReader.GetInt32(3);
				result = new NitTransportStream(onid, tsid);
				result.East = dataReader.IsDBNull(4) ? null : dataReader.GetBoolean(4);
				result.FecInner = dataReader.IsDBNull(5) ? null : (SatelliteDeliverySystemDescriptor.InnerFecScheme)dataReader.GetInt32(5);
				result.Frequency = dataReader.IsDBNull(6) ? null : dataReader.GetInt64(6);
				result.OrbitalPosition = dataReader.IsDBNull(7) ? null : dataReader.GetFloat(7);
				result.Polarization = dataReader.IsDBNull(8) ? null : (SatelliteDeliverySystemDescriptor.PolarizationEnum)dataReader.GetInt32(8);
				result.RollOff = dataReader.IsDBNull(9) ? null : dataReader.GetFloat(9);
				result.S2 = dataReader.IsDBNull(10) ? null : dataReader.GetBoolean(10);
				result.SymbolRate = dataReader.IsDBNull(11) ? null : dataReader.GetInt64(11);
				result.ScramblingSequenceIndex = dataReader.IsDBNull(12) ? null : dataReader.GetInt32(12);
				result.InputStreamIdentifier = dataReader.IsDBNull(13) ? null : (byte)dataReader.GetInt32(13);
				result.TimesliceNumber = dataReader.IsDBNull(14) ? null : (byte)dataReader.GetInt32(14);
				result.TsGsMode = dataReader.IsDBNull(15) ? null : (S2SatelliteDeliverySystemDescriptor.TsGsModeCoding)dataReader.GetInt32(15);
				result.PrivateDataSpecifierId = dataReader.IsDBNull(16) ? null : (uint)dataReader.GetInt64(16);
				result.TfsFlag = dataReader.IsDBNull(17) ? null : dataReader.GetBoolean(17);
				result.Bandwidth = dataReader.IsDBNull(18) ? null : dataReader.GetInt32(18);
				result.GuardInterval = dataReader.IsDBNull(19) ? null : dataReader.GetInt32(19);
				result.OtherFrequencyFlag = dataReader.IsDBNull(20) ? null : dataReader.GetBoolean(20);
				result.PlpId = dataReader.IsDBNull(21) ? null : dataReader.GetByte(21);
				result.SisoMiso = dataReader.IsDBNull(22) ? null : dataReader.GetInt32(22);
				result.T2SystemId = dataReader.IsDBNull(23) ? null : (ushort)dataReader.GetInt32(23);
				result.TransmissionMode = dataReader.IsDBNull(24) ? null : dataReader.GetInt32(24);
				result.CodingType = dataReader.IsDBNull(25) ? null : (FrequencyListDescriptor.CodingTypeValue)dataReader.GetInt32(25);
				result.ModulationType = dataReader.IsDBNull(26) ? null : dataReader.GetInt32(26);
				result.FecOuter = dataReader.IsDBNull(27) ? null : (CableDeliverySystemDescriptor.OuterFecScheme)dataReader.GetInt32(27);
				result.CodeRateHpStream = dataReader.IsDBNull(28) ? null : (TerristialDeliverySystemDescriptor.CodeRate)dataReader.GetInt32(28);
				result.CodeRateLpStream = dataReader.IsDBNull(29) ? null : (TerristialDeliverySystemDescriptor.CodeRate)dataReader.GetInt32(29);
				result.HierarchyInformation = dataReader.IsDBNull(30) ? null : (TerristialDeliverySystemDescriptor.HierarchySignallingFormat)dataReader.GetInt32(30);
				result.MpeFecIndicator = dataReader.IsDBNull(31) ? null : dataReader.GetBoolean(31);
				result.Priority = dataReader.IsDBNull(32) ? null : dataReader.GetBoolean(32);
				result.TimeSlicingIndicator = dataReader.IsDBNull(33) ? null : dataReader.GetInt32(33);
				result.NetworkName = dataReader.IsDBNull(34) ? null : dataReader.GetString(34);
				result.TargetRegionCountryCode = dataReader.IsDBNull(35) ? null : dataReader.GetString(35);
			}
			dataReader.Close();
			command.Dispose();
			return result;
		}
	}
}
