// ------------------------------------------------
// Приложение MIDIPL
// Проигрывание файлов MIDI
// с помощью интерфейса сообщений MCI
// ------------------------------------------------

#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <mem.h>
#pragma hdrstop

#include "midipl.hpp"
#include "midiio.hpp"

// Идентификатор таймера
#define BEEP_TIMER 1

// Идентификатор полосы просмотра
#define ID_SCROLL 10

// Длина полосы просмотра
#define SCROLL_SIZE 400

// Прототипы функций
BOOL    InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Глобальные переменные
int      nMode = MODE_STOP;
MMTIME   mmtimeOut;
BOOL     fFileLoaded = FALSE;
int      nPosition;
HWND     hScroll;
UINT     wOutDeviceID;
BYTE     szFileName[128];
DWORD    dwFileSize;

char const szClassName[]   = "MCIMIDIClass";
char const szWindowTitle[] = "MIDI Player";
HINSTANCE  hInst;

// =====================================
// Функция WinMain
// =====================================
#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpszCmdLine, int nCmdShow)
{
  MSG  msg;   // структура для работы с сообщениями
  HWND hwnd;  // идентификатор главного окна приложения

  if(hPrevInstance)
    return FALSE;

  if(!InitApp(hInstance))
    return FALSE;

  hInst = hInstance;

  hwnd = CreateWindow(
    szClassName,         // имя класса окна
    szWindowTitle,       // заголовок окна
    WS_OVERLAPPEDWINDOW, // стиль окна
    CW_USEDEFAULT,       // размеры и расположение окна
    CW_USEDEFAULT,       
    450, 120, 0, 0, hInstance, NULL);
                       
  if(!hwnd)
    return FALSE;

  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);

  while(GetMessage(&msg, 0, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// =====================================
// Функция InitApp
// Выполняет регистрацию класса окна
// =====================================

BOOL
InitApp(HINSTANCE hInstance)
{
  ATOM aWndClass; // атом для кода возврата
  WNDCLASS wc;    // структура для регистрации

  memset(&wc, 0, sizeof(wc));
  wc.lpszMenuName  = "APP_MENU";
  wc.style         = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc   = (WNDPROC) WndProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = hInstance;
  wc.hIcon         = LoadIcon(hInstance, "APPICON");
  wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  wc.lpszClassName = (LPSTR)szClassName;

  aWndClass = RegisterClass(&wc);
  return (aWndClass != 0);
}

// =====================================
// Функция WndProc
// =====================================

LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  PAINTSTRUCT ps;
  int rc;

  switch (msg)
  {
// ------------------------------------------------------------
// WM_CREATE
// Создание главного окна приложения
// ------------------------------------------------------------
    case WM_CREATE:
    {
       nMode        = MODE_STOP;
       fFileLoaded  = FALSE;
       wOutDeviceID = 0;

       // Создаем таймер
       SetTimer(hwnd, BEEP_TIMER, 100, NULL);

       // Создаем полосу просмотра
       hScroll = CreateWindow("scrollbar", NULL,
         WS_CHILD | WS_VISIBLE | SBS_HORZ,
         10, 40, SCROLL_SIZE, 15, hwnd,
         (HMENU) ID_SCROLL, hInst, NULL);

       // Устанавливаем текущую позицию
       nPosition = 0;

       // Устанавливаем минимальное и максимальное
       // значения для полосы просмотра
       SetScrollRange(hScroll, SB_CTL, 1, SCROLL_SIZE, TRUE);

       // Устанавливаем ползунок
       SetScrollPos(hScroll, SB_CTL, nPosition, TRUE);
       return 0;
    }

// ------------------------------------------------------------
// WM_PAINT
// Рисование в окне
// ------------------------------------------------------------
    case WM_PAINT:
    {
      // Получаем контекст отображения для
      // рисования во внутренней области окна 
      hdc = BeginPaint(hwnd, &ps);

      // Отображаем текущий режим работы
      if(nMode == MODE_STOP)
        TextOut(hdc, 10, 10, "Остановлено", 11);
      else if(nMode == MODE_PLAYING)
        TextOut(hdc, 10, 10, "Идет проигрывание...", 20);
      else if(nMode == MODE_PLAYINGPAUSED)
        TextOut(hdc, 10, 10, "Проигрывание остановлено", 24);
      else
        TextOut(hdc, 10, 10, "Неправильный режим!", 19);

      // Освобождаем контекст отображения
      EndPaint(hwnd, &ps);
      return 0;
    }

// ------------------------------------------------------------
// WM_COMMAND
// Обработка сообщений от меню
// ------------------------------------------------------------
    case WM_COMMAND:
    {
      switch (wParam)
      {
        // -------------------------------------------------
        // Строка "About" меню "Help"
        // -------------------------------------------------
        case CM_HELPABOUT:
        {
          MessageBox(hwnd,
            "MIDI Player, v.1.0\n"
            "(C) Frolov A.V., 1994",
            "About MIDIPL", MB_OK | MB_ICONINFORMATION);
          return 0;
        }

        // -------------------------------------------------
        // Строка "Open" меню "File"
        // -------------------------------------------------
        case CM_FILEOPEN:
        {
          char szTitle[256];

          // Загружаем новый файл
          if(!mcimidiSelectFile(szFileName))
            return 0;

          // Отображаем в заголовке окна путь к файлу
          lstrcpy(szTitle, szWindowTitle);
          lstrcat(szTitle, " - ");
          lstrcat(szTitle, szFileName);
          SetWindowText(hwnd, szTitle);

          // Если было запущено воспроизведение,
          // останавливаем его и закрываем устройство вывода
          if(wOutDeviceID)
          {
            mcimidiStop(wOutDeviceID);
            mcimidiClose(wOutDeviceID);
            wOutDeviceID = 0;

            // Новый режим
            nMode = MODE_STOP;

            // Перерисовываем окно для отображения строки,
            // соответствующей новому режиму
            InvalidateRect(hwnd, NULL, TRUE);
          }

          // Устанавливаем движок в начало полосы просмотра
          nPosition = 0;
          SetScrollPos(hScroll, SB_CTL, nPosition, TRUE);

          // Устанавливаем флаг загрузки файла
          fFileLoaded = TRUE;
          return 0;
        }

        // -------------------------------------------------
        // Строка "Play!"
        // Проигрывание загруженного файла MIDI
        // -------------------------------------------------
        case CM_CTLPLAY:
        {
          // Если файл загружен и не проигрывается,
          // запускаем проигрывание файла
          if((fFileLoaded == TRUE) && (nMode == MODE_STOP))
          {
            // Новый режим
            nMode = MODE_PLAYING;

            // Перерисовываем окно для отображения строки,
            // соответствующей новому режиму
            InvalidateRect(hwnd, NULL, TRUE);

            // Если устройство не было открыто раньше,
            // открываем его
            if(!wOutDeviceID)
              wOutDeviceID = mcimidiOpen((LPSTR)szFileName);

            // Проигрываем файл
            mcimidiPlay(hwnd, wOutDeviceID);
          }
          return 0;
        }

        // -------------------------------------------------
        // Строка "Stop!"
        // Останов проигрывания или записи файла MIDI
        // -------------------------------------------------
        case CM_CTLSTOP:
        {
          if(nMode == MODE_PLAYING || nMode == MODE_PLAYINGPAUSED)
          {
            // Останавливаем проигрывание
            mcimidiStop(wOutDeviceID);
          }

          // Устанавливаем движок в начало полосы просмотра
          nPosition = 0;
          SetScrollPos(hScroll, SB_CTL, nPosition, TRUE);

          // Новый режим
          nMode = MODE_STOP;
          InvalidateRect(hwnd, NULL, TRUE);
          return 0;
        }

        // -------------------------------------------------
        // Строка "Pause!"
        // Временный останов проигрывания
        // -------------------------------------------------
        case CM_CTLPAUSE:
        {
          if(nMode == MODE_PLAYING)
          {
            // Временный останов проигрывания
            mcimidiPause(wOutDeviceID);
            nMode = MODE_PLAYINGPAUSED;
          }

          InvalidateRect(hwnd, NULL, TRUE);
          return 0;
        }

        // -------------------------------------------------
        // Строка "Resume!"
        // Продолжение проигрывания после останова
        // -------------------------------------------------
        case CM_CTLRESUME:
        {
          if(nMode == MODE_PLAYINGPAUSED)
          {
            // Продолжаем проигрывание
            mcimidiPlayCurrent(hwnd, wOutDeviceID);
            nMode = MODE_PLAYING;
            InvalidateRect(hwnd, NULL, TRUE);
          }
          return 0;
        }

        // -------------------------------------------------
        // Строка "Exit" меню "File"
        // Завершение работы приложения
        // -------------------------------------------------
        case CM_FILEEXIT:
        {
          DestroyWindow(hwnd);
          return 0;
        }
        default:
          return 0;
      }
    }

// ------------------------------------------------------------
// MM_MCINOTIFY
// ------------------------------------------------------------
    case MM_MCINOTIFY:
    {
      // Если находились в режиме воспроизведения, останавливаем
      // и закрываем устройство вывода
      if(nMode == MODE_PLAYING)
      {
        if(wOutDeviceID)
        {
          mcimidiStop(wOutDeviceID);
          mcimidiClose(wOutDeviceID);
          wOutDeviceID=0;
          nMode = MODE_STOP;
          InvalidateRect(hwnd, NULL, TRUE);
        }
      }
      return 0;
    }

// ------------------------------------------------------------
// WM_TIMER
// Сообщение от таймера
// ------------------------------------------------------------
    case WM_TIMER:
    {
      MCI_STATUS_PARMS mciStatus;
      DWORD dwPos;

      // Режим воспроизведения
      if(nMode == MODE_PLAYING)
      {
        // Определяем текущую позицию внутри блока
        mciStatus.dwItem = MCI_STATUS_POSITION;
        mciSendCommand(wOutDeviceID, MCI_STATUS,
          MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatus);
        dwPos = mciStatus.dwReturn;

        // Вычисляем новое положение движка полосы просмотра
        nPosition = ((DWORD)SCROLL_SIZE * dwPos) / dwFileSize;

        // Ограничиваем пределы изменения текущей
        // позиции значениями от 1 до SCROLL_SIZE
        if(nPosition > SCROLL_SIZE) nPosition = SCROLL_SIZE;
        if(nPosition < 1) nPosition = 1;

        // Устанавливаем ползунок полосы просмотра
        // в соответствии с новым значением текущей позиции
        SetScrollPos(hScroll, SB_CTL, nPosition, TRUE);
      }
      return 0;
    }

// ------------------------------------------------------------
// WM_DESTROY
// Уничтожение главного окна приложения
// ------------------------------------------------------------
    case WM_DESTROY:
    {
      // Удаляем таймер и полосу просмотра
      KillTimer(hwnd, BEEP_TIMER);
      DestroyWindow(hScroll);

      // Если находимся в режиме проигрывания, останавливаем
      // запись и закрываем устройство вывода
      if(fFileLoaded)
      {
        if(nMode == MODE_PLAYING || nMode == MODE_PLAYINGPAUSED)
        {
          mcimidiStop(wOutDeviceID);
          mcimidiClose(wOutDeviceID);
        }
        nMode = MODE_STOP;
      }
      PostQuitMessage(0);
      return 0;
    }
    default:
      break;
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

