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

namespace skyscraper8.Skyscraper.FrequencyListGenerator
{
	public class CoopBlindscanStreamReaderProxy : IStreamReader
	{
		private static ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);

		private readonly IStreamReader _proxiedStreamReader;
		private readonly int _blindScanTuner;
		private readonly int _setFilterTuner;

		private int currentlySelectedTuner;
		private uint lastDiseqcType;
		private DiSEqC_Opcode lastDiseqcOpcode;
		public CoopBlindscanStreamReaderProxy(IStreamReader proxiedStreamReader, int blindScanTuner, int setFilterTuner)
		{
			_proxiedStreamReader = proxiedStreamReader;
			_blindScanTuner = blindScanTuner;
			_setFilterTuner = setFilterTuner;
			currentlySelectedTuner = -1;
			lastDiseqcType = UInt32.MaxValue;

			if (logger == null)
			{
				logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
				logger.Debug("We'll be using two tuners for this operation.");
			}
		}

		private bool SwitchTuner(int targetTuner)
		{
			if (targetTuner == currentlySelectedTuner)
				return true;

			if (logger == null)
				logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
			logger.Info(String.Format("Switching tuner to #{0}", targetTuner));
			if (currentlySelectedTuner != -1)
			{
				if (!_proxiedStreamReader.StopDVB())
					return false;
			}

			if (!_proxiedStreamReader.StartDvbEx(targetTuner))
				return false;

			/*if (lastDiseqcType != uint.MaxValue)
			{
				if (!_proxiedStreamReader.SendDiSEqC(lastDiseqcType, lastDiseqcOpcode))
					return false;
            }*/

			if (lastDiseqcOpcode.HasFlag(DiSEqC_Opcode.DISEQC_HORIZONTAL))
			{
                if (!_proxiedStreamReader.StopDVB())
                    return false;
                if (!_proxiedStreamReader.StartDvbEx(targetTuner))
                    return false;
            }

			currentlySelectedTuner = targetTuner;
			return true;
		}

		public void Dispose()
		{
			throw new NotImplementedException();
		}

		public bool CheckForDVB()
		{
			throw new NotImplementedException();
		}

		public bool CheckForDVBEx(DvbEnumCallback func)
		{
			throw new NotImplementedException();
		}

		public bool CheckForDVBExEx(DvbEnumCallbackEx func)
		{
			throw new NotImplementedException();
		}

		public bool StartDVB()
		{
			throw new NotImplementedException();
		}

		public bool StartDvbEx(int index)
		{
			bool result = _proxiedStreamReader.StartDvbEx(index);
			if (result)
			{
				currentlySelectedTuner = index;
			}
			else
			{
				currentlySelectedTuner = -1;
			}
			return result;
		}

		public bool StopDVB()
		{
			bool result = _proxiedStreamReader.StopDVB();
			if (result)
				currentlySelectedTuner = -1;
			return result;
		}

		public bool GetTunerType(ref STD_TYPE type)
		{
			throw new NotImplementedException();
		}

		public bool SendDiSEqC(uint diseqcType, DiSEqC_Opcode data)
		{
			bool result = _proxiedStreamReader.SendDiSEqC(diseqcType, data);
			if (result)
			{
				lastDiseqcType = diseqcType;
				lastDiseqcOpcode = data;
			}

			return result;
		}

		public bool SendDiseqCmd(byte[] buffer, int length)
		{
			throw new NotImplementedException();
		}

		public bool SendDiseqCmdEx(nint pCmd, int length, nint reply, int replyLength)
		{
			throw new NotImplementedException();
		}

		public bool SetChannel(int freq, int symbrate, int pol, VITERBIRATE_TYPE fec, int lof1, int lof2, int lofsw)
		{
			if (!SwitchTuner(_setFilterTuner))
				return false;

			if (!_proxiedStreamReader.SetChannel(freq, symbrate, pol, fec, lof1, lof2, lofsw))
				return false;

			return true;
		}

