using System;
using System.Runtime.InteropServices;
using skyscraper5.Skyscraper.IO.CrazycatStreamReader;

namespace skyscraper5.Skyscraper.IO
{
    public interface IStreamReader : IDisposable
    {
        /// <summary>
        /// Checks whether any sort of tuner is present in this system.
        /// </summary>
        /// <returns>Whether a tuner is present.</returns>
        bool CheckForDVB();

        /// <summary>
        /// Checks whether one or more tuner is present in the system.
        /// </summary>
        /// <param name="func">A method which will be called for each tuner found.</param>
        /// <returns>Whether a tuner was found</returns>
        bool CheckForDVBEx([MarshalAs(UnmanagedType.FunctionPtr)] DvbEnumCallback func);

        /// <summary>
        /// Checks whether one or more tuner is present in the system.
        /// </summary>
        /// <param name="func">A method which will be called for each tuner found.</param>
        /// <returns>Whether a tuner was found</returns>
        bool CheckForDVBExEx([MarshalAs(UnmanagedType.FunctionPtr)] DvbEnumCallbackEx func);

        /// <summary>
        /// Boots the first system tuner and sets it as the currently active tuner. Note that
        /// only one tuner can be active at the same time.
        /// </summary>
        /// <returns>Whether a tuner was booted sucessfully.</returns>
        bool StartDVB();

        /// <summary>
        /// Boots a tuner and makes it the currently active tuner. Note that only one tuner
        /// can be active at the same time.
        /// </summary>
        /// <param name="index">The ID of the tuner to boot</param>
        /// <returns>Whether the turner booted sucessfully.</returns>
        bool StartDvbEx(int index);

        /// <summary>
        /// Shuts down the currently active tuner.
        /// </summary>
        /// <returns>Whether the tuner was shut down sucessfully</returns>
        bool StopDVB();

        /// <summary>
        /// Retrieves the tuner standard.
        /// </summary>
        /// <param name="type">A Pointer in which the tuner type will be put into.</param>
        /// <returns>Whether the tuner reports a standard</returns>
        bool GetTunerType(ref STD_TYPE type);

        /// <summary>
        /// Sends a raw command to a DiSEqC switch with up to four positions.
        /// </summary>
        /// <param name="diseqcType">0 for disabling DiSEqc, 1 for a tone-burst, 2 for enabling DiSEqC 1.0</param>
        /// <param name="data">The opcode to send.</param>
        /// <returns>Whether the command was sent</returns>
        bool SendDiSEqC(uint diseqcType, DiSEqC_Opcode data);

        /// <summary>
        /// Send a raw command to a DiSEqC-Motor, ignoring any potential responses.
        /// </summary>
        /// <param name="buffer">The buffer containing the raw command</param>
        /// <param name="length">The length of the command</param>
        /// <returns>Whether the command was sent</returns>
        bool SendDiseqCmd(byte[] buffer, int length);

        /// <summary>
        /// Supposed to send a raw command to a DiSEqC-Motor and awaits a response.
        /// Since I couldn't test this - (I don't have a motor on my dish!) this will merely
        /// throw a NotImplementedException
        /// </summary>
        /// <param name="pCmd">The command to send to the motor</param>
        /// <param name="length">The length of the command to send to the motor.</param>
        /// <param name="reply">A byte array to store the motor's response in.</param>
        /// <param name="replyLength">Expected length of the motor's response</param>
        /// <returns>Throws a NotImplementedException</returns>
        bool SendDiseqCmdEx(IntPtr pCmd, int length, IntPtr reply, int replyLength);

        /// <summary>
        /// Set the tuner to a satellite frequency
        /// </summary>
        /// <param name="freq">The frequency in kHz.</param>
        /// <param name="symbrate">The symbol rate in Sym/s. (e.g. 27500 * 1000 = 27500000)</param>
        /// <param name="pol">0 for horizontal, 1 for vertical.</param>
        /// <param name="fec">The error correction method used by the channel.</param>
        /// <param name="lof1">Local Oscillator Frequency 1 of the LNB in kHz. (9750000 for Ku-Band)</param>
        /// <param name="lof2">Local Oscillator Frequency 2 of the LNB in kHz. (10600000 for Ku-Band)</param>
        /// <param name="lofsw">Border Frequency for switching between Low Band and High Band in kHz (11700000 for Ku-Band)</param>
        /// <returns>Whether the tuner complied with the request</returns>
        bool SetChannel(int freq, int symbrate, int pol, VITERBIRATE_TYPE fec, int lof1, int lof2, int lofsw);

