using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.IO;
using skyscraper5.Skyscraper.IO.CrazycatStreamReader;
using skyscraper5.src.Skyscraper.IO.PortableExecutables;

namespace skyscraper5.IO.StreamReader
{
    public partial class LocalStreamReader : IStreamReader
    {
        private LocalStreamReader()
        {

        }

        private static LocalStreamReader _instance;

        public static LocalStreamReader GetInstance()
        {
            if (_instance == null)
            {
                _instance = new LocalStreamReader();
                _instance.dllInfo = _instance.GetDllInfo();
            }
            return _instance;
        }

        public bool CheckForDVB()
        {
            return UnsafeStreamReaderMethods.CheckForDVB();
        }

        public bool CheckForDVBEx(DvbEnumCallback func)
        {
            return UnsafeStreamReaderMethods.CheckForDVBEx(func);
        }

        public bool CheckForDVBExEx(DvbEnumCallbackEx func)
        {
            return UnsafeStreamReaderMethods.CheckForDVBExEx(func);
        }

        public bool StartDVB()
        {
            return UnsafeStreamReaderMethods.StartDVB();
        }

        public bool StartDvbEx(int index)
        {
            return UnsafeStreamReaderMethods.StartDvbEx(index);
        }

        public bool StopDVB()
        {
            return UnsafeStreamReaderMethods.StopDVB();
        }

        public bool GetTunerType(ref STD_TYPE type)
        {
            return UnsafeStreamReaderMethods.GetTunerType(ref type);
        }

        public bool SendDiSEqC(uint diseqcType, DiSEqC_Opcode data)
        {
            return UnsafeStreamReaderMethods.SendDiSEqC(diseqcType, data);
        }

        public bool SendDiseqCmd(byte[] buffer, int length)
        {
            return UnsafeStreamReaderMethods.SendDiseqCmd(buffer, length);
        }

        public bool SendDiseqCmdEx(IntPtr pCmd, int length, IntPtr reply, int replyLength)
        {
            return UnsafeStreamReaderMethods.SendDiseqCmdEx(pCmd, length, reply, replyLength);
        }

        public bool SetChannel(int freq, int symbrate, int pol, VITERBIRATE_TYPE fec, int lof1, int lof2, int lofsw)
        {
            try
            {
                return UnsafeStreamReaderMethods.SetChannel(freq, symbrate, pol, fec, lof1, lof2, lofsw);
            }
            catch (DivideByZeroException dvze)
            {
                Console.WriteLine("Mitigated Division-by-Zero bug in StreamReader.dll.");
                return false;
            }
        }

        public bool SetChannelEx(int freq, int symbrate, int pol, VITERBIRATE_TYPE fec, int lof1, int lof2, int lofsw, MOD_TYPE mod)
        {
            return UnsafeStreamReaderMethods.SetChannelEx(freq, symbrate, pol, fec, lof1, lof2, lofsw, mod);
        }

        public bool SetChannelExEx(int freq, int symbrate, int pol, VITERBIRATE_TYPE fec, int lof1, int lof2, int lofsw, MOD_TYPE mod, int inv, int pilot, ROLLOFF_TYPE rolloff)
        {
            return UnsafeStreamReaderMethods.SetChannelExEx(freq, symbrate, pol, fec, lof1, lof2, lofsw, mod, inv, pilot, rolloff);
        }

        public bool SetFilter(int pid, StdcallDvbCallback func, int callbackType, int size, ref IntPtr lpFilterNum)
        {
            return UnsafeStreamReaderMethods.SetFilter(pid, func, callbackType, size, ref lpFilterNum);
        }

        public bool SetFilterEx(int pid, StdcallDvbCallbackEx lpFunc, int callbackType, int size, ref IntPtr lpFilterNum)
        {
            return UnsafeStreamReaderMethods.SetFilterEx(pid, lpFunc, callbackType, size, ref lpFilterNum);
        }

        public bool SetBitFilter(int pid, IntPtr filterData, IntPtr filterMask, byte filterLength, StdcallDvbCallback lpFunc, ref IntPtr lpFilterNum)
        {
            return UnsafeStreamReaderMethods.SetBitFilter(pid, filterData, filterMask, filterLength, lpFunc, ref lpFilterNum);
        }

        public bool SetRemoteControl(int irtype, short devAddr, StdcallDvbCallback func, ref IntPtr lpFilterNum)
        {
            return UnsafeStreamReaderMethods.SetRemoteControl(irtype, devAddr, func, ref lpFilterNum);
        }

