using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Npgsql;
using NpgsqlTypes;
using skyscraper5.Mhp;
using skyscraper5.Mhp.Descriptors;
using skyscraper5.Mhp.Descriptors.InteractionTransportSelectors;
using skyscraper5.Mhp.Si;
using skyscraper5.Mhp.Si.Model;

namespace skyscraper5.Data.PostgreSql
{
	public partial class PostgresqlDataStore
	{
		private HashSet<ApplicationIdentifier> _knownAitApplications;
		public bool TestForAitApplication(ApplicationIdentifier aitApplicationApplicationIdentifier)
		{
			if (_knownAitApplications == null)
				_knownAitApplications = new HashSet<ApplicationIdentifier>();
			if (_knownAitApplications.Contains(aitApplicationApplicationIdentifier))
				return true;

			bool result;
			using (NpgsqlConnection conn = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				conn.Open();
				NpgsqlCommand command = conn.CreateCommand();
				command.CommandText = "SELECT dateadded FROM dvb_ait WHERE org_id = @org AND app_id = @app";
				command.Parameters.AddWithValue("@org",NpgsqlDbType.Bigint, (long)aitApplicationApplicationIdentifier.OrganisationId);
				command.Parameters.AddWithValue("@app",NpgsqlDbType.Integer,(int)aitApplicationApplicationIdentifier.ApplicationId);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				result = dataReader.Read();
				dataReader.Close();
				command.Dispose();
				conn.Close();
			}

			if (result)
				_knownAitApplications.Add(aitApplicationApplicationIdentifier);

			return result;
		}

		public void StoreAitApplication(AitApplication aitApplication)
		{
			EnqueueTask(x => WriteAitApplication(x, aitApplication));
			_knownAitApplications.Add(aitApplication.ApplicationIdentifier);
		}

		private void WriteAitApplication(NpgsqlConnection connection, AitApplication aitApplication)
		{
			NpgsqlCommand command = connection.CreateCommand();
			command.CommandText =
				"insert into dvb_ait (org_id, app_id, app_control_code, app_priority, service_bound_flag,\r\n                     transport_protocol_label, visibility, initial_path, app_usage_code, jvm_arguments,\r\n                     jvm_base_directory, jvm_class_path_extension, jvm_initial_class, version,\r\n                     is_launchable_from_older_version, launchable_completely_from_cache, not_launchable_from_broadcast,\r\n                     application_storage_priority, storage_property, auto_select, service_id, service_name,\r\n                     version_number, launch_order, storage_priorityy, ip_connection_required)" +
				"values " +
				"(@org_id,@app_id,@app_control_code,@app_priority,@service_bound_flag,@transport_protocol_label,@visibility,@initial_path,@app_usage_code,@jvm_arguments,@jvm_base_directory,@jvm_class_path_extension,@jvm_initial_class, @version,\r\n                     @is_launchable_from_older_version, @launchable_completely_from_cache, @not_launchable_from_broadcast,\r\n                     @application_storage_priority, @storage_property, @auto_select, @service_id, @service_name,\r\n                     @version_number, @launch_order, @storage_priorityy, @ip_connection_required);";
			command.Parameters.AddParameter("@org_id", NpgsqlDbType.Bigint, aitApplication.ApplicationIdentifier.OrganisationId);
			command.Parameters.AddParameter("@app_id", NpgsqlDbType.Integer, aitApplication.ApplicationIdentifier.ApplicationId);
			command.Parameters.AddParameter("@app_control_code", NpgsqlDbType.Integer, aitApplication.ApplicationControlCode);
			command.Parameters.AddParameter("@app_priority", NpgsqlDbType.Integer, aitApplication.ApplicationPriority);
			command.Parameters.AddParameter("@service_bound_flag", NpgsqlDbType.Boolean, aitApplication.ServiceBoundFlag);
			command.Parameters.AddParameter("@transport_protocol_label", NpgsqlDbType.Bytea, aitApplication.TransportProtocolLabel);
			command.Parameters.AddParameter("@visibility", NpgsqlDbType.Integer, aitApplication.Visibility);
			command.Parameters.AddParameter("@initial_path", NpgsqlDbType.Text, aitApplication.InitialPath);
			command.Parameters.AddParameter("@app_usage_code", NpgsqlDbType.Integer, aitApplication.ApplicationUsageCode);
			command.Parameters.AddParameter("@jvm_arguments", NpgsqlDbType.Text, aitApplication.ConcatJvmArguments());
			command.Parameters.AddParameter("@jvm_base_directory", NpgsqlDbType.Text, aitApplication.JvmBaseDirectory);
			command.Parameters.AddParameter("@jvm_class_path_extension", NpgsqlDbType.Text, aitApplication.JvmClassPathExtension);
			//@jvm_initial_class,@version,@is_launchable_from_older_version,@launchable_completely_from_cache,@not_launchable_from_broadcast,@application_storage_priority,@storage_property,@auto_select,@service_id,
			command.Parameters.AddParameter("@jvm_initial_class", NpgsqlDbType.Text, aitApplication.JvmInitialClass);
			command.Parameters.AddParameter("@version", NpgsqlDbType.Bigint, (long?)aitApplication.Version);
			command.Parameters.AddParameter("@is_launchable_from_older_version", NpgsqlDbType.Boolean, aitApplication.IsLaunchableFromOlderVersion);
			command.Parameters.AddParameter("@launchable_completely_from_cache", NpgsqlDbType.Boolean, aitApplication.LaunchableCompletelyFromCache);
			command.Parameters.AddParameter("@not_launchable_from_broadcast", NpgsqlDbType.Boolean, aitApplication.NotLaunchableFromBroadcast);
			command.Parameters.AddParameter("@application_storage_priority", NpgsqlDbType.Integer, (int?)aitApplication.ApplicationStoragePriority);
			command.Parameters.AddParameter("@storage_property", NpgsqlDbType.Integer, (int?)aitApplication.StorageProperty);
			command.Parameters.AddParameter("@auto_select", NpgsqlDbType.Boolean, aitApplication.AutoSelect);
			command.Parameters.AddParameter("@service_id", NpgsqlDbType.Bigint, (long?)aitApplication.ServiceId);
			//@service_name,@version_number,@launch_order,@storage_priorityy,@ip_connection_required);";
			command.Parameters.AddParameter("@service_name", NpgsqlDbType.Text, aitApplication.ServiceName);
			command.Parameters.AddParameter("@version_number", NpgsqlDbType.Bigint, (long?)aitApplication.VersionNumber);
			command.Parameters.AddParameter("@launch_order", NpgsqlDbType.Integer, (int?)aitApplication.LaunchOrder);
			command.Parameters.AddParameter("@storage_priorityy", NpgsqlDbType.Integer, (int?)aitApplication.StoragePriority);
			command.Parameters.AddParameter("@ip_connection_required", NpgsqlDbType.Boolean, aitApplication.IpConnectionRequired);
			command.ExecuteNonQuery();

			WriteAitApplicationNames(connection, aitApplication);
			WriteAitApplicationProfiles(connection, aitApplication);
			WriteAitBoundaryExtension(connection, aitApplication);
			WriteAitExtensionAuthorization(connection, aitApplication);
			WriteAitTransportProtocols(connection, aitApplication);
		}

