using Npgsql;
using NpgsqlTypes;
using skyscraper8.DvbI;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Dvb.Descriptors;
using skyscraper8.DvbNip;
using skyscraper8.Skyscraper.Scraper.Storage;

namespace skyscraper5.Data.PostgreSql
{
	public partial class PostgresqlDataStore : DataStorage
	{

		public void InsertDvbiServiceListEntryPoint(long sourceHash)
		{
			EnqueueTask(x => InsertDvbiServiceListEntryPoint(x, sourceHash));
			_knownDvbiServiceListEntryPoints.Add(sourceHash);
		}

		private void InsertDvbiServiceListEntryPoint(NpgsqlConnection x, long sourceHash)
		{
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "INSERT INTO dvbi_service_list_entry_points VALUES (@shash,DEFAULT,NULL)";
			command.Parameters.AddWithValue("@shash", NpgsqlTypes.NpgsqlDbType.Bigint, sourceHash);
			int result = command.ExecuteNonQuery();
			if (result == -1)
				throw new NotImplementedException(result.ToString());
		}

		public bool TestForServiceListEntryPoints(long sourceHash)
		{
			throw new NotImplementedException();
		}

		public void InsertDvbiServiceList(DvbiServiceList serviceList)
		{
			EnqueueTask(x => InsertDvbiServiceListEx(x, serviceList));
			_knownDvbiServiceLists.Add(serviceList.Id);
		}

		private void InsertDvbiServiceListEx(NpgsqlConnection x, DvbiServiceList serviceList)
		{
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "INSERT INTO dvbi_service_list VALUES (@id,DEFAULT,@name,@dadelivery,@dvdelivery,@rmaterial,@uri) ON CONFLICT (id) DO NOTHING;";
			command.Parameters.AddWithValue("@id", NpgsqlDbType.Text, serviceList.Id);
			command.Parameters.AddWithValue("@name", NpgsqlDbType.Text, serviceList.Name);
			command.Parameters.AddWithValue("@dadelivery", NpgsqlDbType.Text, serviceList.DashDelivery);
			command.Parameters.AddWithValue("@dvdelivery", NpgsqlDbType.Text, serviceList.DvbSDelivery);
			command.Parameters.AddWithValue("@rmaterial", NpgsqlDbType.Text, serviceList.RelatedMaterial);
			command.Parameters.AddWithValue("@uri", NpgsqlDbType.Text, serviceList.URI);
			SetNulls(command);
			command.ExecuteNonQuery();
		}

		private HashSet<string> _knownDvbiServices;
		public bool TestForDvbiService(string id)
		{
			if (_knownDvbiServices == null)
				_knownDvbiServices = new HashSet<string>();

			bool result;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT dateadded FROM dvbi_services WHERE id = @id";
				command.Parameters.AddWithValue("@id", NpgsqlDbType.Text, id);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				if (result = dataReader.Read())
					_knownDvbiServices.Add(id);
				dataReader.Close();
				command.Dispose();
				connection.Close();
			}
			return result;
		}

