// **********************************************
// File: LAYOUT.CPP
// Layout dialog box functions

#include "muzika.h"
#include "dialog.h"
#include <stdio.h>
#include <stdlib.h>

BOOL scoreDisplay = FALSE;        // Initially single part; TRUE = score
int displayedPart = 0;            // The part being displayed
unsigned pixelsPerStaff = 65;     // Height of a staff in pixels
unsigned pixelsPerObject = 20;    // Width of an object in pixels

// ***************************************************
// DialogParts is the "Layout/Parts..." dialog box function,
// processing messages intended for this dialog box.

BOOL FAR PASCAL DialogParts(HWND hDlg, unsigned message, WORD wParam, LONG)
{
  switch (message) {
    case WM_INITDIALOG:
      // Process a WM_INITDIALOG message, indicating that the
      // DialogParts function is called for the first time.
      // The part list box is filled with names of the existing parts.
      for (int index = 0; index < melody.part.number(); ++index)
        SendDlgItemMessage(hDlg, ID_PARTLIST, LB_INSERTSTRING, -1,
          (LONG) ((Part *) &melody.part[index])->name());
      return TRUE;

    case WM_COMMAND:
      // Process a WM_COMMAND message, indicating an action
      // of some kind in the dialog box.
      switch (wParam) {
        case ID_NEWPART:
          // The user selected "New":
          // create a new part dialog box, using the DialogNewPart function
          FARPROC lpDialog = MakeProcInstance((FARPROC) DialogNewPart, hInst);
          if (DialogBox(hInst, "D_NEWPART", hDlg, lpDialog)) {
            SendDlgItemMessage(hDlg, ID_PARTLIST, LB_INSERTSTRING, -1,
              (LONG) ((Part *) &melody.part[melody.part.number()-1])->name());
            melodyModified = TRUE;
          }
          FreeProcInstance(lpDialog);
          break;

        case ID_REMOVEPART:
          // The user selected "Remove":
          // remove the selected part unless it is being displayed,
          // or is the last one.
          int currSel = (int) SendDlgItemMessage(hDlg, ID_PARTLIST,
            LB_GETCURSEL, 0, 0);
          if (currSel != LB_ERR)
            if (SendDlgItemMessage(hDlg, ID_PARTLIST, LB_GETCOUNT, 0, 0) > 1)
              if (displayedPart != currSel) {
                // See if the removed part contains anything
                if (((Part *) &melody.part[currSel])->staff.number())
                  if (MessageBox(hDlg, "Part is not empty. Remove anyway?",
                     "WARNING", MB_ICONEXCLAMATION | MB_YESNOCANCEL) != IDYES)
                     return TRUE;
                melody.part.destroyAt(currSel);
                SendDlgItemMessage(hDlg, ID_PARTLIST,
                  LB_DELETESTRING, currSel, 0);
                if (displayedPart > currSel)
                  --displayedPart;
                melodyModified = TRUE;
              }
              else
                // The selected part is the one being displayed
                MessageBox(hDlg, "Cannot remove the part being displayed",
                  NULL, MB_ICONSTOP | MB_OK);
            else
              // The selected part is the last one left
              MessageBox(hDlg, "Cannot remove the last part", NULL,
                MB_ICONSTOP | MB_OK);
          else
            // No selection has been made in the list box
            MessageBox(hDlg, "No part is currently selected", NULL,
              MB_ICONEXCLAMATION | MB_OK);
          break;

        case IDOK:
          // The user selected OK:
          // finish the dialog box
          EndDialog(hDlg, TRUE);
          break;
      }
      return TRUE;
  }

  return FALSE;
}

// **********************************************
// DialogPage is the "Layout/Page..." dialog box function,
// processing messages intended for this dialog box.