		public bool SetChannelEx(int freq, int symbrate, int pol, VITERBIRATE_TYPE fec, int lof1, int lof2, int lofsw, MOD_TYPE mod)
		{
			throw new NotImplementedException();
		}

		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)
		{
			throw new NotImplementedException();
		}

		public bool SetFilter(int pid, StdcallDvbCallback func, int callbackType, int size, ref nint lpFilterNum)
		{
			if (!SwitchTuner(_setFilterTuner))
				return false;

			if (!_proxiedStreamReader.SetFilter(pid, func, callbackType, size, ref lpFilterNum))
				return false;

			return true;
		}

		public bool SetFilterEx(int pid, StdcallDvbCallbackEx lpFunc, int callbackType, int size, ref nint lpFilterNum)
		{
			throw new NotImplementedException();
		}

		public bool SetBitFilter(int pid, nint filterData, nint filterMask, byte filterLength, StdcallDvbCallback lpFunc,
			ref nint lpFilterNum)
		{
			throw new NotImplementedException();
		}

		public bool SetRemoteControl(int irtype, short devAddr, StdcallDvbCallback func, ref nint lpFilterNum)
		{
			throw new NotImplementedException();
		}

		public bool DelFilter(nint filterNum)
		{
			return _proxiedStreamReader.DelFilter(filterNum);
		}

		public bool GetSignal(ref int pStrength, ref int pQuality)
		{
			throw new NotImplementedException();
		}

		public bool GetSignalStrength(ref float pStrength, ref float pQuality)
		{
			throw new NotImplementedException();
		}

		public bool GetSignalEx(ref float pSNR, ref float pBER)
		{
			throw new NotImplementedException();
		}

		public bool GetSignalExEx(ref bool pPresent, ref bool pLock, ref int pRFLevel, ref float pSNR, ref float pBER)
		{
			throw new NotImplementedException();
		}

		public bool Statistic(int[] pStat)
		{
			throw new NotImplementedException();
		}

		public bool GetMAC(byte[] pMac)
		{
			throw new NotImplementedException();
		}

		public Caps GetCaps()
		{
			throw new NotImplementedException();
		}

		public bool RFScan(int freq, int pol, int lof1, int lof2, int lofsw, out double pRFLevel)
		{
			pRFLevel = double.NaN;

			if (!SwitchTuner(_blindScanTuner))
				return false;

			if (!_proxiedStreamReader.RFScan(freq, pol, lof1, lof2, lofsw, out pRFLevel))
				return false;
			return true;
		}

		public bool FFTInit()
		{
			throw new NotImplementedException();
		}

		public bool FFTScan(int freq, int pol, int lof1, int lof2, int lofsw, uint range, byte mode, byte nb_acc, uint[] pTab, ref uint pBegin, ref uint pNum)
		{
			throw new NotImplementedException();
		}

		public bool BLScan(int freq, int freq_range, int pol, int lof1, int lof2, int lofsw, int minsr,
			ref SearchResult pSearchResult)
		{
			throw new NotImplementedException();
		}

		public bool BLScanEx(int freq, int freq_range, int pol, int lof1, int lof2, int lofsw, int minsr, STD_TYPE std,
			ref SearchResult pSearchResult)
		{
			if (!SwitchTuner(_setFilterTuner))
				return false;

			bool diseqc = SendDiSEqC(lastDiseqcType, lastDiseqcOpcode);

			if (!_proxiedStreamReader.BLScanEx(freq, freq_range, pol, lof1, lof2, lofsw, minsr, std, ref pSearchResult))
				return false;

			logger.InfoFormat("Frequency: {0}, Polarity: {1}, Lock: {2}", freq, pol, pSearchResult.Lock);
			
			return true;
		}

		public bool BLScan2(int freq_start, int freq_stop, int pol, int lof1, int lof2, int lofsw, nint pSearchResult, ref int pTpNum,
			BlScanCallback lpFunc)
		{
			if (!SwitchTuner(_blindScanTuner))
				return false;

			if (!_proxiedStreamReader.BLScan2(freq_start, freq_stop, pol, lof1, lof2, lofsw, pSearchResult, ref pTpNum,
				    lpFunc))
				return false;

			return true;
		}

		public bool SignalInfo(ref SearchResult pSearchResult)
		{
			if (!SwitchTuner(_setFilterTuner))
				return false;

			if (!_proxiedStreamReader.SignalInfo(ref pSearchResult))
				return false;

			return true;
		}

		public bool IQScan(uint input, sbyte[] pIQ, uint num)
		{
			if (!SwitchTuner(_setFilterTuner))
				return false;

			if (!_proxiedStreamReader.IQScan(input, pIQ, num))
				return false;

			return true;
		}

		public bool IQScan2(uint point, short[] pIQ, uint num)
		{
			throw new NotImplementedException();
		}

		public bool IQScan2Range(byte input, ref ushort pMinPoint, ref ushort pMaxPoint)
		{
			throw new NotImplementedException();
		}

		public bool CIRScanRange(bool bHiRes, ref ushort pMinCnum, ref ushort pMaxCnum, ref int pMinDelayNs, ref int pMaxDelayNs)
		{
			throw new NotImplementedException();
		}

		public bool CIRScan(bool bHiRes, int[] pPowers, int[] pDelays)
		{
			throw new NotImplementedException();
		}

		public bool CarRange(ref ushort pMinCnum, ref ushort pMaxCnum)
		{
			throw new NotImplementedException();
		}

		public bool CarEsNo(ref ushort cnum, ref double pEsNo)
		{
			throw new NotImplementedException();
		}

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

		public bool PLSSel(byte plsMode, uint code)
		{
			throw new NotImplementedException();
		}

		public bool PLSGet(byte pMode, ref uint pCode)
		{
			throw new NotImplementedException();
		}

		public bool ModSel(ref S2Mode ps2Modes, uint num)
		{
			throw new NotImplementedException();
		}

		public bool ModInv(uint WaitMs, ref S2Mode pS2Modes, ref uint pNum)
		{
			throw new NotImplementedException();
		}

		public bool SetChannel2(uint freq, uint bandwidth)
		{
			throw new NotImplementedException();
		}

		public bool SetChannel2Ex(uint freq, uint bandwidth, STD_TYPE std, int stream)
		{
			throw new NotImplementedException();
		}

		public bool SetChannel2ExEx(uint freq, uint bandwidth, uint symbrate, STD_TYPE std, int stream)
		{
			throw new NotImplementedException();
		}

		public bool SignalInfo2(ref SearchResult2 si2)
		{
			throw new NotImplementedException();
		}

		public bool RFScan2(uint freq, STD_TYPE std, ref double pRFLevel)
		{
			throw new NotImplementedException();
		}

		public bool AirScan(int freq_start, int freq_stop, uint step, uint bandwidth, int std, nint pSearchResult, ref int pTpNum,
			AirScanCallback lpFunc)
		{
			throw new NotImplementedException();
		}

		public bool GetEEPROM(byte[] buffer, int offset, int len)
		{
			throw new NotImplementedException();
		}

		public bool SetEEPROM(byte[] buffer, int offset, int len)
		{
			throw new NotImplementedException();
		}

		public Version GetEngineVersion()
		{
			throw new NotImplementedException();
		}

		public string GetEngineName()
		{
			throw new NotImplementedException();
		}

		public bool FFTTerm()
		{
			throw new NotImplementedException();
		}
	}
}
