using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using skyscraper5.Dvb.Descriptors;
using skyscraper5.Dvb.Descriptors.Extension;
using skyscraper5.Skyscraper;

namespace skyscraper5.Dvb.Psi.Model
{
    public class NitTransportStream
    {
        public enum TransportMedium
        {
            Unknown,
            DVB_S,
            DVB_S2,
            DVB_T2,
            DVB_C
        }

        public NitTransportStream(ushort originalNetworkId, ushort transportStreamId)
        {
            OriginalNetworkId = originalNetworkId;
            TransportStreamId = transportStreamId;
            DeliveryMethod = TransportMedium.Unknown;
            Linkages = new List<LinkageDescriptor>();
            LogicalChannels = new List<BaseLcn>();
        }

        public TransportMedium DeliveryMethod { get; set; }
        public ushort OriginalNetworkId { get; }
        public ushort TransportStreamId { get; }
        public bool? East { get; set; }
        public SatelliteDeliverySystemDescriptor.InnerFecScheme? FecInner { get; set; }
        public long? Frequency { get; set; }

        public float? OrbitalPosition { get; set; }
        public SatelliteDeliverySystemDescriptor.PolarizationEnum? Polarization { get; set; }
        public float? RollOff { get; set; }
        public bool? S2 { get; set; }
        public long? SymbolRate { get; set; }
        
        //From 0x41 service list descriptor
        public ServiceListDescriptor Services { get; set; }

        //From 0x79 s2 satellite system descriptor
        public int? ScramblingSequenceIndex { get; set; }
        public byte? InputStreamIdentifier { get; set; }
        public byte? TimesliceNumber { get; set; }
        public S2SatelliteDeliverySystemDescriptor.TsGsModeCoding? TsGsMode { get; set; }

        //From 0x5F private data specifier descriptor
        public uint? PrivateDataSpecifierId { get; set; }

        //From 0x4A linkage descriptor
        public List<LinkageDescriptor> Linkages { get; internal set; }

        //From User Defined Descriptor
        public List<BaseLcn> LogicalChannels { get; private set; }

        //From extension 0x04 T2 Delivery System Descriptor
        public bool? TfsFlag { get; set; }
        public int? Bandwidth { get; set; }
        public ReadOnlyCollection<T2DeliverySystemDescriptor.CellInfo> CellInfos { get; set; }
        public int? GuardInterval { get; set; }
        public bool? OtherFrequencyFlag { get; set; }
        public byte? PlpId { get; set; }
        public int? SisoMiso { get; set; }
        public ushort? T2SystemId { get; set; }
        public int? TransmissionMode { get; set; }

        //From 0x62 Frequency list Descriptor
        public FrequencyListDescriptor.CodingTypeValue? CodingType { get; set; }
        public uint[] CentreFrequencies { get; set; }

        //From 0x6c Cell List Descriptor
        public ReadOnlyCollection<CellListDescriptor.Cell> Cells { get; set; }

        //From 0x44 cable delivery system descriptor
        public int? ModulationType { get; set; }

        //From 0x5a terristial delivery system descriptor
        public CableDeliverySystemDescriptor.OuterFecScheme? FecOuter { get; set; }
        public TerristialDeliverySystemDescriptor.CodeRate? CodeRateHpStream { get; set; }
        public TerristialDeliverySystemDescriptor.CodeRate? CodeRateLpStream { get; set; }
        public TerristialDeliverySystemDescriptor.HierarchySignallingFormat? HierarchyInformation { get; set; }
        public bool? MpeFecIndicator { get; set; }
        public bool? Priority { get; set; }
        public int? TimeSlicingIndicator { get; set; }

        //from network name descriptor
        public string NetworkName { get; set; }

        //from target region descriptor
        public IReadOnlyList<TargetRegionDescriptor.TargetRegion> TargetRegions { get; set; }
        public string TargetRegionCountryCode { get; set; }

        //from cell frequency list descriptor
        public List<CellFrequencyLinkDescriptor.Cell> CellFrequencies { get; set; }

