///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// File:           ConfigDialogs.cpp                                         //
//                                                                           //
// Functionality:  Configuration dialog implementation.                      //
//                                                                           //
// 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.              //
///////////////////////////////////////////////////////////////////////////////
#include "reaper_cc121.h"

///////////////////////////////////////////////////////////////////////////////
// actionDlgProc()
///////////////////////////////////////////////////////////////////////////////
// Description:  Window procedure for the action configuration dialog.
// Parameters:   [in] hwndDlg: Window handle of the dialog.
//               [in] uMsg:    Message ID.
//               [in] wParam:  Message dependent parameter.
//               [in] lParam:  Message dependent parameter.
// Return value: Message dependent.
// Remarks:      Look into the MS platform SDK to find out more.
///////////////////////////////////////////////////////////////////////////////
static INT_PTR CALLBACK actionDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  // Initialization of the dialog, lParam points to the current configuration:
  if (uMsg == WM_INITDIALOG)
  {
    // Get option set and attach it to the window:
    CC121Options* opt = reinterpret_cast<CC121Options*>(lParam);
    #ifdef _WIN64
    SetWindowLongPtr(hwndDlg, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(opt));
    #else
    SetWindowLongPtr(hwndDlg, GWLP_USERDATA, static_cast<LONG>(reinterpret_cast<LONG_PTR>(opt)));
    #endif
    if (!opt)
      return 0;

    // Init controls:
    SetWindowTextA(GetDlgItem(hwndDlg, IDC_EDIT_VALUE_ENC), opt->valueEncCmd.c_str());
    SetWindowTextA(GetDlgItem(hwndDlg, IDC_EDIT_VALUE_BTN), opt->valueBtnCmd.c_str());
    SetWindowTextA(GetDlgItem(hwndDlg, IDC_EDIT_FUNC1_BTN), opt->user1BtnCmd.c_str());
    SetWindowTextA(GetDlgItem(hwndDlg, IDC_EDIT_FUNC2_BTN), opt->user2BtnCmd.c_str());
    SetWindowTextA(GetDlgItem(hwndDlg, IDC_EDIT_FUNC3_BTN), opt->user3BtnCmd.c_str());
    SetWindowTextA(GetDlgItem(hwndDlg, IDC_EDIT_FUNC4_BTN), opt->user4BtnCmd.c_str());
    SetWindowTextA(GetDlgItem(hwndDlg, IDC_EDIT_FT_SWITCH), opt->ftSwitchCmd.c_str());
  }

  // Actions button has been clicked:
  else if (uMsg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ACTIONS)
  {
    // Show action dialog:
    Main_OnCommand(ID_SHOW_ACTION_LIST, 0);
  }

  // Cancel button has been clicked:
  else if (uMsg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL)
  {
    // Close the dialog:
    EndDialog(hwndDlg, IDCANCEL);
  }

  // OK button has been clicked:
  else if (uMsg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK)
  {
    // Close the dialog:
    EndDialog(hwndDlg, IDOK);

    // Get the initial option set that is attached to the window:
    CC121Options* opt = reinterpret_cast<CC121Options*>(static_cast<LONG_PTR>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA)));
    if (!opt)
      return 0;

    // Update option set:
    char buff[256];
    if (GetWindowTextA(GetDlgItem(hwndDlg, IDC_EDIT_VALUE_ENC), buff, sizeof(buff) / sizeof(buff[0])))
      opt->valueEncCmd = std::string(buff);
    if (GetWindowTextA(GetDlgItem(hwndDlg, IDC_EDIT_VALUE_BTN), buff, sizeof(buff) / sizeof(buff[0])))
      opt->valueBtnCmd = std::string(buff);
    if (GetWindowTextA(GetDlgItem(hwndDlg, IDC_EDIT_FUNC1_BTN), buff, sizeof(buff) / sizeof(buff[0])))
      opt->user1BtnCmd = std::string(buff);
    if (GetWindowTextA(GetDlgItem(hwndDlg, IDC_EDIT_FUNC2_BTN), buff, sizeof(buff) / sizeof(buff[0])))
      opt->user2BtnCmd = std::string(buff);
    if (GetWindowTextA(GetDlgItem(hwndDlg, IDC_EDIT_FUNC3_BTN), buff, sizeof(buff) / sizeof(buff[0])))
      opt->user3BtnCmd = std::string(buff);
    if (GetWindowTextA(GetDlgItem(hwndDlg, IDC_EDIT_FUNC4_BTN), buff, sizeof(buff) / sizeof(buff[0])))
      opt->user4BtnCmd = std::string(buff);
    if (GetWindowTextA(GetDlgItem(hwndDlg, IDC_EDIT_FT_SWITCH), buff, sizeof(buff) / sizeof(buff[0])))
      opt->ftSwitchCmd = std::string(buff);
  }

  // Done:
  return 0;
}

