using System;
using System.IO;
using System.Runtime.InteropServices;

namespace skyscraper5.Skyscraper.IO.LibDvbV5
{
    public class LibDvb5FrontendParameters : IDisposable
    {
        internal LibDvb5FrontendParameters(uint adapter, uint frontend)
        {
            ptr = LibDvbV5.DvbFrontendOpen(adapter, frontend, 1, 0);
            wrapped = Marshal.PtrToStructure<FrontendParametersWrapped>(ptr);

            /*
            IntPtr satNumberOffset = Marshal.OffsetOf<FrontendParametersWrapped>("sat_number");
            IntPtr satNumberPtr = new IntPtr(ptr.ToInt64() + satNumberOffset.ToInt64());
            int readInt32 = Marshal.ReadInt32(ptr, satNumberOffset.ToInt32());
            Marshal.WriteInt32(ptr, satNumberOffset.ToInt32(), 2);
            wrapped = Marshal.PtrToStructure<FrontendParametersWrapped>(ptr);
            */
        }

        private IntPtr ptr;
        private FrontendParametersWrapped wrapped;

        internal struct FrontendParametersWrapped
        {
            public DvbFrontendInfo info;
            public uint version;
            public int has_v5_stats;
            public LibDvbV5FrontendDeliverySystem current_sys;
            public int num_system;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
            public LibDvbV5FrontendDeliverySystem[] system;
            public int legacy_fe;
            public int abort;
            public int lna;
            public IntPtr lnb;
            public int sat_number;
            public uint freq_bqf;
            public uint diseqc_wait;
            public uint verbose;
            public IntPtr logfunc;
            public IntPtr default_charset;
            public IntPtr output_charset;
        }

        public void Dispose()
        {
            LibDvbV5.DvbFrontendClose(ptr);
        }

        public void StoreParameter(uint cmd, uint value)
        {
            if (LibDvbV5.DvbFrontentStoreParameter(ptr, cmd, value) != 0)
            {
                throw new IOException("failed to store parameter");
            }
        }

        public void SetParameters()
        {
            if (LibDvbV5.DvbFrontendSetParameters(ptr) != 0)
            {
                throw new IOException("failed to set parameters");
            }
        }

        public void SetLnb(LibDvbV5SatLnb lnb)
        {
            IntPtr offset = Marshal.OffsetOf<FrontendParametersWrapped>("lnb");
            Marshal.WriteIntPtr(ptr, offset.ToInt32(), lnb.ptr);
            wrapped = Marshal.PtrToStructure<FrontendParametersWrapped>(ptr);
        }

        public void GetStatistics()
        {
            if (LibDvbV5.DvbFrontendGetStatistics(ptr) != 0)
            {
                throw new IOException("failed");
            }
        }

        public uint RetrieveStats(uint cmd)
        {
            uint value = UInt32.MaxValue;
            if (LibDvbV5.DvbFrontendRetrieveStats(ptr, cmd, ref value) != 0)
            {
                throw new IOException("invalid value");
            }

            return value;
        }

        public LibDvbV5FrontendDeliverySystem[] GetDeliverySystems()
        {
            LibDvbV5FrontendDeliverySystem[] result = new LibDvbV5FrontendDeliverySystem[wrapped.num_system];
            Array.Copy(wrapped.system, 0, result, 0, wrapped.num_system);
            return result;
        }

        public void SetDeliverySystem(LibDvbV5FrontendDeliverySystem deliverySystem)
        {
            if (LibDvbV5.DvbSetSystem(this.ptr, deliverySystem) != 0)
            {
                throw new ArgumentException("unsupported system");
            }
            wrapped = Marshal.PtrToStructure<FrontendParametersWrapped>(ptr);
        }
    }
}