        public bool DelFilter(IntPtr filterNum)
        {
            return UnsafeStreamReaderMethods.DelFilter(filterNum);
        }

        public bool GetSignal(ref int pStrength, ref int pQuality)
        {
            return UnsafeStreamReaderMethods.GetSignal(ref pStrength, ref pQuality);
        }

        public bool GetSignalStrength(ref float pStrength, ref float pQuality)
        {
            return UnsafeStreamReaderMethods.GetSignalStrength(ref pStrength, ref pQuality);
        }

        public bool GetSignalEx(ref float pSNR, ref float pBER)
        {
            return UnsafeStreamReaderMethods.GetSignalEx(ref pSNR, ref pBER);
        }

        public bool GetSignalExEx(ref bool pPresent, ref bool pLock, ref int pRFLevel, ref float pSNR, ref float pBER)
        {
            return UnsafeStreamReaderMethods.GetSignalExEx(ref pPresent, ref pLock, ref pRFLevel, ref pSNR, ref pBER);
        }

        public bool Statistic(int[] pStat)
        {
            return UnsafeStreamReaderMethods.Statistic(pStat);
        }

        public bool GetMAC(byte[] pMac)
        {
            return UnsafeStreamReaderMethods.GetMAC(pMac);
        }

        public Caps GetCaps()
        {
            return UnsafeStreamReaderMethods.GetCaps();
        }

        public bool RFScan(int freq, int pol, int lof1, int lof2, int lofsw, out double pRFLevel)
        {
            return UnsafeStreamReaderMethods.RFScan(freq, pol, lof1, lof2, lofsw, out pRFLevel);
        }

        public bool FFTInit()
        {
            return UnsafeStreamReaderMethods.FFTInit();
        }

        public bool FFTScan(int freq, int pol, int lof1, int lof2, int lofsw, uint range, byte mode, byte nb_acc, IntPtr pTab, IntPtr pBegin, IntPtr pNum)
        {
            return UnsafeStreamReaderMethods.FFTScan(freq, pol, lof1, lof2, lofsw, range, mode, nb_acc, pTab, pBegin, pNum);
        }

        public bool BLScan(int freq, int freq_range, int pol, int lof1, int lof2, int lofsw, int minsr, ref SearchResult pSearchResult)
        {
            return UnsafeStreamReaderMethods.BLScan(freq, freq_range, pol, lof1, lof2, lofsw, minsr, ref pSearchResult);
        }

        public bool BLScanEx(int freq, int freq_range, int pol, int lof1, int lof2, int lofsw, int minsr, STD_TYPE std, ref SearchResult pSearchResult)
        {
            return UnsafeStreamReaderMethods.BLScanEx(freq, freq_range, pol, lof1, lof2, lofsw, minsr, std, ref pSearchResult);
        }

        public bool BLScan2(int freq_start, int freq_stop, int pol, int lof1, int lof2, int lofsw, IntPtr pSearchResult, ref int pTpNum, BlScanCallback lpFunc)
        {
            return UnsafeStreamReaderMethods.BLScan2(freq_start, freq_stop, pol, lof1, lof2, lofsw, pSearchResult, ref pTpNum, lpFunc);
        }

        public bool SignalInfo(ref SearchResult pSearchResult)
        {
            return UnsafeStreamReaderMethods.SignalInfo(ref pSearchResult);
        }

        public bool IQScan(uint input, sbyte[] pIQ, uint num)
        {
            return UnsafeStreamReaderMethods.IQScan(input, pIQ, num);
        }

        public bool IQScan2(uint point, short[] pIQ, uint num)
        {
            return UnsafeStreamReaderMethods.IQScan2(point, pIQ, num);
        }

        public bool IQScan2Range(byte input, ref ushort pMinPoint, ref ushort pMaxPoint)
        {
            return UnsafeStreamReaderMethods.IQScan2Range(input, ref pMinPoint, ref pMaxPoint);
        }

        public bool CIRScanRange(bool bHiRes, ref ushort pMinCnum, ref ushort pMaxCnum, ref int pMinDelayNs, ref int pMaxDelayNs)
        {
            return UnsafeStreamReaderMethods.CIRScanRange(bHiRes, ref pMinCnum, ref pMaxCnum, ref pMinDelayNs, ref pMaxDelayNs);
        }

        public bool CIRScan(bool bHiRes, int[] pPowers, int[] pDelays)
        {
            return UnsafeStreamReaderMethods.CIRScan(bHiRes, pPowers, pDelays);
        }

