using System;
using System.Runtime.InteropServices;

namespace skyscraper5.Skyscraper.IO.LibDvbV5
{
    public class LibDvbV5DeviceList
    {
        public LibDvbV5DeviceList(IntPtr ptr)
        {
            StructDevList ptrToStructure = Marshal.PtrToStructure<StructDevList>(ptr);
            pointer = ptr;
            SizeOf = Marshal.SizeOf(ptrToStructure);
            SystemPath = Marshal.PtrToStringAuto(ptrToStructure.syspath);
            Path = Marshal.PtrToStringAuto(ptrToStructure.path);
            SystemName = Marshal.PtrToStringAuto(ptrToStructure.sysname);
            DvbType = ptrToStructure.dvbType;
            BusAddress = Marshal.PtrToStringAuto(ptrToStructure.busAddr);
            BusId = Marshal.PtrToStringAuto(ptrToStructure.busId);
            Manufacturer = Marshal.PtrToStringAuto(ptrToStructure.manufacturer);
            Product = Marshal.PtrToStringAuto(ptrToStructure.product);
            Serial = Marshal.PtrToStringAuto(ptrToStructure.serial);
        }

        public string? Serial { get; }

        public string? Product { get; }

        public string? Manufacturer { get; }

        public string? BusId { get; }

        public string? BusAddress { get; }

        public string? SystemName { get; }

        public string? Path { get; }

        public string? SystemPath { get; }


        private IntPtr pointer;
        public int SizeOf { get; }
        public DvbDeviceType DvbType { get; }

        struct StructDevList
        {
            public IntPtr syspath;
            public IntPtr path;
            public IntPtr sysname;
            public DvbDeviceType dvbType;
            public IntPtr busAddr;
            public IntPtr busId;
            public IntPtr manufacturer;
            public IntPtr product;
            public IntPtr serial;
        }

        public override string ToString()
        {
            return String.Format("{0} {1} {2} ({3:X16})",Manufacturer,Product,DvbType,pointer.ToInt64());
        }

        protected bool Equals(LibDvbV5DeviceList other)
        {
            return pointer.Equals(other.pointer);
        }

        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((LibDvbV5DeviceList)obj);
        }

        public override int GetHashCode()
        {
            return pointer.GetHashCode();
        }


        public uint? GuessAdapterNo()
        {
            string[] strings = SystemName.Split('.');
            if (!strings[0].StartsWith("dvb"))
                return null;

            return extractNumber(strings[0]);
        }

        public uint? GuessDeviceNo()
        {
            string[] strings = SystemName.Split('.');
            if (!strings[0].StartsWith("dvb"))
                return null;

            return extractNumber(strings[1]);
        }

        private static uint extractNumber(string str)
        {
            int magnitude = 1;
            uint result = 0;
            for (int i = 0; i < str.Length; i++)
            {
                if (Char.IsDigit(str[i]))
                {
                    int addMe = str[i] - 0x30;
                    addMe *= magnitude;
                    magnitude *= 10;
                    result += (uint)addMe;
                }
            }

            return result;
        }

        public LibDvb5FrontendParameters ToFrontendParameters()
        {
            if (this.DvbType != DvbDeviceType.Frontend)
                throw new InvalidOperationException("i'm not a frontend");

            uint? adapterNo = GuessAdapterNo();
            uint? deviceNo = GuessDeviceNo();

            if (!adapterNo.HasValue || !deviceNo.HasValue)
                return null;

            return new LibDvb5FrontendParameters(adapterNo.Value, deviceNo.Value);
        }
    }
}