        public double GetT2Frequency()
        {
            foreach (T2DeliverySystemDescriptor.CellInfo cellInfo in CellInfos)
            {
                foreach (uint cellInfoCentreFrequency in cellInfo.CentreFrequencies)
                {
                    return (double)cellInfoCentreFrequency / 100000.0;
                }
            }

            throw new DvbException("not dvb-t2");
        }

        public string RenderForUi()
        {
            switch (DeliveryMethod)
            {
                case TransportMedium.DVB_T2:
                    if (!Frequency.HasValue)
                        return String.Format("DVB-T2 Cell {0}", this.CellInfos[0].CellId);
                    return String.Format("DVB-T2: {0}/{1}", Frequency, SymbolRate);
                case TransportMedium.DVB_C:
                    return String.Format("DVB-C: {0}", Frequency);
                default:
                    throw new NotImplementedException(DeliveryMethod.ToString());
            }
        }

        public string RenderModulation()
        {
            switch (DeliveryMethod)
            {
                case TransportMedium.DVB_C:
                    switch (ModulationType)
                    {
                        case 0x01: return "16-QAM";
                        case 0x02: return "32-QAM";
                        case 0x03: return "64-QAM";
                        case 0x04: return "128-QAM";
                        case 0x05: return "256-QAM";
                        default: return "???";
                    }
                default:
                    throw new NotImplementedException(DeliveryMethod.ToString());
            }
        }

        protected bool Equals(NitTransportStream other)
        {
            return OriginalNetworkId == other.OriginalNetworkId && TransportStreamId == other.TransportStreamId;
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != this.GetType()) return false;
            return Equals((NitTransportStream)obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                return (OriginalNetworkId.GetHashCode() * 397) ^ TransportStreamId.GetHashCode();
            }
        }

        public bool NeedUpdate(NitTransportStream other)
        {
            if (other.East != this.East)
                return true;

            if (other.FecInner != this.FecInner)
                return true;

            if (other.Frequency != this.Frequency)
                return true;
            
            if (other.Polarization != this.Polarization)
                return true;
            
            if (other.S2 != this.S2)
                return true;

            if (other.SymbolRate != this.SymbolRate)
                return true;

            if (other.ScramblingSequenceIndex != this.ScramblingSequenceIndex)
                return true;

            if (other.InputStreamIdentifier != this.InputStreamIdentifier)
                return true;

            if (other.TimesliceNumber != this.TimesliceNumber)
                return true;

            if (other.TsGsMode != this.TsGsMode)
                return true;

            if (other.PrivateDataSpecifierId != this.PrivateDataSpecifierId)
                return true;

            if (other.TfsFlag != this.TfsFlag)
                return true;

            if (other.Bandwidth != this.Bandwidth)
                return true;

            if (other.GuardInterval != this.GuardInterval)
                return true;

            if (other.OtherFrequencyFlag != this.OtherFrequencyFlag)
                return true;

            if (other.PlpId != this.PlpId)
                return true;

            if (other.SisoMiso != this.SisoMiso)
                return true;

            if (other.T2SystemId != this.T2SystemId)
                return true;

            if (other.TransmissionMode != this.TransmissionMode)
                return true;

            if (other.CodingType != this.CodingType)
                return true;

            if (other.ModulationType != this.ModulationType)
                return true;

            if (other.FecOuter != this.FecOuter)
                return true;

            if (other.CodeRateHpStream != this.CodeRateHpStream)
                return true;

            if (other.CodeRateLpStream != this.CodeRateLpStream)
                return true;

            if (other.HierarchyInformation != this.HierarchyInformation)
                return true;

            if (other.MpeFecIndicator != this.MpeFecIndicator)
                return true;

            if (other.Priority != this.Priority)
                return true;

            if (other.TimeSlicingIndicator != this.TimeSlicingIndicator)
                return true;

            if (this.NetworkName == null && other.NetworkName != null)
                return true;

            if (this.NetworkName != null)
                if (!this.NetworkName.Equals(other.NetworkName))
                    return true;

            if (this.TargetRegionCountryCode == null && other.TargetRegionCountryCode != null)
                return true;

            if (this.TargetRegionCountryCode != null)
                if (!this.TargetRegionCountryCode.Equals(other.TargetRegionCountryCode))
                    return true;

            return false;
        }
    }
}