        public bool CarRange(ref ushort pMinCnum, ref ushort pMaxCnum)
        {
            return UnsafeStreamReaderMethods.CarRange(ref pMinCnum, ref pMaxCnum);
        }

        public bool CarEsNo(ref ushort cnum, ref double pEsNo)
        {
            return UnsafeStreamReaderMethods.CarEsNo(ref cnum, ref pEsNo);
        }

        public bool MISSel(bool bEnable, byte misFilter, byte misFilterMask)
        {
            return UnsafeStreamReaderMethods.MISSel(bEnable, misFilter, misFilterMask);
        }

        public bool PLSSel(byte plsMode, uint code)
        {
            return UnsafeStreamReaderMethods.PLSSel(plsMode, code);
        }

        public bool PLSGet(byte pMode, ref uint pCode)
        {
            return UnsafeStreamReaderMethods.PLSGet(pMode, ref pCode);
        }

        public bool ModSel(ref S2Mode ps2Modes, uint num)
        {
            return UnsafeStreamReaderMethods.ModSel(ref ps2Modes, num);
        }

        public bool ModInv(uint WaitMs, ref S2Mode pS2Modes, ref uint pNum)
        {
            return UnsafeStreamReaderMethods.ModInv(WaitMs, ref pS2Modes, ref pNum);
        }

        public bool SetChannel2(uint freq, uint bandwidth)
        {
            return UnsafeStreamReaderMethods.SetChannel2(freq, bandwidth);
        }

        public bool SetChannel2Ex(uint freq, uint bandwidth, STD_TYPE std, int stream)
        {
            return UnsafeStreamReaderMethods.SetChannel2Ex(freq, bandwidth, std, stream);
        }

        public bool SetChannel2ExEx(uint freq, uint bandwidth, uint symbrate, STD_TYPE std, int stream)
        {
            return UnsafeStreamReaderMethods.SetChannel2ExEx(freq, bandwidth, symbrate, std, stream);
        }

        public bool SignalInfo2(ref SearchResult2 si2)
        {
            return UnsafeStreamReaderMethods.SignalInfo2(ref si2);
        }

        public bool RFScan2(uint freq, STD_TYPE std, ref double pRFLevel)
        {
            return UnsafeStreamReaderMethods.RFScan2(freq, std, ref pRFLevel);
        }

        public bool AirScan(int freq_start, int freq_stop, uint step, uint bandwidth, int std, IntPtr pSearchResult, ref int pTpNum, AirScanCallback lpFunc)
        {
            return UnsafeStreamReaderMethods.AirScan(freq_start, freq_stop, step, bandwidth, std, pSearchResult, ref pTpNum, lpFunc);
        }

        public bool GetEEPROM(byte[] buffer, int offset, int len)
        {
            return UnsafeStreamReaderMethods.GetEEPROM(buffer, offset, len);
        }

        public bool SetEEPROM(byte[] buffer, int offset, int len)
        {
            return UnsafeStreamReaderMethods.SetEEPROM(buffer, offset, len);
        }

        public void Dispose()
        {
            if (!StopDVB())
            {
                throw new InvalidOperationException("failed to StopDVB");
            }
        }

        private Dictionary<string, string> dllInfo;

        private Dictionary<string,string> GetDllInfo()
        {
			FileInfo streamReaderDll = new FileInfo("StreamReader.dll");
			if (!streamReaderDll.Exists)
			{
				Console.WriteLine("Could not find StreamReader.dll");
                return null;
			}

			FileStream streamReaderDllStream = streamReaderDll.OpenRead();
			PeFile peFile = new PeFile(streamReaderDllStream);
			PeSectionStream rsrcStream = peFile.GetSectionStream(".rsrc");
			ImageResourceDirectory imageResourceDirectory = RsrcParser.ParseRsrc(rsrcStream);
			VsVersionInfo versionInfo = RsrcUtility.TryGetVersionInfo(imageResourceDirectory);
			dllInfo = versionInfo.ToDictionary();
			peFile.Dispose();

			if (!dllInfo.ContainsKey("ProductName"))
			{
				Console.WriteLine("Could not detect the ProductName in StreamReader.dll");
				return null;
			}
			if (!dllInfo.ContainsKey("ProductVersion"))
			{
				Console.WriteLine("Could not detect the Version of StreamReader.dll");
				return null;
			}
            return dllInfo;
		}
		public Version GetEngineVersion()
		{
			return Version.Parse(dllInfo["ProductVersion"]);
		}

		public string GetEngineName()
		{
            return dllInfo["ProductName"];
		}
	}
}