		private void WriteAitTransportProtocols(NpgsqlConnection connection, AitApplication aitApplication)
		{
			if (aitApplication.TransportProtocols == null)
				return;

			NpgsqlCommand command = connection.CreateCommand();
			command.CommandText =
				"insert into dvb_ait_transport_protocols (org_id, app_id, protocol_id, transport_protocol_label,\r\n                                         oc_remote_connection, oc_onid, oc_tsid, oc_sid, oc_component_tag,\r\n                                         inter_url_base) " +
				"values (@org_id, @app_id, @protocol_id, @transport_protocol_label,@oc_remote_connection, @oc_onid, @oc_tsid, @oc_sid, @oc_component_tag,@inter_url_base)";
			command.Parameters.AddWithValue("@org_id", NpgsqlDbType.Bigint, (long)aitApplication.ApplicationIdentifier.OrganisationId);
			command.Parameters.AddWithValue("@app_id", NpgsqlDbType.Integer, (int)aitApplication.ApplicationIdentifier.ApplicationId);
			command.Parameters.Add("@protocol_id", NpgsqlDbType.Integer);
			command.Parameters.Add("@transport_protocol_label", NpgsqlDbType.Integer);
			command.Parameters.Add("@oc_remote_connection", NpgsqlDbType.Boolean);
			command.Parameters.Add("@oc_onid", NpgsqlDbType.Integer);
			command.Parameters.Add("@oc_tsid", NpgsqlDbType.Integer);
			command.Parameters.Add("@oc_sid", NpgsqlDbType.Integer);
			command.Parameters.Add("@oc_component_tag", NpgsqlDbType.Integer);
			command.Parameters.Add("@inter_url_base", NpgsqlDbType.Text);

			foreach (TransportProtocolDescriptor protocol in aitApplication.TransportProtocols)
			{
				command.Parameters["@protocol_id"].Value = (int)protocol.ProtocolId;
				command.Parameters["@transport_protocol_label"].Value = protocol.TransportProtocolLabel;
				switch (protocol.ProtocolId)
				{
					case 1:
						ObjectCarouselTransportSelector objectCarouselTransportSelector = (ObjectCarouselTransportSelector)protocol.Selector;
						command.Parameters["@oc_remote_connection"].Value = objectCarouselTransportSelector.RemoteConnection;
						command.Parameters["@oc_onid"].Value = (int?)objectCarouselTransportSelector.OriginalNetworkId;
						command.Parameters["@oc_tsid"].Value = (int?)objectCarouselTransportSelector.TransportStreamId;
						command.Parameters["@oc_sid"].Value = (int?)objectCarouselTransportSelector.ServiceId;
						command.Parameters["@oc_component_tag"].Value = (int)objectCarouselTransportSelector.ComponentTag;
						command.Parameters["@inter_url_base"].Value = DBNull.Value;
						break;
					case 3:
						InteractionTransportSelector interactionTransportSelector = (InteractionTransportSelector)protocol.Selector;
						command.Parameters["@oc_remote_connection"].Value = DBNull.Value;
						command.Parameters["@oc_onid"].Value = DBNull.Value;
						command.Parameters["@oc_tsid"].Value = DBNull.Value;
						command.Parameters["@oc_sid"].Value = DBNull.Value;
						command.Parameters["@oc_component_tag"].Value = DBNull.Value;
						command.Parameters["@inter_url_base"].Value = interactionTransportSelector.UrlBase;
						break;
					default:
						throw new NotImplementedException(String.Format("{0} {1}", nameof(protocol.ProtocolId), protocol.ProtocolId));
				}
				command.Parameters.CheckNulls();
				command.ExecuteNonQuery();

				if (protocol.ProtocolId == 3)
				{
					WriteAitTransportProtocolsUrlExtensions(connection, aitApplication, (InteractionTransportSelector)protocol.Selector);
				}
			}
		}