BOOL FAR PASCAL DialogPage(HWND hDlg, unsigned message, WORD wParam, LONG lParam)
{
  switch (message) {
    case WM_INITDIALOG:
      // Process a WM_INITDIALOG message, indicating that the
      // DialogPage function is called for the first time.
      // Various controls of the dialog box are initialized.
      SendDlgItemMessage(hDlg, scoreDisplay ? ID_SCORE : ID_SINGLEPART,
        BM_SETCHECK, 1, 0);
      for (int index = 0; index < melody.part.number(); ++index)
        SendDlgItemMessage(hDlg, ID_DISPLAYEDPART, CB_INSERTSTRING, -1,
          (LONG) ((Part *) &melody.part[index])->name());
      SendDlgItemMessage(hDlg, ID_DISPLAYEDPART,
        CB_SETCURSEL, displayedPart, 0);
      char numstring[4];
      SetDlgItemText(hDlg, ID_STAFFHEIGHT,
        itoa(pixelsPerStaff, numstring, 10));
      SetDlgItemText(hDlg, ID_OBJECTWIDTH,
        itoa(pixelsPerObject, numstring, 10));
      return TRUE;

    case WM_COMMAND:
      // Process a WM_COMMAND message, indicating an action
      // of some kind in the dialog box.
      switch (wParam) {
        case ID_SINGLEPART:
        case ID_SCORE:
          // The user has selected one of the display radio buttons:
          // check the appropriate button
          if (HIWORD(lParam) == BN_CLICKED)
            EnableWindow(GetDlgItem(hDlg, ID_DISPLAYEDPART),
              (wParam != ID_SCORE));
          return TRUE;

        case IDOK:
          // The user selected OK:
          // read the staff height and object width from the
          // edit controls, and update the current display
          scoreDisplay = (int) SendDlgItemMessage(hDlg, ID_SCORE,
            BM_GETCHECK, 0, 0);
          displayedPart = (int) SendDlgItemMessage(hDlg, ID_DISPLAYEDPART,
            CB_GETCURSEL, 0, 0);

          // Read the numerical values (staff height and object width)
          BOOL translated;
          int temp = GetDlgItemInt(hDlg, ID_STAFFHEIGHT, &translated, 0);
          if (temp >= 40) {
            pixelsPerStaff = temp;
            EndDialog(hDlg, TRUE);
          }
          else {
            MessageBox(hDlg, "Must have at least 40 pixels per staff", NULL,
              MB_ICONEXCLAMATION | MB_OK);
            SetDlgItemText(hDlg, ID_STAFFHEIGHT,
              itoa(pixelsPerStaff, numstring, 10));
          }
          temp = GetDlgItemInt(hDlg, ID_OBJECTWIDTH, &translated, 0);
          pixelsPerObject = temp;

          // Initialize the display parameters if necessary
          if (scoreDisplay) {
            // Calculate the score display parameters
            scoreFirstStaff = scoreFirstPart = 0;
            scoreMultiplicity = scoreStaves = 0;
            for (int index = 0; index < melody.part.number(); ++index) {
              Part &p = *((Part *) &melody.part[index]);
              scoreMultiplicity += p.multiplicity();
              if (p.staff.number()/p.multiplicity() > scoreStaves)
                scoreStaves = p.staff.number()/p.multiplicity();
            }
            SetScrollRange(hEditWnd, SB_VERT, 0, scoreStaves-1, TRUE);
            SetScrollPos(hEditWnd, SB_VERT, 0, TRUE);
          }
          else {
            // Prepare the scroll bar for the single part display
            Part &p = *((Part *) &melody.part[displayedPart]);
            SetScrollRange(hEditWnd, SB_VERT, 0, p.staff.number() ?
              ((Staff *) &p.staff[p.staff.number()-1])->Y() : 0, TRUE);
            SetScrollPos(hEditWnd, SB_VERT, p.GetPartY(), TRUE);
          }

          // Refresh the screen
          InvalidateRect(hEditWnd, NULL, TRUE);
          return TRUE;
      }
      return TRUE;
  }

  return FALSE;
}

// **********************************************
// DialogNewPart is the "Layout/Part.../New..." dialog box function,
// processing messages intended for this dialog box.

BOOL FAR PASCAL DialogNewPart(HWND hDlg, unsigned message, WORD wParam, LONG)
{
  char partName[MAXPARTNAME+1];

  switch(message) {
    case WM_INITDIALOG:
      // Process a WM_INITDIALOG message, indicating that the
      // DialogNewPart function is called for the first time.
      // The dialog box fields are initialized.
      SendDlgItemMessage(hDlg, ID_NEWPARTNAME, EM_LIMITTEXT, MAXPARTNAME, 0);
      SetDlgItemText(hDlg, ID_NEWPARTMULTIPLICITY, "1");
      return TRUE;

    case WM_COMMAND:
      // Process a WM_COMMAND, indicating that the user has selected
      // one of the finishing buttons (OK or CANCEL).
      if (wParam == IDOK) {
        // The user selected OK:
        // create the new part
        int newIndex = melody.part.number();
        BOOL translated;
        int mult = GetDlgItemInt(hDlg, ID_NEWPARTMULTIPLICITY, &translated, 0);
        if (mult >= 1 && mult <= MAXMULTIPLICITY) {
          // Multiplicity is within range:
          // create a new part and insert it in the part list
          if (GetDlgItemText(hDlg, ID_NEWPARTNAME, partName, MAXPARTNAME))
            melody.part.insertAt(*new Part(partName, mult), newIndex);
          EndDialog(hDlg, TRUE);
        }
        else {
          // Multiplicity entered by user was illegal
          char numstr[40];
          sprintf(numstr, "Staff multiplicity cannot exceed %d", MAXMULTIPLICITY);
          MessageBox(hDlg, numstr, NULL, MB_ICONEXCLAMATION | MB_OK);
        }
      }

      if (wParam == IDCANCEL)
        // The user selected CANCEL:
        // just exit from the dialog box, without creating any part.
        EndDialog(hDlg, FALSE);
      return TRUE;
  }

  return FALSE;
}
