using Npgsql;
using skyscraper5.Skyscraper.Scraper.Storage.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace skyscraper5.Data.PostgreSql
{
    public partial class PostgresqlDataStore
    {
        public Dictionary<DatabaseKeyT2MI,DateTime> _knownT2MiTimestamps;

        public DateTime T2MiGetTimestamp(int currentNetworkId, int currentTransportStreamId, int pid)
        {
            if (_knownT2MiTimestamps == null)
                _knownT2MiTimestamps = new Dictionary<DatabaseKeyT2MI, DateTime>();

            DatabaseKeyT2MI key = new DatabaseKeyT2MI(currentNetworkId, currentTransportStreamId, pid);
            if (_knownT2MiTimestamps.ContainsKey(key))
                return _knownT2MiTimestamps[key];

            DateTime result;
            using (NpgsqlConnection conn = new NpgsqlConnection(connectionStringBuilder.ToString()))
            {
                conn.Open();
                NpgsqlCommand cmd = conn.CreateCommand();
                cmd.CommandText = "select t2mi_time\r\nfrom dvb_t2mi_timestamps\r\nWHERE cnid = @cnid\r\nAND ctsid = @ctsid\r\nAND pid = @pid";
                cmd.Parameters.AddWithValue("@cnid", NpgsqlTypes.NpgsqlDbType.Integer, currentNetworkId);
                cmd.Parameters.AddWithValue("@ctsid", NpgsqlTypes.NpgsqlDbType.Integer, currentTransportStreamId);
                cmd.Parameters.AddWithValue("@pid", NpgsqlTypes.NpgsqlDbType.Integer, pid);
                NpgsqlDataReader reader = cmd.ExecuteReader();
                if (reader.Read())
                {
                    result = reader.GetDateTime(0);
                    _knownT2MiTimestamps[key] = result;
                }
                else
                {
                    result = DateTime.MinValue;
                }
                reader.Close();
                cmd.Dispose();
                conn.Close();
            }
            return result;
        }

        public void T2MiSetTimestamp(int currentNetworkId, int currentTransportStreamId, int pid, DateTime resolveTime)
        {
            DatabaseKeyT2MI key = new DatabaseKeyT2MI(currentNetworkId, currentTransportStreamId, pid);
            if (_knownT2MiTimestamps.ContainsKey(key))
            {
                //already in db, so perform update
                EnqueueTask(x => UpdateT2MiTimestamp(x, currentNetworkId, currentTransportStreamId, pid, resolveTime));
            }
            else
            {
                //not yet in db, so perform insert
                EnqueueTask(x => InsertT2MiTimestamp(x, currentNetworkId, currentTransportStreamId, pid, resolveTime));
            }
            _knownT2MiTimestamps[key] = resolveTime;
            EnqueueTask(CommitTransaction);
        }

        private void UpdateT2MiTimestamp(NpgsqlConnection x, int currentNetworkId, int currentTransportStreamId, int pid, DateTime resolveTime)
        {
            NpgsqlCommand cmd = x.CreateCommand();
            cmd.CommandText = "UPDATE dvb_t2mi_timestamps " +
                "SET t2mi_time = @resolveTime, updated_counter = updated_counter + 1, updated_timestamp = current_timestamp " +
                "WHERE cnid = @cnid " +
                "AND ctsid = @ctsid " +
                "AND pid = @pid";
            cmd.Parameters.AddWithValue("@resolveTime", NpgsqlTypes.NpgsqlDbType.Timestamp, resolveTime);
            cmd.Parameters.AddWithValue("@cnid", NpgsqlTypes.NpgsqlDbType.Integer, currentNetworkId);
            cmd.Parameters.AddWithValue("@ctsid", NpgsqlTypes.NpgsqlDbType.Integer, currentTransportStreamId);
            cmd.Parameters.AddWithValue("@pid", NpgsqlTypes.NpgsqlDbType.Integer, pid);
            cmd.ExecuteNonQuery();
        }

        private void InsertT2MiTimestamp(NpgsqlConnection x, int currentNetworkId, int currentTransportStreamId, int pid, DateTime resolveTime)
        {
            NpgsqlCommand cmd = x.CreateCommand();
            cmd.CommandText = "insert into dvb_t2mi_timestamps (cnid, ctsid, pid, t2mi_time)\r\nvalues (@cnid,@ctsid,@pid,@t2mi_time)";
            cmd.Parameters.AddWithValue("@cnid", currentNetworkId);
            cmd.Parameters.AddWithValue("@ctsid", currentTransportStreamId);
            cmd.Parameters.AddWithValue("@pid", pid);
            cmd.Parameters.AddWithValue("@t2mi_time", resolveTime);
            cmd.ExecuteNonQuery();
        }

        private HashSet<DatabaseKeyT2MiTransmitter> _knownT2MiTransmitters;

        public bool T2MiTestForTransmitter(int? currentNetworkId, int? currentTransportStreamId, int relatedPid, ushort txIdentifier)
        {
            if (_knownT2MiTransmitters == null)
                _knownT2MiTransmitters = new HashSet<DatabaseKeyT2MiTransmitter>();

            DatabaseKeyT2MiTransmitter key = new DatabaseKeyT2MiTransmitter(currentNetworkId.Value, currentTransportStreamId.Value, relatedPid, txIdentifier);
            if (_knownT2MiTransmitters.Contains(key))
                return true;

            bool result;
            using (NpgsqlConnection conn = new NpgsqlConnection(connectionStringBuilder.ToString()))
            {
                conn.Open();
                NpgsqlCommand cmd = conn.CreateCommand();
                cmd.CommandText = "SELECT dateadded \r\nFROM dvb_t2mi_transmitters\r\nWHERE cnid = @cnid\r\nAND ctsid = @ctsid\r\nAND pid = @pid\r\nAND tx = @tx";
                cmd.Parameters.AddWithValue("@cnid", NpgsqlTypes.NpgsqlDbType.Integer, currentNetworkId.Value);
                cmd.Parameters.AddWithValue("@ctsid", NpgsqlTypes.NpgsqlDbType.Integer, currentTransportStreamId.Value);
                cmd.Parameters.AddWithValue("@pid", NpgsqlTypes.NpgsqlDbType.Integer, relatedPid);
                cmd.Parameters.AddWithValue("@tx", NpgsqlTypes.NpgsqlDbType.Integer, (int)txIdentifier);
                NpgsqlDataReader dataReader = cmd.ExecuteReader();
                result = dataReader.Read();
                dataReader.Close();
                cmd.Dispose();
                conn.Close();
            }
            if (result)
                _knownT2MiTransmitters.Add(key);
            return result;
        }

        public void T2MiRememberTransmitter(int? currentNetworkId, int? currentTransportStreamId, int relatedPid, ushort txIdentifier)
        {
            EnqueueTask(x => InsertT2MiTransmitter(x, currentNetworkId.Value, currentTransportStreamId.Value, relatedPid, (int)txIdentifier));
            DatabaseKeyT2MiTransmitter key = new DatabaseKeyT2MiTransmitter(currentNetworkId.Value, currentTransportStreamId.Value, relatedPid, txIdentifier);
            _knownT2MiTransmitters.Add(key);
        }

        private void InsertT2MiTransmitter(NpgsqlConnection x, int currentNetworkId, int currentTransportStreamId, int relatedPid, int txIdentifier)
        {
            NpgsqlCommand cmd = x.CreateCommand();
            cmd.CommandText = "INSERT INTO dvb_t2mi_transmitters (cnid, ctsid, pid, tx) VALUES (@cnid, @ctsid, @pid, @tx)";
            cmd.Parameters.AddWithValue("@cnid", NpgsqlTypes.NpgsqlDbType.Integer, currentNetworkId);
            cmd.Parameters.AddWithValue("@ctsid", NpgsqlTypes.NpgsqlDbType.Integer, currentTransportStreamId);
            cmd.Parameters.AddWithValue("@pid", NpgsqlTypes.NpgsqlDbType.Integer, relatedPid);
            cmd.Parameters.AddWithValue("@tx", NpgsqlTypes.NpgsqlDbType.Integer, (int)txIdentifier);
            cmd.ExecuteNonQuery();
        }

        private Dictionary<DatabaseKeyT2MiTransmitter, ushort> _knownT2MiTimeOffsets;
        public void T2MiSetTransmitterTimeOffset(int? currentNetworkId, int? currentTransportStreamId, int relatedPid, ushort txIdentifier, ushort timeOffset)
        {
            if (_knownT2MiTimeOffsets == null)
                _knownT2MiTimeOffsets = new Dictionary<DatabaseKeyT2MiTransmitter, ushort>();

            DatabaseKeyT2MiTransmitter key = new DatabaseKeyT2MiTransmitter(currentNetworkId.Value, currentTransportStreamId.Value, relatedPid, txIdentifier);
            if (_knownT2MiTimeOffsets.ContainsKey(key))
            {
                if (_knownT2MiTimeOffsets[key] == timeOffset)
                    return;
            }

            EnqueueTask(x => UpdateT2MiTransmitterTimeOffset(x, currentNetworkId.Value, currentTransportStreamId.Value, relatedPid, txIdentifier, timeOffset));
            _knownT2MiTimeOffsets[key] = timeOffset;
        }

        private void UpdateT2MiTransmitterTimeOffset(NpgsqlConnection x, int currentNetworkId, int currentTransportStreamId, int relatedPid, ushort txIdentifier, ushort timeOffset)
        {
            NpgsqlCommand cmd = x.CreateCommand();
            cmd.CommandText = "UPDATE dvb_t2mi_transmitters\r\n" +
                "SET timeoffset = @timeoffset, updated_counter = updated_counter + 1, updated_timestamp = CURRENT_TIMESTAMP\r\n" +
                "WHERE cnid = @cnid\r\n" +
                "AND ctsid = @ctsid\r\n" +
                "AND pid = @pid\r\n" +
                "AND tx = @tx";
            cmd.Parameters.AddWithValue("@cnid", NpgsqlTypes.NpgsqlDbType.Integer, currentNetworkId);
            cmd.Parameters.AddWithValue("@ctsid", NpgsqlTypes.NpgsqlDbType.Integer, currentTransportStreamId);
            cmd.Parameters.AddWithValue("@pid", NpgsqlTypes.NpgsqlDbType.Integer, relatedPid);
            cmd.Parameters.AddWithValue("@tx", NpgsqlTypes.NpgsqlDbType.Integer, (int)txIdentifier);
            cmd.Parameters.AddWithValue("@timeoffset", NpgsqlTypes.NpgsqlDbType.Integer, (int)timeOffset);
            cmd.ExecuteNonQuery();
        }
    }
}
