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

namespace skyscraper5.Mpeg2.Psi.Model
{
    public class ProgramMappingStream
    {
		public override string ToString()
        {
            return $"{nameof(StreamType)}: {StreamType}, {nameof(ElementaryPid)}: {ElementaryPid:X4}";
        }

        public PmtStreamType StreamType { get; }
        public int ElementaryPid { get; }

        //From 0x52_StreamIdentifierDescriptor
        public byte? ComponentTag { get; set; }

        //From 0x10_Iso639LanguageDescriptor
        public AudioType? AudioType { get; set; }
        public string Iso639LanguageCode { get; set; }
        
        //From 0x56_TeletextDescriptor
        public TeletextDescriptor.TeletextDescriptorTeletext[] Teletexts { get; set; }

        //From 0x56_ApplicationSignallingDescriptor
        public List<ApplicationSignallingDescriptor.ApplicationSignal> Applications { get; set; }

        //From 0x13_CarouselIdentifierDescriptor
        public byte? CarouselFormatId { get; set; }
        public uint? CarouselId { get; set; }

        //From 0x66_DataBroadcastIdDescriptor
        public ushort? DataBroadcastId { get; set; }
        public byte[] DataBroadcastSelector { get; set; }

        public DataBroadcastIdDescriptor.IpMacPlatform[] IpMacNotificationInfo { get; set; }

        //From Extension 0x06_SupplementaryAudioDescriptor
        public bool? SupplementaryIndependantStream { get; set; }
        public SupplemenatryAudioDescriptor.EditorialClassificationEnum? EditorialClassification { get; set; }
        public string SupplementaryLanguageCode { get; set; }
        
        //From 0x6A_Ac3Descriptor
        public byte? Asvc { get; set; }
        public int? BSID { get; set; }
        public Ac3Descriptor.ComponentTypeEnum? ComponentType { get; set; }
        public byte? MainId { get; set; }

        //From 0x02_VideoStreamDescriptor
        public double? FrameRate { get; set; }
        public bool? Mpeg1OnlyFlag { get; set; }
        public int? ChromaFormat { get; set; }
        public bool? ConstrainedParameterFlag { get; set; }
        public int? FrameRateExtensionFlag { get; set; }
        public bool? MultipleFramerateFlag { get; set; }
        public byte? ProfileAndLevelIndication { get; set; }
        public bool? StillPictureFlag { get; set; }
        
        //From 0x06 Data Stream Alignment Descriptor
        public byte? AlignmentType { get; set; }

        //From 0x0e Maximum Bitrate Descriptor
        public uint? MaximumBitrate { get; set; }

        //From 0x03 Audio Stream Descriptor
        public bool? AudioStreamId { get; set; }
        public bool? FreeFormat { get; set; }
        public int? Layer { get; set; }
        public bool? VariableRateAudio { get; set; }

        //From 0x28 Avc Video Descriptor
        public bool? ConstraintSet0Flag { get; set; }
        public bool? Avc24HourPictureFlag { get; set; }
        public int? AvcCompatibleFlags { get; set; }
        public bool? AvcStillPresent { get; set; }
        public bool? ConstraintSet1Flag { get; set; }
        public bool? ConstraintSet2Flag { get; set; }
        public bool? ConstraintSet3Flag { get; set; }
        public bool? ConstraintSet4Flag { get; set; }
        public bool? ConstraintSet5Flag { get; set; }
        public bool? FramePackingSeiNotPresent { get; set; }
        public byte? LevelIdc { get; set; }
        public byte? ProfileRdc { get; set; }

        //From 0x09 ca descriptor
        public int? CaPid { get; set; }
        public ushort? CaSystemId { get; set; }
        public byte[] CaPrivateData { get; set; }

        //From 0x5f private data specifier descriptor
        public uint? PrivateDataSpecifier { get; set; }

        //From 0x59 subtitling descriptor
        public SubtitlingDescriptor.Subtitling[] Subtitlings { get; set; }

        //From 0x05 registration descriptor
        public uint? FormatIdentifier { get; set; }
        public byte[] AdditionalIdentificationInfo { get; set; }

        //From 0x14 association tag descriptor
        public byte[] AssociationTagSelector { get; set; }
        public uint? AssociationTagTransactionId { get; set; }
        public ushort? AssociationTagUse { get; set; }
        public byte[] AssociationTagPrivateData { get; set; }
        public uint? AssociationTagTimeOut { get; set; }

        //From 0x45 vbi data descriptor
        public ReadOnlyCollection<VbiDataDescriptor.DataService> VbiData { get; set; }

        //From 0x7c aac descriptor
        public byte? AacType { get; set; }
        public byte[] AacAdditionalInfo { get; set; }
        public byte? AacProfileAndLevel { get; set; }
        public bool? SaocDeFlag { get; set; }

        //From 0x6b ancillary data descriptor
        public AncillaryDataDescriptor AncillaryDataDescriptor { get; set; }

        //From 0x7a enhanced ac3 descriptor
        public bool? MixInfoExists { get; set; }
        public byte? Substream1 { get; set; }
        public byte? Substream2 { get; set; }
        public byte? Substream3 { get; set; }

        //From 0x38 hevc video descriptor
        public long? Copied44bits { get; set; }
        public bool? FrameOnlyConstraintFlag { get; set; }
        public int? HdrWcgIdc { get; set; }
        public bool? Hevc24hrPicturePresentFlag { get; set; }
        public bool? HevcStillPresentFlag { get; set; }
        public bool? InterlacedSourceFlag { get; set; }
        public bool? NonPackedConstraintFlag { get; set; }
        public uint? ProfileCompatibilityIndication { get; set; }
        public int? ProfileIdc { get; set; }
        public int? ProfileSpace { get; set; }
        public bool? ProgressiveSourceFlag { get; set; }
        public bool? SubPicHrdParamsNotPresentFlag { get; set; }
        public int? TemporalIdMax { get; set; }
        public bool? TierFlag { get; set; }
        public int? TemporalIdMin { get; set; }

