// **********************************************
// File: SYMBOLS.CPP
// Symbol-treating module

#include "muzika.h"

BOOL editModeSymbolActive = TRUE; // TRUE = one of the three constant symbols

EDITMODE activeEditMode = PENCIL; // The active among the constant symbols

int activeSymbolSet = 1;          // The active symbol set on the screen left
int activeSymbol = 0;             // The active symbol within the symbol set
SymbolClass *currentSymbol = NULL; // The current symbol object

// **********************************************
// DisplayEditModeSymbols displays the edit mode symbols
// (i.e. the three constant symbols at the screen top).
// If one of them is selected, it is displayed in reverse video.

void DisplayEditModeSymbols(HDC hDC)
{
  HDC hBitmapDC = CreateCompatibleDC(hDC);
  HBITMAP hBitmap, hOldBitmap;
  COLORREF hOldColor, hOldBkColor;
  char name[9] = "B_";

  // Select a bitmap color
  hOldColor = SetTextColor(hDC, melodyExists ? 0 : GetSysColor(COLOR_GRAYTEXT));
  hOldBkColor = SetBkColor(hDC, GetSysColor(COLOR_WINDOW));

  // Draw the pencil, eraser, and hand bitmaps
  for (EDITMODE index = PENCIL; index <= HAND; ++index) {
    LoadString(hInst, EDITMODESTRINGS+index, name+2, sizeof(name)-2);
    hBitmap = LoadBitmap(hInst, name);
    hOldBitmap = SelectObject(hBitmapDC, hBitmap);

    // Put the bitmap in normal or reverse video
    if (melodyExists && editModeSymbolActive && index == activeEditMode)
      BitBlt(hDC, 2+36*index, 2, 32, 32, hBitmapDC, 0, 0, NOTSRCCOPY);
    else
      BitBlt(hDC, 2+36*index, 2, 32, 32, hBitmapDC, 0, 0, SRCCOPY);
    SelectObject(hBitmapDC, hOldBitmap);
    DeleteObject(hBitmap);
  }

  // Delete the unneeded display context
  SetTextColor(hDC, hOldColor);
  SetBkColor(hDC, hOldBkColor);
  DeleteDC(hBitmapDC);
}

// **********************************************
// IdentifyEditModeSymbol checks whether the given cursor position
// (on which the mouse has been clicked) is in the region of
// one of the edit mode symbols. If so, the symbol is marked as active.

BOOL IdentifyEditModeSymbol(DWORD position)
{
  HDC hDC;
  POINT pos = MAKEPOINT(position);
  char name[9] = "C_";

  // Don't identify anything if no melody
  if (!melodyExists)
    return FALSE;

  // See if any edit-mode symbol has been clicked
  if (pos.y >= 2 && pos.y <= 33)
    for (EDITMODE index = PENCIL; index <= HAND; ++index)
      if (pos.x >= 2+36*index && pos.x <= 33+36*index) {
        // Identification succeeded:
        // mark the required symbol as active
        // and redisplay the edit mode symbols
        editModeSymbolActive = TRUE;
        activeEditMode = index;
        hDC = GetDC(hMainWnd);
        DisplayEditModeSymbols(hDC);
        RefreshSymbols(hDC);
        ReleaseDC(hMainWnd, hDC);
        LoadString(hInst, EDITMODESTRINGS+index, name+2, sizeof(name)-2);
        hEditCursor = LoadCursor(hInst, name);
        return TRUE;
      }

  // Identification failed: return FALSE
  return FALSE;
}

// **********************************************
// RefreshSymbols redraws the current symbol set
// at the screen left, presumably after the user selected a new set.

