///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// File:           CC121.h                                                   //
//                                                                           //
// Functionality:  Control surface class definition.                         //
//                                                                           //
// Author:         Rolf Meyerhoff (rm@matrix44.de)                           //
//                                                                           //
// Copyright:      (C) 2009 by Rolf Meyerhoff. All rights reserved.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// License:                                                                  //
//                                                                           //
// This library is free software;  you can redistribute it  and/or modify it //
// under the terms of the GNU Lesser  General Public License as published by //
// the Free Software Foundation;  either version 2.1 of the License,  or (at //
// your option) any later version.                                           //
//                                                                           //
// This  library is  distributed in  the hope that  it will  be useful,  but //
// WITHOUT   ANY   WARRANTY;   without   even   the   implied  warranty   of //
// MERCHANTABILITY or FITNESS FOR A  PARTICULAR PURPOSE.  See the GNU Lesser //
// General Public License for more details.                                  //
//                                                                           //
// You should have received a copy of  the GNU Lesser General Public License //
// along with this library;  if not,  write to the Free Software Foundation, //
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.              //
///////////////////////////////////////////////////////////////////////////////
#ifndef __CC121_H_INCLUDED__
#define __CC121_H_INCLUDED__

///////////////////////////////////////////////////////////////////////////////
// class CC121
///////////////////////////////////////////////////////////////////////////////
// This class is the heart of this extension as it implements the required
// IReaperControlSurface interface that REAPER needs to talk to us.
// This class establishs a connections to the hardware and reacts on the
// messages of the controlling device. Depending on the message received this
// extension will then update the state REAPER accordingly.
// REAPER on the other side will notify this extension about state changes so
// we can update the state of the actual hardware accordingly. 
//  
// This is the usual program flow:
//
// At startup REAPER calls the entry point of this dll (ReaperPluginEntry). In
// this function we register a new control surface and pass a creation and a
// configuration function to REAPER.
//
// Whenever REAPER needs an instance of this control surface it will call the
// registered creation function with the configuration as text. This function
// creates an instance of this control surface class and returns it to REAPER.
// From this point on REAPER has a new control surface attached. :)
//
// Whenever something changes inside REAPER we will get notified through the
// public functions of this class. Eg if anybody clicks on the play button on
// REAPER's transport panel REAPER will call the SetPlayState() function of
// this control surface class and we will switch on the play light on the
// actual hardware.
//
// Additionally there is a Run() function that will be called periodically by
// REAPER. This is the place where we check for state changes on the actual
// hardware (button presses, fader moves etc) and react accordingly. So if we
// detect that someone has pressed the stop button on the controller we will
// call REAPER's CSurf_OnStop() function and REAPER will stop playing.
//
// Of course things can get very complex fast when a lot of buttons, displays,
// faders, encoders and tracks are involved but this is the basic idea.
///////////////////////////////////////////////////////////////////////////////
class CC121 :
  public IReaperControlSurface
{
public:
  /////////////////////////////////////////////////////////////////////////////
  // CC121::CC121()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Default constructor of this class.
  // Parameters:   None.
  // Return value: No return value.
  // Remarks:      Explicit construction is required so this constructor will
  //               throw an exception.
  /////////////////////////////////////////////////////////////////////////////
  CC121();

  /////////////////////////////////////////////////////////////////////////////
  // CC121::~CC121()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Destructor of this class.
  // Parameters:   None.
  // Return value: No return value.
  // Remarks:      Closes the MIDI input and MIDI output and frees all
  //               allocated resources.
  /////////////////////////////////////////////////////////////////////////////
  virtual ~CC121();

  /////////////////////////////////////////////////////////////////////////////
  // CC121::GetTypeString()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Retrieve the type ID of this control surface.
  // Parameters:   None.
  // Return value: Returns the type ID as string.
  // Remarks:      This ID seems to be used to select the right surface and to
  //               load and store configurations. This has to be a simple
  //               unique string with only A-Z, 0-9, no spaces or other chars.
  /////////////////////////////////////////////////////////////////////////////
  const char* GetTypeString();

  /////////////////////////////////////////////////////////////////////////////
  // CC121::GetDescString()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Retrieve the human readable description of this surface.
  // Parameters:   None.
  // Return value: Returns the description as string.
  // Remarks:      This description is displayed in the control surface dialog
  //               box. Instance specific informations are allowed here.
  /////////////////////////////////////////////////////////////////////////////
  const char* GetDescString();

  /////////////////////////////////////////////////////////////////////////////
  // CC121::GetConfigString()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Retrieve the configuration of this surface.
  // Parameters:   None.
  // Return value: Returns the current configuration as string.
  // Remarks:      This configuration string is actually stored in REAPER's
  //               configuration.
  /////////////////////////////////////////////////////////////////////////////
  const char* GetConfigString();

  /////////////////////////////////////////////////////////////////////////////
  // CC121::CloseNoReset()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Close this surface without resetting the state.
  // Parameters:   None.
  // Return value: No return value.
  // Remarks:      Close without sending "reset" messages, prevent "reset"
  //               being sent on destructor. 
  /////////////////////////////////////////////////////////////////////////////
  void CloseNoReset();

  /////////////////////////////////////////////////////////////////////////////
  // CC121::Run()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The main message handling routine.
  // Parameters:   None.
  // Return value: No return value.
  // Remarks:      This one is called 30 times a sec or so.
  /////////////////////////////////////////////////////////////////////////////
  void Run();

  /////////////////////////////////////////////////////////////////////////////
  // CC121::SetTrackListChange()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Notification that the track list has changed.
  // Parameters:   None.
  // Return value: No return value.
  // Remarks:      This surface has only one channel so we don't care.
  /////////////////////////////////////////////////////////////////////////////
  void SetTrackListChange();

  /////////////////////////////////////////////////////////////////////////////
  // CC121::SetSurfaceVolume()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Notification that the volume of a track has changed.
  // Parameters:   [in] trackid: The track changed.
  //               [in] volume:  The new volume.
  // Return value: No return value.
  // Remarks:      Updates the fader of the surface.
  /////////////////////////////////////////////////////////////////////////////
  void SetSurfaceVolume(MediaTrack* trackid, double volume);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::SetSurfacePan()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Notification that the pan of a track has changed.
  // Parameters:   [in] trackid: The track changed.
  //               [in] pan:     The new pan.
  // Return value: No return value.
  // Remarks:      Updates the fader of the surface.
  /////////////////////////////////////////////////////////////////////////////
  void SetSurfacePan(MediaTrack* trackid, double pan);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::SetSurfaceMute()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Notification that the mute state of a track has changed.
  // Parameters:   [in] trackid: The track changed.
  //               [in] mute:    New mute state of the track.
  // Return value: No return value.
  // Remarks:      Just updates the light on the surface.
  /////////////////////////////////////////////////////////////////////////////
  void SetSurfaceMute(MediaTrack* trackid, bool mute);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::SetSurfaceSelected()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Sets the selection state of a track.
  // Parameters:   [in] trackid:  The track changed.
  //               [in] selected: New selection state of the track.
  // Return value: No return value.
  // Remarks:      Validates the selection state of this surface.
  /////////////////////////////////////////////////////////////////////////////
  void SetSurfaceSelected(MediaTrack* trackid, bool selected);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::SetSurfaceSolo()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Notification that the solo state of a track has changed.
  // Parameters:   [in] trackid: The track changed.
  //               [in] solo:    New solo state of the track.
  // Return value: No return value.
  // Remarks:      Just updates the light on the surface.
  /////////////////////////////////////////////////////////////////////////////
  void SetSurfaceSolo(MediaTrack* trackid, bool solo);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::SetSurfaceRecArm()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Notification that the record arm of a track has changed.
  // Parameters:   [in] trackid: The track changed.
  //               [in] recarm:  New record arm state of the track.
  // Return value: No return value.
  // Remarks:      Just updates the light on the surface.
  /////////////////////////////////////////////////////////////////////////////
  void SetSurfaceRecArm(MediaTrack* trackid, bool recarm);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::SetPlayState()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Notification about a change in transport state.
  // Parameters:   [in] play:  Current state of the play button.
  //               [in] pause: Current state of the pause button.
  //               [in] rec:   Current state of the record button.
  // Return value: No return value.
  // Remarks:      Updates the corresponding lights on the surface.
  /////////////////////////////////////////////////////////////////////////////
  void SetPlayState(bool play, bool pause, bool rec);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::SetRepeatState()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Notification that the repeat state has changed.
  // Parameters:   [in] bool: New repeat state.
  // Return value: No return value.
  // Remarks:      Just updates the light on the surface.
  /////////////////////////////////////////////////////////////////////////////
  void SetRepeatState(bool rep);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::SetTrackTitle()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Notification that the name of a track has changed.
  // Parameters:   [in] trackid: The track changed.
  //               [in] title:   New title of the track.
  // Return value: No return value.
  // Remarks:      We do nothing as we don't have a place to display this info.
  /////////////////////////////////////////////////////////////////////////////
  void SetTrackTitle(MediaTrack* trackid, const char* title);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::GetTouchState()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Request for the current touch states of the faders.
  // Parameters:   [in] trackid: The track in question.
  //               [in] isPan:   Check pan fader?
  // Return value: Returns true if the fader currently has been touched.
  // Remarks:      This is used for automation to detect fader moves. Only the
  //               volume fader is supported now.
  /////////////////////////////////////////////////////////////////////////////
  bool GetTouchState(MediaTrack* trackid, int isPan);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::SetRepeatState()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Notification that the automation mode has changed.
  // Parameters:   [in] mode: New automation.
  // Return value: No return value.
  // Remarks:      Uses the currently selected track as this parameter seems to
  //               be missing here. Just updates the automation lights on the
  //               surface.
  /////////////////////////////////////////////////////////////////////////////
  void SetAutoMode(int mode);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::ResetCachedVolPanStates()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Request to reset internal control states.
  // Parameters:   None.
  // Return value: No return value.
  // Remarks:      Good to flush your control states.
  /////////////////////////////////////////////////////////////////////////////
  void ResetCachedVolPanStates();

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnTrackSelection()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Notify the extension about newly selected track.
  // Parameters:   None.
  // Return value: No return value.
  // Remarks:      Updates the internal selection states.
  /////////////////////////////////////////////////////////////////////////////
  void OnTrackSelection(MediaTrack* trackid);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::IsKeyDown()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Request for the state of the special keys on a surface.
  // Parameters:   [in] key: The key in question.
  // Return value: Returns true if the key is down or false otherwise.
  // Remarks:      The key may be VK_CONTROL, VK_MENU, VK_SHIFT, etc, whatever
  //               makes sense for a surface.
  /////////////////////////////////////////////////////////////////////////////
  bool IsKeyDown(int key);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::Extended()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Extended command handling.
  // Parameters:   [in]     call:  Message ID.
  //               [in/out] parm1: First message parameter.
  //               [in/out] parm2: Second message parameter.
  //               [in/out] parm3: Third message parameter.
  // Return value: Returns a value depending on the call ID. Returning zero
  //               means that the call is not supported.
  // Remarks:      There are no public defined message IDs yet so this function
  //               always returns zero.
  /////////////////////////////////////////////////////////////////////////////
  int Extended(int call, void* parm1, void* parm2, void* parm3);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::CreateInstance()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Create an instance of this plugin.
  // Parameters:   [in]  typeString:   Denotes the type of plugin to create (in
  //                                   case an extension has registered more
  //                                   plugins with this creation function).
  //               [in]  configString: Configuration to use as string.
  //               [out] errStats:     Returns error statistics back to REAPER.
  //                                   Bit 0 denotes an input error and bit 1
  //                                   an output error.
  // Return value: Returns the newly created instance.
  // Remarks:      This function will be registered to REAPER when the plugin
  //               gets loaded. REAPER calls this function whenever it needs an
  //               instance of this surface.
  /////////////////////////////////////////////////////////////////////////////
  static IReaperControlSurface* CreateInstance(const char* typeString, const char* configString, int* errStats);

private:

  /////////////////////////////////////////////////////////////////////////////
  // All the assignable buttons of the surface:
  /////////////////////////////////////////////////////////////////////////////
  typedef enum ButtonState
  {
    ButtonNone     = 0,        // No button.
    ButtonRecArm   = 1 <<  0,  // Note number: 0x00, light: yes.
    ButtonSolo     = 1 <<  1,  // Note number: 0x08, light: yes.
    ButtonMute     = 1 <<  2,  // Note number: 0x10, light: yes.
    ButtonPrev     = 1 <<  3,  // Note number: 0x30, light: no.
    ButtonNext     = 1 <<  4,  // Note number: 0x31, light: no.
    ButtonEdit     = 1 <<  5,  // Note number: 0x33, light: yes.
    ButtonFunc1    = 1 <<  6,  // Note number: 0x36, light: yes.
    ButtonFunc2    = 1 <<  7,  // Note number: 0x37, light: yes.
    ButtonFunc3    = 1 <<  8,  // Note number: 0x38, light: yes.
    ButtonFunc4    = 1 <<  9,  // Note number: 0x39, light: yes.
    ButtonEncoder  = 1 << 10,  // Note number: 0x3A, light: no.
    ButtonFootSW   = 1 << 11,  // Note number: 0x3B, light: no.
    ButtonRead     = 1 << 12,  // Note number: 0x4A, light: yes.
    ButtonWrite    = 1 << 13,  // Note number: 0x4B, light: yes.
    ButtonLoop     = 1 << 14,  // Note number: 0x56, light: yes.
    ButtonToStart  = 1 << 15,  // Note number: 0x58, light: no.
    ButtonToEnd    = 1 << 16,  // Note number: 0x5A, light: no.
    ButtonRewind   = 1 << 17,  // Note number: 0x5B, light: no.
    ButtonForward  = 1 << 18,  // Note number: 0x5C, light: no.
    ButtonStop     = 1 << 19,  // Note number: 0x5A, light: no.
    ButtonPlay     = 1 << 20,  // Note number: 0x5E, light: yes.
    ButtonRecord   = 1 << 21,  // Note number: 0x5F, light: yes.
    ButtonEQ1      = 1 << 22,  // Note number: 0x70, light: yes.
    ButtonEQ2      = 1 << 23,  // Note number: 0x71, light: yes.
    ButtonEQ3      = 1 << 24,  // Note number: 0x72, light: yes.
    ButtonEQ4      = 1 << 25,  // Note number: 0x73, light: yes.
    ButtonEQType   = 1 << 26,  // Note number: 0x74, light: yes.
    ButtonEQBypass = 1 << 27,  // Note number: 0x75, light: yes.
    ButtonJog      = 1 << 28,  // Note number: 0x76, light: yes.
    ButtonLock     = 1 << 29,  // Note number: 0x77, light: yes.
    ButtonMonitor  = 1 << 30,  // Note number: 0x78, light: yes.
    ButtonVSTi     = 1 << 31,  // Note number: 0x79, light: yes.
  } ButtonState;

  /////////////////////////////////////////////////////////////////////////////
  // The operation modes of the EQ knobs:
  /////////////////////////////////////////////////////////////////////////////
  typedef enum EQEditMode
  {
    EqEditBands        = 0, // Edit Q, F and G of the respective band.
    EqEditType         = 1, // Selecting the EQ type with the G knob.
    EqEditQuickControl = 2  // 'ON' control send 1-4 mute, G 1-4 control send
                            // levels and Q 1 to F 4 control FX parameters.
  } EQEditMode;

  /////////////////////////////////////////////////////////////////////////////
  // CC121::CC121()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Copy constructor of this class.
  // Parameters:   [in] other: The source instance to copy.
  // Return value: No return value.
  // Remarks:      Copying this class is prohibited and explicit construction
  //               is required so this constructor is declared as private.
  /////////////////////////////////////////////////////////////////////////////
  CC121(const CC121& other);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::CC121()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Initialization constructor of this class.
  // Parameters:   [in]  opt:      Options for this control surface.
  //               [out] errStats: Returns error stats to REAPER.
  // Return value: No return value.
  // Remarks:      Initializes the members and opens the required MIDI input
  //               and MIDI output.
  /////////////////////////////////////////////////////////////////////////////
  CC121(const CC121Options& opt, int* errStats);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::operator = ()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Assignment operator of this class.
  // Parameters:   [in] other: The source instance to copy.
  // Return value: No return value.
  // Remarks:      Copying this class is prohibited and explicit construction
  //               is required so this operator is declared as private too.
  /////////////////////////////////////////////////////////////////////////////
  void operator = (const CC121& other);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::UpdateDevice()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Internal helper function to update the status of the device.
  // Parameters:   [in] lts: New state of the lights (see enum above).
  //               [in] vol: 
  // Return value: No return value.
  // Remarks:      This function is all that is needed to set the state of the
  //               surface.
  /////////////////////////////////////////////////////////////////////////////
  void UpdateDevice(const ButtonState lts, const int vol);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::SwitchLight()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Set the state of a specific light on the control surface.
  // Parameters:   [in] which: Light to toggle (for values see enum above).
  //               [in] on:    Turn this light on or off?
  // Return value: No return value.
  // Remarks:      This is just a shortcut to the UpdateDevice function above.
  //               The state of the light is changed immediately.
  /////////////////////////////////////////////////////////////////////////////
  void SwitchLight(const ButtonState which, const bool on);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::CheckRunningStatus()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Sends a message to the surface to ensure 'Cubase Ready'.
  // Parameters:   [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      The CC121 will need to receive this message once a second to
  //               remain operational. If the message has not been sent for
  //               about three seconds then the device will turn off the lights
  //               and the 'Cubase Ready' LED starts to blink.
  /////////////////////////////////////////////////////////////////////////////
  void CheckRunningStatus(const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::SetButtonState()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Set the up/down state of a device button.
  // Parameters:   [in] which: ID of the button to modify.
  //               [in] down:  Is it pressed?
  // Return value: No return value.
  // Remarks:      The button state is usually toggled by note-on and note-off
  //               messages.
  /////////////////////////////////////////////////////////////////////////////
  void SetButtonState(const ButtonState which, const bool down);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::GetButtonState()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Retrieve the up/down state of a device button.
  // Parameters:   [in] which: ID of the button to check.
  // Return value: Returns the current pressed state of the requested button.
  // Remarks:      The button state is usually toggled by note-on and note-off
  //               messages.
  /////////////////////////////////////////////////////////////////////////////
  bool GetButtonState(const ButtonState which) const;

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_RecArm()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Record Arm button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_RecArm(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Solo()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Solo button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Solo(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Mute()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Mute button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Mute(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Prev()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Previous Channel button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Prev(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Next()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Next Channel button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Next(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Edit()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The 'e' button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Edit(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Func1()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Function 1 button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Func1(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Func2()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Function 2 button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Func2(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Func3()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Function 3 button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Func3(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Func4()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Function 4 button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Func4(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Encoder()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Value Encoder button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Encoder(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_FootSW()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The foot switch has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_FootSW(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Read()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Automation Read button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Read(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Write()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Automation Write button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Write(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Loop()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Loop button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Loop(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_ToStart()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The To Start button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_ToStart(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_ToEnd()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The To End button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_ToEnd(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Rewind()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Rewind button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Rewind(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Forward()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Forward button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Forward(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Stop()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Stop button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Stop(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Play()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Play button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Play(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Record()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Record button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Record(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_EQ()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  One of the EQ buttons has been pressed or released.
  // Parameters:   [in] number:  Number of the button (0, 1, 2 or 3).
  //               [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_EQ(const int number, const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_EQType()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The EQ Type button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_EQType(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_EQBypass()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The All Bypass button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_EQBypass(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Jog()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Jog button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Jog(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Lock()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Lock button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Lock(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Monitor()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The Input Monitor button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Monitor(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_VSTi()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The VSTi button has been pressed or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_VSTi(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Btn_Fader()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The fader has been touched or released.
  // Parameters:   [in] down:    True if pressed, false if released.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Btn_Fader(const bool down, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Enc_Pan()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The pan encoder has been moved.
  // Parameters:   [in] amount:  Amount of clicks, already accelerated.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device. The number of clicks has already been
  //               scaled by the AccelRotary() function.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Enc_Pan(const double amount, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Enc_EQ()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  One of the EQ encoders has been moved.
  // Parameters:   [in] amount:  Amount of clicks, already accelerated.
  //               [in] row:     Encoder row (0 = Q, 1 = F, 2 = G).
  //               [in] chan:    Encoder channel (0, 1, 2, 3)
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device. The number of clicks has already been
  //               scaled by the AccelRotary() function.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Enc_EQ(const double amount, const int row, const int chan, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Enc_Value()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The value encoder has been moved.
  // Parameters:   [in] amount:  Amount of clicks, already accelerated.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device. The number of clicks has already been
  //               scaled by the AccelRotary() function.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Enc_Value(const double amount, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Enc_Jog()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The jog/AI encoder has been moved.
  // Parameters:   [in] amount:  Amount of clicks, already accelerated.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device. The number of clicks has already been
  //               scaled by the AccelRotary() function.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Enc_Jog(const double amount, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnCC121Fader_Move()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  The pan fader has been moved.
  // Parameters:   [in] amount:  Fader position as 14 bit (pitch bend) value.
  //               [in] curTime: Time stamp of the event.
  // Return value: No return value.
  // Remarks:      Called when the appropriate MIDI message is received from
  //               the hardware device.
  /////////////////////////////////////////////////////////////////////////////
  void OnCC121Fader_Move(const int amount, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::OnMIDIEvent()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Internal helper function to handle incomming MIDI events.
  // Parameters:   [in] evt:     The event to decode.
  //               [in] curTime: Current time.
  // Return value: No return value.
  // Remarks:      This is the function where the actual messages of the
  //               hardware are decoded and dispatched.
  /////////////////////////////////////////////////////////////////////////////
  void OnMIDIEvent(const MIDI_event_t* evt, const DWORD curTime);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::SkipTracks()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Internal helper function to move from track to track.
  // Parameters:   [in] amt:   Amount of tracks to move, may be negative.
  //               [in] dosel: Select the choosen track.
  // Return value: No return value.
  // Remarks:      The wrap around mode is controlled by the options.
  /////////////////////////////////////////////////////////////////////////////
  void SkipTracks(const int amt, const bool dosel);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::int14ToVol()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Convert a 14 bit value to a REAPER compatible volume value.
  // Parameters:   [in] val: The 14 bit value.
  // Return value: Returns the converted volume.
  // Remarks:      The volume value reaches from -oo to +24dB. This version
  //               differs from the original SDK version as it introduces an
  //               offset to hit the 0 dB marker on the actual control surface.
  /////////////////////////////////////////////////////////////////////////////
  static double int14ToVol(const int val);

  /////////////////////////////////////////////////////////////////////////////
  // CC121::volToInt14()
  /////////////////////////////////////////////////////////////////////////////
  // Description:  Convert a REAPER volume value to a 14 bit MSB/LSB value.
  // Parameters:   [in] vol: The volume value.
  // Return value: Returns the converted volume.
  // Remarks:      The volume value reaches from -oo to +24dB. This version
  //               differs from the original SDK version as it introduces an
  //               offset to hit the 0 dB marker on the actual control surface.
  /////////////////////////////////////////////////////////////////////////////
  static int volToInt14(const double vol);

  ///////////////////////////////////////////////////////////////////////////////
  // CC121::SetLastTouchedTrack()
  ///////////////////////////////////////////////////////////////////////////////
  // Description:  Internal helper to set the active track for REAPER's actions.
  // Parameters:   [in] tr: The target track.
  // Return value: No return value.
  // Remarks:      Many of REPEAR's actions which are invoked with Main_OnCommand
  //               work on the track that has been touched last by the user and
  //               not the selected tracks.
  ///////////////////////////////////////////////////////////////////////////////
  void SetLastTouchedTrack(MediaTrack* tr) const;

  ///////////////////////////////////////////////////////////////////////////////
  // CC121::AccelRotary()
  ///////////////////////////////////////////////////////////////////////////////
  // Description:  Convert encoder speed to value change.
  // Parameters:   [in] speed: The rotation speed as reported by the device.
  // Return value: Returns the rotation speed as accellerated value.
  // Remarks:      Uses an exponential curve to create a reasonable feel on the
  //               encoder.
  ///////////////////////////////////////////////////////////////////////////////
  double AccelRotary(const int speed) const;

  ///////////////////////////////////////////////////////////////////////////////
  // CC121::LoadEqMaps()
  ///////////////////////////////////////////////////////////////////////////////
  // Description:  Load the EQ map from file.
  // Parameters:   None.
  // Return value: No return value.
  // Remarks:      EQ maps have a *.eqmap extension. This functions searches for
  //               maps under $REAPERDIR/Data/CC121/*.
  ///////////////////////////////////////////////////////////////////////////////
  void LoadEqMaps();

  ///////////////////////////////////////////////////////////////////////////////
  // CC121::FindTrackEQ()
  ///////////////////////////////////////////////////////////////////////////////
  // Description:  Searches the FX list of the current track for a supported EQ.
  // Parameters:   [in]  trackID: ID of the source track.
  //               [out] fxid:    At exit: ID of the FX in the list of the track.
  // Return value: Returns the requested parameter map or 0 if no map was found.
  // Remarks:      The parameter map is only for the first EQ on the track.
  ///////////////////////////////////////////////////////////////////////////////
  const CC121EqMap* FindTrackEQ(const int trackID, int& fxid) const;

  ///////////////////////////////////////////////////////////////////////////////
  // Member:
  ///////////////////////////////////////////////////////////////////////////////

  // Controller options:
  CC121Options options;

  // The actual MIDI input device:
  midi_Input* midiIn;

  // The actual MIDI output device:
  midi_Output* midiOut;

  // Current track:
  int currentTrackID;

  // Current state of the buttons:
  ButtonState buttonState;

  // Current state of the lights:
  ButtonState lightState;

  // Current EQ edit mode:
  EQEditMode eqEditMode;

  // Cached 14 bit fader value:
  int faderState;

  // Motorfader turned off?
  bool faderOff;

  // Fader function swapped?
  bool faderSwapped;

  // Current paused state:
  bool paused;

  // Currently touching the fader?
  bool touching;

  // Time stamp of the last running status event:
  DWORD lastAISendTime;

  // Last time we checked the state of the track monitor:
  DWORD lastMonChkTime;

  // Last time we checked the state of the track EQ:
  DWORD lastEQChkTime;

  // Last time we checked the state of the track FX:
  DWORD lastFXChkTime;

  // Last time we checked the state of the track IO:
  DWORD lastIOChkTime;

  // Last time we checked the state of the blinking lights:
  DWORD lastBlinkTime;

  // Temporary value to measure time between two button clicks:
  DWORD lastDblButtonTime;

  // Current jog mode:
  bool jogOn;

  // EQ parameter maps:
  std::vector<CC121EqMap> eqMaps;
};

#endif // #ifndef __CC121_H_INCLUDED__
///////////////////////////////// End of File /////////////////////////////////