		private void WriteAitTransportProtocolsUrlExtensions(NpgsqlConnection connection, AitApplication aitApplication, InteractionTransportSelector protocolSelector)
		{
			if (protocolSelector.UrlExtensions == null)
				return;
			if (protocolSelector.UrlExtensions.Length == 0)
				return;

			NpgsqlCommand cmd = connection.CreateCommand();
			cmd.CommandText = "insert into dvb_ait_transport_protocols_url_extensions (org_id, app_id, protocol_id, extension) values (@orgId,@appId,@protocolId,@extension)";
			cmd.Parameters.AddWithValue("@orgId", NpgsqlDbType.Bigint, (long)aitApplication.ApplicationIdentifier.OrganisationId);
			cmd.Parameters.AddWithValue("@appId", NpgsqlDbType.Integer, (int)aitApplication.ApplicationIdentifier.ApplicationId);
			cmd.Parameters.AddWithValue("@protocolId", NpgsqlDbType.Integer, (int)protocolSelector.ProtocolId);
			cmd.Parameters.Add("@extension", NpgsqlDbType.Text);
			foreach (string extension in protocolSelector.UrlExtensions)
			{
				if (string.IsNullOrEmpty(extension))
					continue;
				cmd.Parameters["@extension"].Value = extension;
				cmd.ExecuteNonQuery();
			}
		}

		private void WriteAitExtensionAuthorization(NpgsqlConnection connection, AitApplication aitApplication)
		{
			if (aitApplication.ExternalAuthorizations == null)
				return;

			NpgsqlCommand cmd = connection.CreateCommand();
			cmd.CommandText = "insert into dvb_ait_external_authorization (src_org_id, src_app_id, tgt_org_id, tgt_app_id, app_priority)\r\n" +
				"values (@src_org_id, @src_app_id, @tgt_org_id, @tgt_app_id, @app_priority)";
			cmd.Parameters.AddWithValue("@src_org_id", NpgsqlDbType.Bigint, (long)aitApplication.ApplicationIdentifier.OrganisationId);
			cmd.Parameters.AddWithValue("@src_app_id", NpgsqlDbType.Integer, (int)aitApplication.ApplicationIdentifier.ApplicationId);
			cmd.Parameters.Add("@tgt_org_id", NpgsqlDbType.Bigint);
			cmd.Parameters.Add("@tgt_app_id", NpgsqlDbType.Integer);
			cmd.Parameters.Add("@app_priority", NpgsqlDbType.Integer);

            for (int i = 0; i < aitApplication.ExternalAuthorizations.Length; i++)
			{
				ExternalApplicationAuthorisationDescriptor.ExternalAuthorization externalAuthorization = aitApplication.ExternalAuthorizations[i];
				cmd.Parameters["@tgt_org_id"].Value = (long)externalAuthorization.OrganisationId;
				cmd.Parameters["@tgt_app_id"].Value = (int)externalAuthorization.ApplicationId;
				cmd.Parameters["@app_priority"].Value = (int)externalAuthorization.ApplicationPriority;
				cmd.ExecuteNonQuery();
			}
		}