        /// <summary>
        /// Set the tuner to a satellite frequency
        /// </summary>
        /// <param name="freq">The frequency in kHz.</param>
        /// <param name="symbrate">The symbol rate in Sym/s. (e.g. 27500 * 1000 = 27500000)</param>
        /// <param name="pol">0 for horizontal, 1 for vertical.</param>
        /// <param name="fec">The error correction method used by the channel.</param>
        /// <param name="lof1">Local Oscillator Frequency 1 of the LNB in kHz. (9750000 for Ku-Band)</param>
        /// <param name="lof2">Local Oscillator Frequency 2 of the LNB in kHz. (10600000 for Ku-Band)</param>
        /// <param name="lofsw">Border Frequency for switching between Low Band and High Band in kHz (11700000 for Ku-Band)</param>
        /// <param name="mod">The modulation type used by the channel.</param>
        /// <returns>Whether the tuner complied with the request.</returns>
        bool SetChannelEx(int freq, int symbrate, int pol, VITERBIRATE_TYPE fec, int lof1, int lof2, int lofsw, MOD_TYPE mod);

        /// <summary>
        /// Set the tuner to a satellite frequency
        /// </summary>
        /// <param name="freq">The frequency in kHz.</param>
        /// <param name="symbrate">The symbol rate in Sym/s. (e.g. 27500 * 1000 = 27500000)</param>
        /// <param name="pol">0 for horizontal, 1 for vertical.</param>
        /// <param name="fec">The error correction method used by the channel.</param>
        /// <param name="lof1">Local Oscillator Frequency 1 of the LNB in kHz. (9750000 for Ku-Band)</param>
        /// <param name="lof2">Local Oscillator Frequency 2 of the LNB in kHz. (10600000 for Ku-Band)</param>
        /// <param name="lofsw">Border Frequency for switching between Low Band and High Band in kHz (11700000 for Ku-Band)</param>
        /// <param name="mod">The modulation type used by the channel.</param>
        /// <param name="inv">Spectral inversion: 0 for autodetection, 1 for no spectral inversion, 2 for spectral inversion.</param>
        /// <param name="pilot">S2 Pilot Symbols: 0 for autodetection, 1 for no pilot symbols, 2 for pilot symbols</param>
        /// <param name="rolloff"></param>
        /// <returns></returns>
        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);

        /// <summary>
        /// Starts receiving data from the tuner.
        /// </summary>
        /// <param name="pid">The PID you want to receive. Set this to 8192 if you want ALL pids.</param>
        /// <param name="func">The function to call when a packet was received.</param>
        /// <param name="callbackType">Set this to 0x02 for all that is holy. Others might cause blue-screens.</param>
        /// <param name="size">Set this to 1 to receive single packets, or 2 to receive (a varying amount of) multiple packets.</param>
        /// <param name="lpFilterNum">The pointer you can use with DelFilter.</param>
        /// <returns>Whether the filter was started sucesfully.</returns>
        bool SetFilter(int pid, [MarshalAs(UnmanagedType.FunctionPtr)] StdcallDvbCallback func, int callbackType, int size, ref IntPtr lpFilterNum);

        /// <summary>
        /// Starts receiving data from the tuner.
        /// </summary>
        /// <param name="pid">The PID you want to receive. Set this to 8192 if you want ALL pids.</param>
        /// <param name="func">The function to call when a packet was received.</param>
        /// <param name="callbackType">Set this to 0x02 for all that is holy. Others might cause blue-screens.</param>
        /// <param name="size">Set this to 1 to receive single packets, or 2 to receive (a varying amount of) multiple packets.</param>
        /// <param name="lpFilterNum">The pointer you can use with DelFilter.</param>
        /// <returns>Whether the filter was started sucesfully.</returns>
        bool SetFilterEx(int pid, [MarshalAs(UnmanagedType.FunctionPtr)] StdcallDvbCallbackEx lpFunc, int callbackType, int size, ref IntPtr lpFilterNum);