		private HashSet<long> _knownDvbiServiceListEntryPoints;
		public bool TestForDvbiServiceListEntryPoints(long sourceHash)
		{
			if (_knownDvbiServiceListEntryPoints == null)
				_knownDvbiServiceListEntryPoints = new HashSet<long>();

			bool result;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT dateadded FROM dvbi_service_list_entry_points WHERE sourcehash = @shash";
				command.Parameters.AddWithValue("@shash", NpgsqlTypes.NpgsqlDbType.Bigint, sourceHash);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				if (result = dataReader.Read())
					_knownDvbiServiceListEntryPoints.Add(sourceHash);
				dataReader.Close();
				command.Dispose();
				connection.Close();
			}
			return result;
		}

		public void UpdateDvbiServiceListLastCheckedDate(string id, DateTime currentTime)
		{
			EnqueueTask(x => UpdateDvbiServiceListLastCheckedDateEx(x, id, currentTime));
			if (_knownDvbiServiceListUpdateDates == null)
				_knownDvbiServiceListUpdateDates = new Dictionary<string, DateTime>();

			_knownDvbiServiceListUpdateDates[id] = currentTime;
		}

		private void UpdateDvbiServiceListLastCheckedDateEx(NpgsqlConnection x, string id, DateTime currentTime)
		{
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "UPDATE dvbi_service_list SET last_updated = @lupdated WHERE id = @id";
			command.Parameters.AddWithValue("@lupdated", NpgsqlDbType.Timestamp, currentTime);
			command.Parameters.AddWithValue("@id", NpgsqlDbType.Text, id);
			command.ExecuteNonQuery();
		}

		public DateTime GetLastDvbiServiceListEntryPointUpdateDate(long sourceHash)
		{
			if (_knownDvbiServiceListEntryPointUpdateDates == null)
				_knownDvbiServiceListEntryPointUpdateDates = new Dictionary<long, DateTime>();

			if (_knownDvbiServiceListEntryPointUpdateDates.ContainsKey(sourceHash))
				return _knownDvbiServiceListEntryPointUpdateDates[sourceHash];

			DateTime result;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT dateupdated FROM dvbi_service_list_entry_points WHERE sourcehash = @shash";
				command.Parameters.AddWithValue("@shash", NpgsqlDbType.Bigint, sourceHash);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				if (dataReader.Read())
				{
					if (dataReader.IsDBNull(0))
					{
						result = DateTime.MinValue;
					}
					else
					{
						result = dataReader.GetDateTime(0);
					}
				}
				else
				{
					result = DateTime.MinValue;
				}
				dataReader.Close();
				command.Dispose();
				connection.Close();
			}
			_knownDvbiServiceListEntryPointUpdateDates[sourceHash] = result;
			return result;
		}

		public void AddDvbiServiceListToServiceListEntryPoint(DvbiServiceList serviceList, long sourceHash)
		{
			EnqueueTask(x => AddDvbiServiceListToServiceListEntryPointEx(x, serviceList, sourceHash));
		}

		private void AddDvbiServiceListToServiceListEntryPointEx(NpgsqlConnection x, DvbiServiceList serviceList, long sourceHash)
		{
			if (!TestForDvbiServiceList(serviceList.Id))
			{
				return;
			}
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "INSERT INTO dvbi_service_lists_to_entry_points VALUES (@slist,@epoint,DEFAULT)";
			command.Parameters.AddWithValue("@slist", NpgsqlDbType.Text, serviceList.Id);
			command.Parameters.AddWithValue("@epoint", NpgsqlDbType.Bigint, sourceHash);
			command.ExecuteNonQuery();
		}

		public void AddDvbiServiceToServiceList(string id, string serviceListId)
		{
			EnqueueTask(x => AddDvbiServiceToServiceList(x, id, serviceListId));
		}

		private void AddDvbiServiceToServiceList(NpgsqlConnection x, string id, string serviceListId)
		{
			NpgsqlCommand checkCommand = x.CreateCommand();
			checkCommand.CommandText = "SELECT dateadded FROM dvbi_services_to_service_lists WHERE service = @service AND service_list = @servicelist";
			checkCommand.Parameters.AddWithValue("@service", NpgsqlDbType.Text, id);
			checkCommand.Parameters.AddWithValue("@servicelist", NpgsqlDbType.Text, serviceListId);
			NpgsqlDataReader dataReader = checkCommand.ExecuteReader();
			bool alreadyKnown = dataReader.Read();
			dataReader.Close();
			checkCommand.Dispose();
			if (alreadyKnown)
				return;

			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "INSERT INTO dvbi_services_to_service_lists VALUES (@service,@servicelist,DEFAULT)";
			command.Parameters.AddWithValue("@service", NpgsqlDbType.Text, id);
			command.Parameters.AddWithValue("@servicelist", NpgsqlDbType.Text, serviceListId);
			command.ExecuteNonQuery();
		}

		private Dictionary<string, int> _knownDvbiServiceVersions;
		public int GetDvbiServiceVersion(string id)
		{
			if (_knownDvbiServiceVersions == null)
				_knownDvbiServiceVersions = new Dictionary<string, int>();

			if (_knownDvbiServiceVersions.ContainsKey(id))
				return _knownDvbiServiceVersions[id];

			int version;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT version FROM dvbi_services WHERE id = @id";
				command.Parameters.AddWithValue("@id", NpgsqlDbType.Text, id);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				dataReader.Read();
				version = dataReader.GetInt32(0);
				dataReader.Close();
				command.Dispose();
				connection.Close();
			}
			_knownDvbiServiceVersions[id] = version;
			return version;
		}

		public void UpdateDvbiService(DvbIService service)
		{
			EnqueueTask(x => UpdateDvbiServiceEx(x, service));
		}

		private void UpdateDvbiServiceEx(NpgsqlConnection connection, DvbIService service)
		{
			NpgsqlCommand command = connection.CreateCommand();
			command.CommandText = "update dvbi_services SET\r\nprovider_name = @provider_name, logo_url = @logo_url, onid = @onid, sid = @sid, tsid = @tsid, fec = @fec,\r\nfrequency = @frequency, modcod = @modcod, modulation_system = @modulation_system, modulation_type = @modulation_type,\r\norbital_position = @orbital_position, polarization = @polarization, roll_off = @roll_off, symbol_rate = @symbol_rate,\r\nsat_ip_query_parameters = @sat_ip_query_parameters, service_name = @service_name, service_type = @service_type, \r\nversion = @version, related_item = @related_item, country = @country, ranking = @ranking, region = @region, \r\nservice_description = @service_description\r\nWHERE id = @id";
			command.Parameters.AddWithValue("@id", NpgsqlDbType.Text, service.Id);
			command.Parameters.AddWithValue("@provider_name", NpgsqlDbType.Text, service.ProviderName);
			command.Parameters.AddWithValue("@logo_url", NpgsqlDbType.Text, service.LogoURL);
			command.Parameters.AddWithValue("@onid", NpgsqlDbType.Integer, (int)service.OriginalNetworkId);
			command.Parameters.AddWithValue("@sid", NpgsqlDbType.Integer, (int)service.ServiceId);
			command.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, (int)service.TransportStreamId);
			command.Parameters.AddWithValue("@fec", NpgsqlDbType.Integer, (int)service.FEC);
			command.Parameters.AddWithValue("@frequency", NpgsqlDbType.Text, service.Frequency);
			command.Parameters.AddWithValue("@modcod", NpgsqlDbType.Integer, (int)service.Modcod);
			command.Parameters.AddWithValue("@modulation_system", NpgsqlDbType.Integer, (int)service.ModulationSystem);
			command.Parameters.AddWithValue("@modulation_type", NpgsqlDbType.Integer, (int)service.ModulationType);
			command.Parameters.AddWithValue("@orbital_position", NpgsqlDbType.Double, service.OrbitalPosition);
			command.Parameters.AddWithValue("@polarization", NpgsqlDbType.Integer, (int)service.Polarization);
			command.Parameters.AddWithValue("@roll_off", NpgsqlDbType.Integer, (int)service.RollOff);
			command.Parameters.AddWithValue("@symbol_rate", NpgsqlDbType.Text, service.SymbolRate);
			command.Parameters.AddWithValue("@sat_ip_query_parameters", NpgsqlDbType.Text, service.SatIpQueryParameters);
			command.Parameters.AddWithValue("@service_name", NpgsqlDbType.Text, service.ServiceName);
			command.Parameters.AddWithValue("@service_type", NpgsqlDbType.Text, service.ServiceType);
			command.Parameters.AddWithValue("@version", NpgsqlDbType.Integer, service.Version);
			command.Parameters.AddWithValue("@related_item", NpgsqlDbType.Text, service.RelatedItem);
			command.Parameters.AddWithValue("@country", NpgsqlDbType.Text, service.Country);
			command.Parameters.AddWithValue("@ranking", NpgsqlDbType.Integer, service.Ranking);
			command.Parameters.AddWithValue("@region", NpgsqlDbType.Text, service.Region);
			command.Parameters.AddWithValue("@service_description", NpgsqlDbType.Text, service.ServiceDescription);
			SetNulls(command);
			command.ExecuteNonQuery();
		}

		private Dictionary<long, DateTime> _knownDvbiServiceListEntryPointUpdateDates;
		public void UpdateDvbiServiceListEntryPointUpdateDate(long hash, DateTime currentTime)
		{
			EnqueueTask(x => UpdateDvbiServiceListEntryPointUpdateDateEx(x, hash, currentTime));
			if (_knownDvbiServiceListEntryPointUpdateDates == null)
				_knownDvbiServiceListEntryPointUpdateDates = new Dictionary<long, DateTime>();

			_knownDvbiServiceListEntryPointUpdateDates[hash] = currentTime;
		}

		private void UpdateDvbiServiceListEntryPointUpdateDateEx(NpgsqlConnection x, long hash, DateTime currentTime)
		{
			DateTime currentTimeLocal = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, currentTime.Hour, currentTime.Minute, currentTime.Second, DateTimeKind.Local);
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "UPDATE dvbi_service_list_entry_points SET dateupdated = @dupdated WHERE sourcehash = @shash";
			command.Parameters.AddWithValue("@dupdated", NpgsqlDbType.Timestamp, currentTimeLocal);
			command.Parameters.AddWithValue("@shash",NpgsqlDbType.Bigint, hash);
			command.ExecuteNonQuery();
		}

		public void InsertDvbiService(DvbIService service)
		{
			EnqueueTask(x => InsertDvbiServiceEx(x, service));
			_knownDvbiServices.Add(service.Id);

			if (_knownDvbiServiceVersions == null)
				_knownDvbiServiceVersions = new Dictionary<string, int>();
			_knownDvbiServiceVersions[service.Id] = service.Version;
		}

		private void InsertDvbiServiceEx(NpgsqlConnection x, DvbIService service)
		{
			NpgsqlCommand command = x.CreateCommand();
			command.CommandText = "insert into dvbi_services VALUES " +
				"(@id, DEFAULT, @provider_name, @logo_url, @onid, @sid, @tsid, @fec, @frequency, @modcod, @modulation_system, @modulation_type, @orbital_position, @polarization, @roll_off, " +
				"@symbol_rate, @sat_ip_query_parameters, @service_name, @service_type, @version, @related_item, @country, @ranking, @region, @service_description) " +
				"ON CONFLICT (id) DO NOTHING;";
			command.Parameters.AddWithValue("@id", NpgsqlDbType.Text, service.Id);
			command.Parameters.AddWithValue("@provider_name", NpgsqlDbType.Text, service.ProviderName);
			command.Parameters.AddWithValue("@logo_url", NpgsqlDbType.Text, service.LogoURL);
			command.Parameters.AddWithValue("@onid", NpgsqlDbType.Integer, (int)service.OriginalNetworkId);
			command.Parameters.AddWithValue("@sid", NpgsqlDbType.Integer, (int)service.ServiceId);
			command.Parameters.AddWithValue("@tsid", NpgsqlDbType.Integer, (int)service.TransportStreamId);
			command.Parameters.AddWithValue("@fec", NpgsqlDbType.Integer, (int)service.FEC);
			command.Parameters.AddWithValue("@frequency", NpgsqlDbType.Text, service.Frequency);
			command.Parameters.AddWithValue("@modcod", NpgsqlDbType.Integer, (int)service.Modcod);
			command.Parameters.AddWithValue("@modulation_system", NpgsqlDbType.Integer, (int)service.ModulationSystem);
			command.Parameters.AddWithValue("@modulation_type", NpgsqlDbType.Integer, (int)service.ModulationType);
			command.Parameters.AddWithValue("@orbital_position", NpgsqlDbType.Double, service.OrbitalPosition);
			command.Parameters.AddWithValue("@polarization", NpgsqlDbType.Integer, (int)service.Polarization);
			command.Parameters.AddWithValue("@roll_off", NpgsqlDbType.Integer, (int)service.RollOff);
			command.Parameters.AddWithValue("@symbol_rate", NpgsqlDbType.Text, service.SymbolRate);
			command.Parameters.AddWithValue("@sat_ip_query_parameters", NpgsqlDbType.Text, service.SatIpQueryParameters);
			command.Parameters.AddWithValue("@service_name", NpgsqlDbType.Text, service.ServiceName);
			command.Parameters.AddWithValue("@service_type", NpgsqlDbType.Text, service.ServiceType);
			command.Parameters.AddWithValue("@version", NpgsqlDbType.Integer, service.Version);
			command.Parameters.AddWithValue("@related_item", NpgsqlDbType.Text, service.RelatedItem);
			command.Parameters.AddWithValue("@country", NpgsqlDbType.Text, service.Country);
			command.Parameters.AddWithValue("@ranking", NpgsqlDbType.Integer, service.Ranking);
			command.Parameters.AddWithValue("@region", NpgsqlDbType.Text, service.Region);
			command.Parameters.AddWithValue("@service_description", NpgsqlDbType.Text, service.ServiceDescription);
			SetNulls(command);
			command.ExecuteNonQuery();
		}

		private HashSet<string> _knownDvbiServiceLists;
		public bool TestForDvbiServiceList(string id)
		{
			if (_knownDvbiServiceLists == null)
				_knownDvbiServiceLists = new HashSet<string>();

			bool result;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT dateadded FROM dvbi_service_list WHERE id = @id";
				command.Parameters.AddWithValue("@id", NpgsqlTypes.NpgsqlDbType.Text, id);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				if (result = dataReader.Read())
					_knownDvbiServiceLists.Add(id);
				dataReader.Close();
				command.Dispose();
				connection.Close();
			}
			return result;
		}

		


		private Dictionary<string, DateTime> _knownDvbiServiceListUpdateDates;
		public DateTime GetDvbiServiceListLastUpdateDate(string id)
		{
			if (_knownDvbiServiceListUpdateDates == null)
				_knownDvbiServiceListUpdateDates = new Dictionary<string, DateTime>();

			if (_knownDvbiServiceListUpdateDates.ContainsKey(id))
				return _knownDvbiServiceListUpdateDates[id];

			DateTime result;
			using (NpgsqlConnection connection = new NpgsqlConnection(connectionStringBuilder.ToString()))
			{
				connection.Open();
				NpgsqlCommand command = connection.CreateCommand();
				command.CommandText = "SELECT last_updated FROM dvbi_service_list WHERE id = @id";
				command.Parameters.AddWithValue("@id", NpgsqlTypes.NpgsqlDbType.Text, id);
				NpgsqlDataReader dataReader = command.ExecuteReader();
				if (dataReader.Read())
				{
					if (dataReader.IsDBNull(0))
					{
						result = DateTime.MinValue;
					}
					else
					{
						result = dataReader.GetDateTime(0);
					}
				}
				else
				{
					result = DateTime.MinValue;
				}
				dataReader.Close();
				command.Dispose();
				connection.Close();
			}
			_knownDvbiServiceListUpdateDates[id] = result;
			return result;
		}
	}
}