		private void WriteAitBoundaryExtension(NpgsqlConnection connection, AitApplication aitApplication)
		{
			if (aitApplication.BoundaryExtensions == null)
				return;

			NpgsqlCommand command = connection.CreateCommand();
			command.CommandText = "insert into dvb_ait_boundary_extensions (org_id, app_id, extension)" +
			                      "values (@org_id, @app_id, @extension)";
			command.Parameters.AddWithValue("@org_id", NpgsqlDbType.Bigint, (long)aitApplication.ApplicationIdentifier.OrganisationId);
			command.Parameters.AddWithValue("@app_id", NpgsqlDbType.Integer, (int)aitApplication.ApplicationIdentifier.ApplicationId);
			command.Parameters.Add("@extension", NpgsqlDbType.Text);
			for (int i = 0; i < aitApplication.BoundaryExtensions.Length; i++)
			{
				command.Parameters["@extension"].Value = aitApplication.BoundaryExtensions[i];
				command.ExecuteNonQuery();
			}
		}

		private void WriteAitApplicationProfiles(NpgsqlConnection connection, AitApplication aitApplication)
		{
			if (aitApplication.ApplicationProfiles == null)
				return;

			NpgsqlCommand command = connection.CreateCommand();
			command.CommandText =
				"insert into dvb_ait_application_profiles (org_id, app_id, ordinal, app_profile, major, minor, micro) " +
				"values (@org_id, @app_id, @ordinal, @app_profile, @major, @minor, @micro)";
			command.Parameters.AddWithValue("@org_id", NpgsqlDbType.Bigint, (long)aitApplication.ApplicationIdentifier.OrganisationId);
			command.Parameters.AddWithValue("@app_id", NpgsqlDbType.Integer, (int)aitApplication.ApplicationIdentifier.ApplicationId);
			command.Parameters.Add("@ordinal", NpgsqlDbType.Integer);
			command.Parameters.Add("@app_profile", NpgsqlDbType.Integer);
			command.Parameters.Add("@major", NpgsqlDbType.Integer);
			command.Parameters.Add("@minor", NpgsqlDbType.Integer);
			command.Parameters.Add("@micro", NpgsqlDbType.Integer);
			for (int i = 0; i < aitApplication.ApplicationProfiles.Length; i++)
			{
				ApplicationDescriptor.ApplicationProfileEncoding applicationProfile = aitApplication.ApplicationProfiles[i];
				command.Parameters["@ordinal"].Value = i;
				command.Parameters["@app_profile"].Value = (int)applicationProfile.ApplicationProfile;
				command.Parameters["@major"].Value = applicationProfile.Major;
				command.Parameters["@minor"].Value = applicationProfile.Minor;
				command.Parameters["@micro"].Value = applicationProfile.Micro;
				command.ExecuteNonQuery();
			}
		}

		private void WriteAitApplicationNames(NpgsqlConnection connection, AitApplication aitApplication)
		{
			if (aitApplication.ApplicationName == null)
				return;

			NpgsqlCommand command = connection.CreateCommand();
			command.CommandText = "INSERT INTO dvb_ait_application_names (org_id, app_id, k, v)" +
			                      "VALUES (@org_id, @app_id, @k, @v)";
			command.Parameters.AddWithValue("@org_id", NpgsqlDbType.Bigint, (long)aitApplication.ApplicationIdentifier.OrganisationId);
			command.Parameters.AddWithValue("@app_id", NpgsqlDbType.Integer, (int)aitApplication.ApplicationIdentifier.ApplicationId);
			command.Parameters.Add("@k", NpgsqlDbType.Varchar);
			command.Parameters.Add("@v", NpgsqlDbType.Text);
			foreach (KeyValuePair<string, string> keyValuePair in aitApplication.ApplicationName)
			{
				command.Parameters["@k"].Value = keyValuePair.Key.Replace("\0", "");
				command.Parameters["@v"].Value = keyValuePair.Value;
				command.ExecuteNonQuery();
			}
		}

		
	}
}