        bool SetBitFilter(int pid, IntPtr filterData, IntPtr filterMask, byte filterLength, [MarshalAs(UnmanagedType.FunctionPtr)] StdcallDvbCallback lpFunc, ref IntPtr lpFilterNum);

        /// <summary>
        /// Creates a filter to read data from the virtual IR Channel of the tuner.
        /// </summary>
        /// <param name="irtype">The remote control type. 0x00 works for all remote types.</param>
        /// <param name="devAddr">The adress of the remote control. Set to -1 for all remotes.</param>
        /// <param name="func">The function to call when an IR Command was received.</param>
        /// <param name="lpFilterNum">A pointer to the filter to use with DelFilter.</param>
        /// <returns>Whether the listening to a remote command is working.</returns>
        bool SetRemoteControl(int irtype, short devAddr, [MarshalAs(UnmanagedType.FunctionPtr)] StdcallDvbCallback func, ref IntPtr lpFilterNum);

        /// <summary>
        /// Stops reading data.
        /// </summary>
        /// <param name="filterNum">The pointer to the filter which should be stopped.</param>
        /// <returns>Whether the filter could be stopped.</returns>
        bool DelFilter(IntPtr filterNum);

        /// <summary>
        /// Reports the signal strength and quality. For some tuners you'll need GetSignalStrength instead.
        /// </summary>
        /// <param name="pStrength">A float pointer which will be set to the signal strength.</param>
        /// <param name="pQuality">A float pointer which will be set to the signal quality.</param>
        /// <returns>Whether these information could be reported.</returns>
        bool GetSignal(ref int pStrength, ref int pQuality);

        /// <summary>
        /// An alternative method to read the signal strength and quality.
        /// </summary>
        /// <param name="pStrength">A float pointer which will be set to the signal strength.</param>
        /// <param name="pQuality">A float pointer which will be set to the signal quality.</param>
        /// <returns>Whether these information could be reported.</returns>
        bool GetSignalStrength(ref float pStrength, ref float pQuality);

        /// <summary>
        /// Gets information about the current signal.
        /// </summary>
        /// <param name="pSNR">Pointer to a float which will be set to the Signal-to-Noise ratio.</param>
        /// <param name="pBER">Pointer to a float which will be set to the biterror rate.</param>
        /// <returns>Whether the data could be reported.</returns>
        bool GetSignalEx(ref float pSNR, ref float pBER);

        /// <summary>
        /// Gets information about the current signal.
        /// </summary>
        /// <param name="pPresent">Pointer to a boolean which will be set to true if a signal is present.</param>
        /// <param name="pLock">Pointer to a boolean which will be set to true if a signal lock is available.</param>
        /// <param name="pRFLevel">Pointer to an integer which will be set to the RF Level.</param>
        /// <param name="pSNR">Pointer to a float which will be set to the Signal-to-Noise ratio.</param>
        /// <param name="pBER">Pointer to a float which will be set to the biterror rate.</param>
        /// <returns>Whether the data could be reported.</returns>
        bool GetSignalExEx(ref bool pPresent, ref bool pLock, ref int pRFLevel, ref float pSNR, ref float pBER);

        /// <summary>
        /// Read signal statistics into an integer array.
        /// </summary>
        /// <param name="pStat">Pointer to an array which will receive the following elements: Locked?, Signal Quality, Signal Present?, Signal Strength</param>
        /// <returns>Whether the statistics could be reported.</returns>
        bool Statistic(int[] pStat);

        /// <summary>
        /// Reads the MAC Adress of a tuner.
        /// </summary>
        /// <param name="pMac">A byte array to put the MAC into.</param>
        /// <returns>Whether the MAC was sucessfully read.</returns>
        bool GetMAC(byte[] pMac);

        /// <summary>
        /// Gets the TunerMetadata capabilities.
        /// </summary>
        /// <returns>A bitmask containing all the tuner capabilities.</returns>
        Caps GetCaps();

