/// <summary>
    /// Decode timing data to discover IR Protocol and packet payload.
    /// </summary>
    /// <param name="timingData">Input timing data.</param>
    /// <param name="remoteCallback">Method to call when Remote button decoded.</param>
    /// <param name="keyboardCallback">Method to call when Keyboard event decoded.</param>
    /// <param name="mouseCallback">Method to call when Mouse event decoded.</param>
    public static void DecodeIR(int[] timingData, RemoteCallback remoteCallback, KeyboardCallback keyboardCallback,
                                MouseCallback mouseCallback)
    {
      if (timingData == null)
        return;

      try
      {
        DetectDaewoo(timingData, remoteCallback);
        //DetectITT(timingData, remoteCallback);
        DetectJVC(timingData, remoteCallback);
        DetectMatsushita(timingData, remoteCallback);
        DetectMitsubishi(timingData, remoteCallback);
        DetectNEC(timingData, remoteCallback);
        DetectNRC17(timingData, remoteCallback);
        DetectPanasonic(timingData, remoteCallback);
        DetectRC5(timingData, remoteCallback);
        DetectRC6(timingData, remoteCallback);
        DetectRCA(timingData, remoteCallback);
        //DetectRCMM(timingData, remoteCallback);
        DetectRECS80(timingData, remoteCallback);
        //DetectSharp(timingData, remoteCallback);
        DetectSIRC(timingData, remoteCallback);
        DetectToshiba(timingData, remoteCallback);
        //DetectXSAT(timingData, remoteCallback);

        DetectMCE(timingData, keyboardCallback, mouseCallback);
        //DetectIMon(timingData, keyboardCallback, mouseCallback);
      }
#if TRACE
      catch (Exception ex)
      {
        Trace.WriteLine(ex.ToString());
#else
      catch
      {
#endif
        Daewoo_Data = new RemoteDetectionData();
        JVC_Data = new RemoteDetectionData();
        Matsushita_Data = new RemoteDetectionData();
        Mitsubishi_Data = new RemoteDetectionData();
        NEC_Data = new RemoteDetectionData();
        NRC17_Data = new RemoteDetectionData();
        Panasonic_Data = new RemoteDetectionData();
        RC5_Data = new RemoteDetectionData();
        RC6_Data = new RemoteDetectionData();
        RCA_Data = new RemoteDetectionData();
        RECS80_Data = new RemoteDetectionData();
        SIRC_Data = new RemoteDetectionData();
        Toshiba_Data = new RemoteDetectionData();

        MCE_Data = new MceDetectionData();
      }
    }
    private static void DetectMCE(int[] timingData, KeyboardCallback keyboardCallback, MouseCallback mouseCallback)
    {
      // Mouse:    0 0001 xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
      // Keyboard: 0 0100 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

      const int HalfBit_None = 0;
      const int HalfBit_Zero = 1;
      const int HalfBit_One = 2;

      for (int i = 0; i < timingData.Length; i++)
      {
        int duration = Math.Abs(timingData[i]);
        bool pulse = (timingData[i] > 0);

        #region Working data ...

        if (MCE_Data.State != MceKeyboardDetectState.Header)
        {
          switch (MCE_Data.HalfBit)
          {
              #region HalfBit_None

            case HalfBit_None:
              if (IsBetween(duration, 100, 450))
              {
                MCE_Data.HalfBit = (pulse ? HalfBit_One : HalfBit_Zero);
              }
              else if (IsBetween(duration, 500, 800))
              {
                //Trace.WriteLine("Bad bit sequence double {0}", pulse);
                MCE_Data.HalfBit = (pulse ? HalfBit_One : HalfBit_Zero);
                //MCE_Data = new MceDetectionData();
                return;
              }
              else
              {
                // Over Length duration (Treat as a Zero bit)
                //Trace.WriteLine("Bad duration {0}", duration);
                MCE_Data.Working <<= 1;
                MCE_Data.Bit--;
              }
              break;

              #endregion HalfBit_None

              #region HalfBit_Zero

            case HalfBit_Zero:
              if (IsBetween(duration, 100, 450))
              {
                if (pulse)
                {
                  MCE_Data.Working <<= 1;
                  MCE_Data.Bit--;
                  MCE_Data.HalfBit = HalfBit_None;
                }
                else
                {
                  //Trace.WriteLine("Bad bit sequence 00");
                  MCE_Data = new MceDetectionData();
                  //return;
                }
              }
              else if (IsBetween(duration, 500, 800))
              {
                if (pulse)
                {
                  MCE_Data.Working <<= 1;
                  MCE_Data.Bit--;
                  MCE_Data.HalfBit = HalfBit_One;
                }
                else
                {
                  //Trace.WriteLine("Bad bit sequence 00 0");
                  MCE_Data = new MceDetectionData();
                  //return;
                }
              }
              else
              {
                //Trace.WriteLine("Bad duration {0}", duration);
                if (MCE_Data.Bit == 1)
                {
                  MCE_Data.Working <<= 1;
                  //MceKeyboard_Data.Working |= 1;
                  MCE_Data.Bit--;
                  MCE_Data.HalfBit = HalfBit_None;
                  //i--;
                }
                else
                {
                  MCE_Data = new MceDetectionData();
                }
                //return;
              }
              break;

              #endregion HalfBit_Zero

              #region HalfBit_One

            case HalfBit_One:
              if (IsBetween(duration, 100, 450))
              {
                if (!pulse)
                {
                  MCE_Data.Working <<= 1;
                  MCE_Data.Working |= 1;
                  MCE_Data.Bit--;
                  MCE_Data.HalfBit = HalfBit_None;
                }
                else
                {
                  //Trace.WriteLine("Bad bit sequence 11");
                  MCE_Data = new MceDetectionData();
                  //return;
                }
              }
              else if (IsBetween(duration, 500, 800))
              {
                if (!pulse)
                {
                  MCE_Data.Working <<= 1;
                  MCE_Data.Working |= 1;
                  MCE_Data.Bit--;
                  MCE_Data.HalfBit = HalfBit_Zero;
                }
                else
                {
                  //Trace.WriteLine("Bad bit sequence 11 1");
                  MCE_Data = new MceDetectionData();
                  //return;
                }
              }
              else
              {
                //Trace.WriteLine("Bad duration {0}", duration);
                if (MCE_Data.Bit == 1)
                {
                  MCE_Data.Working <<= 1;
                  MCE_Data.Working |= 1;
                  MCE_Data.Bit--;
                  MCE_Data.HalfBit = HalfBit_None;
                  //i--;
                }
                else
                {
                  MCE_Data = new MceDetectionData();
                  //return;
                }
              }
              break;

              #endregion HalfBit_One
          }
        }

        #endregion Working data ...

        switch (MCE_Data.State)
        {
            #region Header

          case MceKeyboardDetectState.Header:
            if (pulse && IsBetween(duration, 2600, 3300))
            {
              MCE_Data.State = MceKeyboardDetectState.CodeType;
              MCE_Data.Type = 0;
              MCE_Data.Bit = 5;
              MCE_Data.Working = 0;
            }

            break;

            #endregion Header

            #region CodeType

          case MceKeyboardDetectState.CodeType:
            if (MCE_Data.Bit == 0)
            {
              MCE_Data.Type = MCE_Data.Working;

              if (MCE_Data.Type == MceKeyboard)
              {
                MCE_Data.State = MceKeyboardDetectState.KeyboardIgnore;
                MCE_Data.Bit = 16;
                MCE_Data.Working = 0;
              }
              else if (MCE_Data.Type == MceMouse)
              {
                MCE_Data.State = MceKeyboardDetectState.MouseIgnore;
                MCE_Data.Bit = 8;
                MCE_Data.Working = 0;
              }
              else
              {
                //Trace.WriteLine("KB: Invalid Type {0}", MceKeyboard_Data.Type);
                return;
              }
            }

            break;

            #endregion CodeType

            #region Keyboard

            #region KeyboardIgnore

          case MceKeyboardDetectState.KeyboardIgnore:
            if (MCE_Data.Bit == 0)
            {
              MCE_Data.State = MceKeyboardDetectState.KeyCode;
              MCE_Data.Bit = 8;
              MCE_Data.Working = 0;
            }

            break;

            #endregion KeyboardIgnore

            #region KeyCode

          case MceKeyboardDetectState.KeyCode:
            if (MCE_Data.Bit == 0)
            {
              MCE_Data.KeyCode = MCE_Data.Working;

              MCE_Data.State = MceKeyboardDetectState.Modifiers;
              MCE_Data.Bit = 8;
              MCE_Data.Working = 0;
            }

            break;

            #endregion KeyCode

            #region Modifiers

          case MceKeyboardDetectState.Modifiers:
            if (MCE_Data.Bit == 0)
            {
              MCE_Data.Modifiers = MCE_Data.Working;

              keyboardCallback(MCE_Data.KeyCode, MCE_Data.Modifiers);

              MCE_Data = new MceDetectionData();
            }

            break;

            #endregion Modifiers

            #endregion Keyboard

            #region Mouse

            #region MouseIgnore

          case MceKeyboardDetectState.MouseIgnore:
            if (MCE_Data.Bit == 0)
            {
              MCE_Data.State = MceKeyboardDetectState.DeltaY;
              MCE_Data.Bit = 7;
              MCE_Data.Working = 0;
            }

            break;

            #endregion MouseIgnore

            #region DeltaY

          case MceKeyboardDetectState.DeltaY:
            if (MCE_Data.Bit == 0)
            {
              MCE_Data.DeltaY = ScaleMouseDelta((int) MCE_Data.Working);

              MCE_Data.State = MceKeyboardDetectState.DeltaX;
              MCE_Data.Bit = 7;
              MCE_Data.Working = 0;
            }

            break;

            #endregion DeltaY

            #region DeltaX

          case MceKeyboardDetectState.DeltaX:
            if (MCE_Data.Bit == 0)
            {
              MCE_Data.DeltaX = ScaleMouseDelta((int) MCE_Data.Working);

              MCE_Data.State = MceKeyboardDetectState.Right;
              MCE_Data.Bit = 1;
              MCE_Data.Working = 0;
            }

            break;

            #endregion DeltaX

            #region Right

          case MceKeyboardDetectState.Right:
            if (MCE_Data.Bit == 0)
            {
              MCE_Data.Right = (MCE_Data.Working == 1);

              MCE_Data.State = MceKeyboardDetectState.Left;
              MCE_Data.Bit = 1;
              MCE_Data.Working = 0;
            }

            break;

            #endregion Right

            #region Left

          case MceKeyboardDetectState.Left:
            if (MCE_Data.Bit == 0)
            {
              MCE_Data.Left = (MCE_Data.Working == 1);

              MCE_Data.State = MceKeyboardDetectState.Checksum;
              MCE_Data.Bit = 5;
              MCE_Data.Working = 0;
            }

            break;

            #endregion Left

            #region Checksum

          case MceKeyboardDetectState.Checksum:
            if (MCE_Data.Bit == 0)
            {
              mouseCallback(MCE_Data.DeltaX, MCE_Data.DeltaY, MCE_Data.Right, MCE_Data.Left);

              MCE_Data = new MceDetectionData();
            }

            break;

            #endregion Checksum

            #endregion Mouse
        }

        if (MCE_Data.Bit < 0)
          MCE_Data = new MceDetectionData();
      }
    }