void RefreshSymbols(HDC hDC)
{
  HDC hBitmapDC = CreateCompatibleDC(hDC);
  COLORREF hOldColor, hOldBkColor;
  HBRUSH hBrush, hOldBrush;

  // Select a bitmap color
  hOldColor = SetTextColor(hDC, melodyExists ? 0 : GetSysColor(COLOR_GRAYTEXT));
  hOldBkColor = SetBkColor(hDC, GetSysColor(COLOR_WINDOW));
  hBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
  hOldBrush = SelectObject(hDC, hBrush);

  // Load and display the symbol bitmaps
  int lastDrawn;
  for (int index = 0; symbolArray[index]->GetID() < 0x10*(activeSymbolSet+1);
    index++)
    if ((lastDrawn = symbolArray[index]->GetID()) >= 0x10*activeSymbolSet)
      symbolArray[index]->DrawSymbol(hDC, hBitmapDC,
        melodyExists && !editModeSymbolActive &&
          symbolArray[index]->GetID() == activeSymbol);

  // Fill the remaining slots with the empty symbol
  while (++lastDrawn % 0x10)
    nullSymbol.DrawSymbol(hDC, lastDrawn);

  // Delete the unnecessary Windows objects
  SelectObject(hDC, hOldBrush);
  DeleteObject(hBrush);
  SetTextColor(hDC, hOldColor);
  SetBkColor(hDC, hOldBkColor);
  DeleteDC(hBitmapDC);
}

// **********************************************
// IdentifySymbol checks whether the given cursor position
// (on which the mouse has been clicked) is in the region of
// one of the symbols on the screen left.

SymbolClass *IdentifySymbol(DWORD position)
{
  HDC hDC;
  POINT pos = MAKEPOINT(position);

  // Don't identify anything if no melody
  if (!melodyExists)
    return FALSE;

  // Calculate the coordinates of the clicked symbol
  if ((pos.x-2)%36 <= 31 && (pos.y-38)%24 <= 19 && pos.x <= 72 && pos.y >= 38) {
    int ix = pos.x/36;
    int iy = (pos.y-36)/24;
    if (iy < 0x10/2) {
      // The cursor was actually on the symbol range at the left side:
      // search for the symbol object with the correct ID
      int index = iy*2+ix+0x10*activeSymbolSet;
      for (SymbolClass **p = symbolArray; (*p)->GetID() < index; ++p);
      if ((*p)->GetID() == index) {
        // The symbol has been found:
        // set the active symbol, refresh the screen and return the pointer
        editModeSymbolActive = FALSE;
        activeSymbol = activeSymbolSet*0x10+iy*2+ix;
        hDC = GetDC(hMainWnd);
        DisplayEditModeSymbols(hDC);
        RefreshSymbols(hDC);
        ReleaseDC(hMainWnd, hDC);
        hEditCursor = LoadCursor(NULL, IDC_ARROW);
        return (currentSymbol = *p);
      }
    }
  }

  // No symbol identified: return a null pointer
  return NULL;
}

// **********************************************
// SelectSymbolSet changes the current symbol set
// in response to the user's selection at the Symbols submenu.
// The screen is updated with the new set, and the first
// symbol in the set is chosen as the current one.

void SelectSymbolSet(int set)
{
  activeSymbolSet = set;
  activeSymbol = set*0x10;
  editModeSymbolActive = FALSE;
  hEditCursor = LoadCursor(NULL, IDC_ARROW);

  // Search for the first symbol object belonging to this set
  for (SymbolClass **p = symbolArray; (*p)->GetID() < 0x10*set; ++p);
  currentSymbol = *p;

  // Refresh the screen with the new symbols
  HDC hDC = GetDC(hMainWnd);
  DisplayEditModeSymbols(hDC);
  RefreshSymbols(hDC);
  ReleaseDC(hMainWnd, hDC);
}

// **********************************************
// GetActiveSymbol returns the ID of the current symbol.

int GetActiveSymbol()
{
  return editModeSymbolActive ? activeEditMode : activeSymbol;
}

// **********************************************
// GetCurrentSymbol returns a pointer to the current symbol object.

SymbolClass *GetCurrentSymbol()
{
  return editModeSymbolActive ? NULL : currentSymbol;
}