        /// <summary>
        /// Gets the RF Strength of a satellite frequency.
        /// </summary>
        /// <param name="freq">An approximate channel frequency in kHz</param>
        /// <param name="pol">0 for horizontal, 1 for vertical</param>
        /// <param name="lof1">Local Oscillator Frequency 1 of the LNB in kHz. (9750000 for Ku-Band)</param>
        /// <param name="lof2">Local Oscillator Frequency 2 of the LNB in kHz. (10600000 for Ku-Band)</param>
        /// <param name="lofsw">Border Frequency for switching between Low Band and High Band in kHz (11700000 for Ku-Band)</param>
        /// <param name="pRFLevel">The pointer to put the result in.</param>
        /// <returns>Whether the probe was sucessful.</returns>
        bool RFScan(int freq, int pol, int lof1, int lof2, int lofsw, out double pRFLevel);

        bool FFTInit();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="freq"></param>
        /// <param name="pol"></param>
        /// <param name="lof1"></param>
        /// <param name="lof2"></param>
        /// <param name="lofsw"></param>
        /// <param name="range"></param>
        /// <param name="mode">Set this to 5. This is a value known to work.</param>
        /// <param name="nb_acc">Set this to 255. This is a value known to work.</param>
        /// <param name="pTab"></param>
        /// <param name="pBegin"></param>
        /// <param name="pNum"></param>
        /// <returns></returns>
        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);

        /// <summary>
        /// Given an approximate satellite frequency, tune to the nearest channel.
        /// </summary>
        /// <param name="freq">An approximate channel frequency in kHz</param>
        /// <param name="freq_range">The range where to look for in kHz</param>
        /// <param name="pol">0 for horizontal, 1 for vertical</param>
        /// <param name="lof1">Local Oscillator Frequency 1 of the LNB in kHz. (9750000 for Ku-Band)</param>
        /// <param name="lof2">Local Oscillator Frequency 2 of the LNB in kHz. (10600000 for Ku-Band)</param>
        /// <param name="lofsw">Border Frequency for switching between Low Band and High Band in kHz (11700000 for Ku-Band)</param>
        /// <param name="minsr">The minimum acceptable symbol rate. Depending on your tuner you either want 200 or 1000.</param>
        /// <param name="pSearchResult">The exact tuning data</param>
        /// <returns>Whether a channel could be found</returns>
        bool BLScan(int freq, int freq_range, int pol, int lof1, int lof2, int lofsw, int minsr, ref SearchResult pSearchResult);

        /// <summary>
        /// Given an approximate satellite frequency, tune to the nearest channel.
        /// </summary>
        /// <param name="freq">An approximate channel frequency in kHz</param>
        /// <param name="freq_range">The range where to look for in kHz</param>
        /// <param name="pol">0 for horizontal, 1 for vertical</param>
        /// <param name="lof1">Local Oscillator Frequency 1 of the LNB in kHz. (9750000 for Ku-Band)</param>
        /// <param name="lof2">Local Oscillator Frequency 2 of the LNB in kHz. (10600000 for Ku-Band)</param>
        /// <param name="lofsw">Border Frequency for switching between Low Band and High Band in kHz (11700000 for Ku-Band)</param>
        /// <param name="minsr">The minimum acceptable symbol rate. Depending on your tuner you either want 200 or 1000.</param>
        /// <param name="std">The network type</param>
        /// <param name="pSearchResult">The exact tuning data</param>
        /// <returns>Whether a channel could be found</returns>
        bool BLScanEx(int freq, int freq_range, int pol, int lof1, int lof2, int lofsw, int minsr, STD_TYPE std, ref SearchResult pSearchResult);

        /// <summary>
        /// Performs a blind scan on a satellite network.
        /// This method blocks until the scan is complete.
        /// </summary>
        /// <param name="freq_start">The frequency to the start the scan on in kHz (10700000 for Ku-Band)</param>
        /// <param name="freq_stop">The frequency to end the scan on in kHz (12750000 for Ku-Band)</param>
        /// <param name="pol">0 for horizontal, 1 for vertical.</param>
        /// <param name="lof1">Local Oscillator Frequency 1 of the LNB in kHz. (9750000 for Ku-Band)</param>
        /// <param name="lof2">Local Oscillator Frequency 2 of the LNB in kHz. (10600000 for Ku-Band)</param>
        /// <param name="lofsw">Border Frequency for switching between Low Band and High Band in kHz (11700000 for Ku-Band)</param>
        /// <param name="pSearchResult">The struct to put the last found channel in.</param>
        /// <param name="pTpNum">Number of channels found.</param>
        /// <param name="lpFunc">The function to call when a channel was found.</param>
        /// <returns>Whether the blind scan was performed sucessfully.</returns>
        bool BLScan2(int freq_start, int freq_stop, int pol, int lof1, int lof2, int lofsw, IntPtr pSearchResult, ref int pTpNum, [MarshalAs(UnmanagedType.FunctionPtr)] BlScanCallback lpFunc);

        /// <summary>
        /// Retrieve extended carrier info after lock for a satelite signal.
        /// </summary>
        /// <param name="pSearchResult">Struct to output the data to</param>
        /// <returns>Whether the carrier info could be read.</returns>
        bool SignalInfo(ref SearchResult pSearchResult);

        /// <summary>
        /// Read constellation data for a satellite signal.
        /// </summary>
        /// <param name="input">Type of constellation data to read. You probably want 0.</param>
        /// <param name="pIQ">Output Array</param>
        /// <param name="num">Number of Constellation points to read. Must be (pIQ.Length / 2)</param>
        /// <returns>Whether the constellation data was read</returns>
        bool IQScan(uint input, sbyte[] pIQ, uint num);

        /// <summary>
        /// Read constellation data for a non-satellite signal.
        /// </summary>
        /// <param name="input">Type of constellation data to read. You probably want 0.</param>
        /// <param name="pIQ">Output Array</param>
        /// <param name="num">Number of Constellation points to read. Must be (pIQ.Length / 2)</param>
        /// <returns>Whether the constellation data was read</returns>
        bool IQScan2(uint point, short[] pIQ, uint num);

        bool IQScan2Range(byte input, ref ushort pMinPoint, ref ushort pMaxPoint);

        bool CIRScanRange(bool bHiRes, ref ushort pMinCnum, ref ushort pMaxCnum, ref int pMinDelayNs, ref int pMaxDelayNs);

        bool CIRScan(bool bHiRes, int[] pPowers, int[] pDelays);

        bool CarRange(ref ushort pMinCnum, ref ushort pMaxCnum);

        bool CarEsNo(ref ushort cnum, ref double pEsNo);

        bool MISSel(bool bEnable, byte misFilter, byte misFilterMask);

        /// <summary>
        /// Sets the Physical Layer Scrambling credentials.
        /// </summary>
        /// <param name="plsMode">The PLS mode.</param>
        /// <param name="code">The PLS key.</param>
        /// <returns>Whether the tuner complied.</returns>
        bool PLSSel(byte plsMode, uint code);

        /// <summary>
        /// Gets the current Physical Layer Scrambling key.
        /// </summary>
        /// <param name="pMode">The pointer to write the PLS Mode in.</param>
        /// <param name="pCode">The pointer to write the PLS Key in.</param>
        /// <returns>Whether the tuner sent valid PLS credentials.</returns>
        bool PLSGet(byte pMode, ref uint pCode);

        bool ModSel(ref S2Mode ps2Modes, uint num);

        bool ModInv(uint WaitMs, ref S2Mode pS2Modes, ref uint pNum);

        /// <summary>
        /// Tune to DVB-T2 / DVB-C Channel
        /// </summary>
        /// <param name="freq">Frequency in kHz</param>
        /// <param name="bandwidth">Channel Bandwidth in kHz (usually 8000)</param>
        /// <returns>Whether the tuner complied with the request.</returns>
        bool SetChannel2(uint freq, uint bandwidth);

        /// <summary>
        /// Tune to DVB-T2 / DVB-C channel.
        /// </summary>
        /// <param name="freq">Frequency in kH</param>
        /// <param name="bandwidth">Channel Bandwidth in kHz (usually 8000)</param>
        /// <param name="std">Type of network</param>
        /// <param name="stream">??? (Setting this to 0 works.)</param>
        /// <returns>Whether the tuner complied with the request.</returns>
        bool SetChannel2Ex(uint freq, uint bandwidth, STD_TYPE std, int stream);

        /// <summary>
        /// Tune to DVB-T2 / DVB-C Channel. Beware, will freeze for a minute when SymbolRate is wrong.
        /// </summary>
        /// <param name="freq">Frequency in kHz</param>
        /// <param name="bandwidth">Channel Bandwidth in kHz (usually 8000)</param>
        /// <param name="symbrate">Sybol Rate in kiloSymbols (6900000 for Vodafone)</param>
        /// <param name="std">Type of network</param>
        /// <param name="stream">??? Setting this to 0 works.</param>
        /// <returns>Whether the tuner complied with the request.</returns>
        bool SetChannel2ExEx(uint freq, uint bandwidth, uint symbrate, STD_TYPE std, int stream);

        /// <summary>
        /// Retrieve extended carrier info after lock for a non-sattelite signal.
        /// </summary>
        /// <param name="si2">Outputs the Signal information</param>
        /// <returns>Whether the signal information was read sucessfully</returns>
        bool SignalInfo2(ref SearchResult2 si2);


        /// <summary>
        /// Get RF Level from FM / UHF / VHF Frequency
        /// </summary>
        /// <param name="freq">Frequency in kHz</param>
        /// <param name="std">TunerMetadata standard</param>
        /// <param name="pRFLevel">Output pointer</param>
        /// <returns>whether the operation completed sucessfully</returns>
        bool RFScan2(uint freq, STD_TYPE std, ref double pRFLevel);

        /// <summary>
        /// Performs a blind scan on non-satellite networks.
        /// This method blocks until the scan is complete!
        /// </summary>
        /// <param name="freq_start">Starting Frequency in kHz</param>
        /// <param name="freq_stop">End Frequency in kHz</param>
        /// <param name="step">Frequency increment in kHz (3000 is usually a safe choice here)</param>
        /// <param name="bandwidth">System bandwidth (usually 8000)</param>
        /// <param name="std">Type of Network</param>
        /// <param name="pSearchResult">Outputs the final channel. Not really necessary.</param>
        /// <param name="pTpNum">Outputs the number of channels.</param>
        /// <param name="lpFunc">Function to call when a channel was found.</param>
        /// <returns>Whether the scan was performed sucessfully</returns>
        bool AirScan(int freq_start, int freq_stop, uint step, uint bandwidth, int std, IntPtr pSearchResult, ref int pTpNum, [MarshalAs(UnmanagedType.FunctionPtr)] AirScanCallback lpFunc);

        /// <summary>
        /// Reads from the EEPROM of the tuner.
        /// </summary>
        /// <param name="buffer">Buffer to put the read data in.</param>
        /// <param name="offset">Offset from where to start reading from the EEPROM</param>
        /// <param name="len">How many bytes to read</param>
        /// <returns>Wheter the reading operation was sucessful.</returns>
        bool GetEEPROM(byte[] buffer, int offset, int len);

        /// <summary>
        /// Supposed to write data to the device's EEPROM.
        /// This method is not implemented on purpose because I neither have the balls to try
        /// this, nor the money to replace my tuner in case I mess up.
        /// This will throw a NotSupportedException when calling this.
        /// </summary>
        /// <param name="buffer">Buffer of data to write to the EEPROM.</param>
        /// <param name="offset">Offset in the EEPROM to start writing.</param>
        /// <param name="len">How many bytes to write to the EEPROM</param>
        /// <returns>Throws NotSupportedException</returns>
        bool SetEEPROM(byte[] buffer, int offset, int len);

		/// <summary>
		/// This is a skyscraper5 extension. It is supposed to return the version of the software used for tuning.
		/// </summary>
		/// <returns>For example: 1.2.5.176</returns>
		Version GetEngineVersion();

		/// <summary>
		/// This is a skyscraper5 extension. It is supposed to return the name of the software used for tuning.
		/// </summary>
		/// <returns>For example: "StreamReaderEx for BDA Tuners</returns>
		string GetEngineName();
		bool FFTTerm();
	}
}