        //from extension descriptor 0x15 ac4 descriptor
        public int? Ac4ChannelMode { get; set; }
        public bool? Ac4DialogEnhancement { get; set; }
        public byte[] Ac4Dsi { get; set; }

        //from extension descriptor 0x19 audio preselection descriptor
        public AudioPreselectionDescriptor.AudioPreselection[] AudioPreselection { get; set; }

        //from 0x11 std descriptor
        public bool? LeakValidFlag { get; set; }

        //from 0x1f fmc descriptor
        public FmcDescriptor.Fmc[] FlexMuxChannels { get; set; }

        //from 0x10 smoothing buffer descriptor
        public uint? SbLeakRate { get; set; }
        public uint? SbSize { get; set; }

        //from 0x0f private data indicator descriptor
        public uint? PrivateDataIndicator { get; set; }

        //from mpeg-2 extension descriptor 0x03 hevc timing and hrd
        public bool? _90khzFlag { get; set; }
        public bool? PictureAndTimingInfoPresentFlag { get; set; }
        public bool? HdrManagementValidFlag { get; set; }
        public uint? K { get; set; }
        public uint? N { get; set; }
        public uint? NumUnitsInTick { get; set; }
        public int? TargetScheduleIdx { get; set; }
        public bool? TargetScheduleIdxNotPresentFlag { get; set; }

        //From 0x70 adaption field data descriptor
        public byte? AdaptionFieldDataIdentifier { get; set; }

        //From 0x2a avc timing and hrd descriptor
        public bool? FixedFrameRateFlag { get; set; }
        public bool? PictureToDisplayConversionFlag { get; set; }
        public bool? TemporalPocFlag { get; set; }

        //From 0x1c mpeg4 audio descriptor
        public byte? Mpeg4AudioProfileAndLevel { get; set; }

        //From 0x2b mpeg2 aac audio descriptor
        public byte? AacChannelConfiguration { get; set; }
        public byte? AacProfile { get; set; }
        public byte? AacAdditionalInformation { get; set; }

        //from 0x65 scrambling descriptor
        public byte? ScramblingMode { get; set; }

        //from 0x74 related content descriptor
        public bool? RelatedContentDescriptorPresent { get; set; }

        //from 0x46 vbi teletext descriptor
        public TeletextDescriptor.TeletextDescriptorTeletext[] VbiTeletexts { get; internal set; }

        //from extension 0x11, t2mi descriptor
        public int? NumT2MiStreams { get; internal set; }
        public int? T2MiStreamId { get; internal set; }
        public bool? PcrIscrCommonClockFlag { get; internal set; }

        //from 0x2e, MPEG-4 Audio Extension Descriptor
		public byte[] AudioSpecifConfig { get; internal set; }
		public byte[] AudioProfileLevelIndication { get; internal set; }
		public uint? MetadataApplicationFormatIdentifier { get; internal set; }
		public ushort? MetadataApplicationFormat { get; internal set; }
		public byte? MetadataFormat { get; internal set; }
		public uint? MetadataFormatIdentifier { get; internal set; }
		public byte? MetadataServiceId { get; internal set; }
		public int? DecoderConfigFlag { get; internal set; }
		public byte[] ServiceIdentificationRecord { get; internal set; }
		public byte[] DecoderConfig { get; internal set; }
		public byte[] DecoderConfigIdentificationRecord { get; internal set; }
		public ushort? DecoderConfigMetadataServiceId { get; internal set; }
		public byte[] Reserved { get; internal set; }
		public byte[] PrivateData { get; internal set; }

        //Voile tools
		public bool? Ac3DescriptorPresent { get; internal set; }
		public int NumDescriptors { get; internal set; }
		public bool? Ac3PlusDescriptorPresent { get; internal set; }

		public List<UserDefinedDescriptor> UnknownUserDefines;
		public ProgramMappingStream(PmtStreamType streamType, int elementaryPid)
        {
            StreamType = streamType;
            ElementaryPid = elementaryPid;
        }

        public bool IsUserPrivateStream()
        {
            return ((byte)StreamType) >= 0x80;
        }

        public bool LooksLikeRcs2Stream()
        {
			if (NumDescriptors != 1)
				return false;
			if (UnknownUserDefines == null)
				return false;
			if (UnknownUserDefines.Count != 1)
				return false;

			UserDefinedDescriptor descriptorCandidate = UnknownUserDefines[0];
			if (descriptorCandidate.DescriptorTag != 0xa7)
				return false;

			foreach(byte b in descriptorCandidate.Data)
			{
				switch(b)
				{
					case 0x40:
					case 0x41:
					case 0x4d:
					case 0x70:
					case 0xa0:
					case 0xa1:
					case 0xa2:
					case 0xa3:
					case 0xa4:
					case 0xa5:
					case 0xa6:
					case 0xaa:
                    case 0xab:
                    case 0xac:
                    case 0xad:
                    case 0xae:
                    case 0xaf:
                    case 0xb0:
                    case 0xb1:
                    case 0xb2:
						continue;
                    case 0xe4:
                    case 0xc0:
                        //User defined, but should be okay.
                        continue;
                    default:
                        return false;
				}
			}
            return true;
        }
    }
}