///////////////////////////////////////////////////////////////////////////////
// editDlgProc()
///////////////////////////////////////////////////////////////////////////////
// Description:  Window procedure for the configuration dialog.
// Parameters:   [in] hwndDlg: Window handle of the dialog.
//               [in] uMsg:    Message ID.
//               [in] wParam:  Message dependent parameter.
//               [in] lParam:  Message dependent parameter.
// Return value: Message dependent.
// Remarks:      Look into the MS platform SDK to find out more.
///////////////////////////////////////////////////////////////////////////////
static INT_PTR CALLBACK editDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  // Initialization of the dialog, lParam points to the configuration string:
  if (uMsg == WM_INITDIALOG)
  {
    // Create temporary option set and attach it to the window:
    CC121Options* opt = new CC121Options(reinterpret_cast<const char*>(lParam));
    #ifdef _WIN64
    SetWindowLongPtr(hwndDlg, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(opt));
    #else
    SetWindowLongPtr(hwndDlg, GWLP_USERDATA, static_cast<LONG>(reinterpret_cast<LONG_PTR>(opt)));
    #endif

    // Init skip markers check box:
    CheckDlgButton(hwndDlg, IDC_CHK_SKIP_MARKERS, opt->skipToStartEnd);

    // Init rotary speed:
    TCHAR buff[512];
    sprintf(buff, "%d", opt->rotarySpeed);
    SetWindowText(GetDlgItem(hwndDlg, IDC_EDIT_ENC_SPEED), buff);


    // Update spin control bounds and value:
    SendDlgItemMessage(hwndDlg, IDC_SPIN_ENC_SPEED, UDM_SETRANGE32, 1, 99);
    SendDlgItemMessage(hwndDlg, IDC_SPIN_ENC_SPEED, UDM_SETPOS32, 0, opt->rotarySpeed);

    // Add an 'unassigned' entry to the MIDI input list:
    LRESULT itemPos = SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_INPUT, CB_ADDSTRING, 0, (LPARAM)_T("None"));
    SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_INPUT, CB_SETITEMDATA, itemPos, -1);
    SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_INPUT, CB_SETCURSEL, itemPos, 0);

    // Loop through MIDI inputs:
    int numDevs = GetNumMIDIInputs();
    for (int i = 0; i < numDevs; i++)
    {
      // Get name of the device:
      if (GetMIDIInputName(i, buff, sizeof(buff) / sizeof(buff[0])))
      {
        // Add item to the combo box:
        itemPos = SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_INPUT, CB_ADDSTRING, 0, (LPARAM)buff);
        SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_INPUT, CB_SETITEMDATA, itemPos, i);

        // Select the entry if it represents the current device:
        if (i == opt->midiInDev)
          SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_INPUT, CB_SETCURSEL, itemPos, 0);
      }
    }

    // Add an 'unassigned' entry to the MIDI output list:
    itemPos = SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_OUTPUT, CB_ADDSTRING, 0, (LPARAM)"None");
    SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_OUTPUT, CB_SETITEMDATA, itemPos, -1);
    SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_OUTPUT, CB_SETCURSEL, itemPos, 0);

    // Loop through MIDI outputs:
    numDevs = GetNumMIDIOutputs();
    for (int i = 0; i < numDevs; i ++)
    {
      // Get name of the device:
      if (GetMIDIOutputName(i, buff, sizeof(buff) / sizeof(buff[0])))
      {
        // Add item to the combo box:
        itemPos = SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_OUTPUT, CB_ADDSTRING, 0, (LPARAM)buff);
        SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_OUTPUT, CB_SETITEMDATA, itemPos, i);

        // Select the entry if it represents the current device:
        if (i == opt->midiOutDev)
          SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_OUTPUT, CB_SETCURSEL, itemPos, 0);
      }
    }

    // Init wrap around and mcp check boxes:
    CheckDlgButton(hwndDlg, IDC_CHK_WRAP_AROUND, opt->wrapAround);
    CheckDlgButton(hwndDlg, IDC_CHK_USE_MCP,     opt->useMCP);

    // Init rotary acceleration:
    sprintf(buff, "%d", opt->rotaryAccel);
    SetWindowText(GetDlgItem(hwndDlg, IDC_EDIT_ENC_ACCEL), buff);

    // Update spin control bounds and value:
    SendDlgItemMessage(hwndDlg, IDC_SPIN_ENC_ACCEL, UDM_SETRANGE32, 1, 200);
    SendDlgItemMessage(hwndDlg, IDC_SPIN_ENC_ACCEL, UDM_SETPOS32, 0, opt->rotaryAccel);
  }

  // Dialog closing:
  else if (uMsg == WM_DESTROY)
  {
    // Remove the memory that is attached to the window:
    CC121Options* opt = reinterpret_cast<CC121Options*>(static_cast<LONG_PTR>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA)));
    if (opt)
      delete opt;
    SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
  }

  // Dialog item handling:
  else if (uMsg == WM_COMMAND)
  {
    // Check contents of the speed edit box:
    if (HIWORD(wParam) == EN_KILLFOCUS && LOWORD(wParam) == IDC_EDIT_ENC_SPEED)
    {
      // Get the text:
      TCHAR buff[256];
      if (SendMessage((HWND)lParam, WM_GETTEXT, sizeof(buff) / sizeof(buff[0]), (LPARAM)buff) > 0)
      {
        // Get value:
        int val = _tstoi(buff);

        // Update display on error:
        if (val < 1)
          SetWindowText((HWND)lParam, _T("1"));
        else if (val > 99)
          SetWindowText((HWND)lParam, _T("99"));
      }

      // Text retrieval was not successfull:
      else
        SetWindowText((HWND)lParam, _T("31"));
    }

    // Check contents of the acceleration edit box:
    else if (HIWORD(wParam) == EN_KILLFOCUS && LOWORD(wParam) == IDC_EDIT_ENC_ACCEL)
    {
      // Get the text:
      TCHAR buff[256];
      if (SendMessage((HWND)lParam, WM_GETTEXT, sizeof(buff) / sizeof(buff[0]), (LPARAM)buff) > 0)
      {
        // Get value:
        int val = _tstoi(buff);

        // Update display on error:
        if (val < 1)
          SetWindowText((HWND)lParam, _T("1"));
        else if (val > 200)
          SetWindowText((HWND)lParam, _T("200"));
      }

      // Text retrieval was not successfull:
      else
        SetWindowText((HWND)lParam, _T("10"));
    }

    else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_BTN_USER_FUNCS)
    {
      // Get the initial option set that is attached to the window:
      CC121Options* opt = reinterpret_cast<CC121Options*>(static_cast<LONG_PTR>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA)));
      if (!opt)
        return 0;

      // Create dialog from resource:
      DialogBoxParam(hResourceInst, MAKEINTRESOURCE(IDD_ACTIONEDIT_CC121), hwndDlg, actionDlgProc, reinterpret_cast<LPARAM>(opt));
    }
  }

  // User message, request configuration where wParam is the size of a text
  // buffer pointed to by lParam.
  else if (uMsg == (WM_USER + 1024))
  {
    // Parameter check:
    if (wParam <= 1 || lParam == NULL)
      return 0;

    // Get the initial option set that is attached to the window:
    CC121Options* opt = reinterpret_cast<CC121Options*>(static_cast<LONG_PTR>(GetWindowLongPtr(hwndDlg, GWLP_USERDATA)));
    if (!opt)
      return 0;

    // Skip markers:
    opt->skipToStartEnd = IsDlgButtonChecked(hwndDlg, IDC_CHK_SKIP_MARKERS) != 0;

    // Encoder speed:
    TCHAR buff[256];
    if (SendMessage(GetDlgItem(hwndDlg, IDC_EDIT_ENC_SPEED), WM_GETTEXT, sizeof(buff) / sizeof(buff[0]), (LPARAM)buff) > 0)
    {
      // Get value:
      opt->rotarySpeed = _tstoi(buff);

      // Check bounds:
      if (opt->rotarySpeed < 1)
        opt->rotarySpeed = 1;
      else if (opt->rotarySpeed > 99)
        opt->rotarySpeed = 99;
    }

    // MIDI in device:
    LRESULT itemPos = SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_INPUT, CB_GETCURSEL, 0, 0);
    if (itemPos != CB_ERR)
      opt->midiInDev = (int)SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_INPUT, CB_GETITEMDATA, itemPos, 0);

    // MIDI out device:
    itemPos = SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_OUTPUT,CB_GETCURSEL, 0, 0);
    if (itemPos != CB_ERR)
      opt->midiOutDev = (int)SendDlgItemMessage(hwndDlg, IDC_CB_MIDI_OUTPUT, CB_GETITEMDATA, itemPos, 0);

    // Wrap around and mcp check boxes:
    opt->useMCP     = IsDlgButtonChecked(hwndDlg, IDC_CHK_USE_MCP)     != 0;
    opt->wrapAround = IsDlgButtonChecked(hwndDlg, IDC_CHK_WRAP_AROUND) != 0;

    // Encoder acceleration:
    if (SendMessage(GetDlgItem(hwndDlg, IDC_EDIT_ENC_ACCEL), WM_GETTEXT, sizeof(buff) / sizeof(buff[0]), (LPARAM)buff) > 0)
    {
      // Get value:
      opt->rotaryAccel = _tstoi(buff);

      // Check bounds:
      if (opt->rotaryAccel < 1)
        opt->rotaryAccel = 1;
      else if (opt->rotaryAccel > 99)
        opt->rotaryAccel = 99;
    }

    // Create configuration string:
    opt->WriteConfig((char*)lParam);
  }

  // Done:
  return 0;
}

///////////////////////////////////////////////////////////////////////////////
// CreateConfigDialog()
///////////////////////////////////////////////////////////////////////////////
// Description:  Create a dialog to edit the options of this extension.
// Parameters:   [in] type_string:      The type of surface to configure, this
//                                      is always "CC121" in our case.
//               [in] parent:           Parent window of the dialog.
//               [in] initConfigString: The current configuration.
// Return value: Returns a handle to the newly created window.
// Remarks:      This dialog is embedded into the REAPER configuration dialog.
///////////////////////////////////////////////////////////////////////////////
HWND CreateConfigDialog(const char* type_string, HWND parent, const char* initConfigString)
{
  // Create dialog from resource:
  return CreateDialogParam(hResourceInst, MAKEINTRESOURCE(IDD_SURFACEEDIT_CC121), parent, editDlgProc, reinterpret_cast<LPARAM>(initConfigString));
}

///////////////////////////////// End of File /////////////////////////////////
