////////////////////////////////////////////////////////////////////////////////
// DriveLED.cpp -- this file is part of the Emulator Developers Kit(EDK)
// available at http://ourworld.compuserve.com/homepages/pc64/develop.htm
//
// Shows the VC1541 drive LED in the upper left corner of the screen.
//
// In Visual C++ "Tools / Options / Directories", add
//   Include files                = C:\PC64Win\EDK\Include
//
// In Visual C++ "Build / Settings / Debug", set
//   Executable for debug session = ..\PC64Win.exe
//   Working Directory            = ..
//   Program arguments            = -extension DriveLED.dll AnyDisk.d64

#include <EDK.h>


class DriveLED : public Object {

  HWND hwnd;
  Line Display;
  Line* pLED;

  // initialisation
  void DoInit() {
    Object::DoInit();
    Display.Init("Display", this, (pfn)OnDisplayChange, (pfn)OnDisplayChange);
    WNDCLASS wc;
    memset(&wc, 0, sizeof wc);
    wc.lpfnWndProc = DriveLEDWndProc;
    wc.hInstance = ghinst;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
    wc.lpszClassName = "DriveLED";
    win(RegisterClass(&wc));
    win(hwnd = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_TOPMOST, "DriveLED", "VC1541 Drive LED", WS_POPUP | WS_DLGFRAME, 0, 0, 20, 20, NULL, NULL, ghinst, this));
    ShowWindow(hwnd, SW_SHOW);
    win(SetTimer(hwnd, 1, 1000, NULL));
  }

public:

  // constructor
  DriveLED() {
    hwnd = NULL;
    pLED = NULL;
  }

  // destructor
  virtual ~DriveLED() {
    if (hwnd != NULL) {
      DestroyWindow(hwnd);
      hwnd = NULL;
    }
  }

  // update display when LED state changes
  void OnDisplayChange() {
    InvalidateRect(hwnd, NULL, FALSE);
    UpdateWindow(hwnd);
  }

  // window function
  friend LRESULT CALLBACK DriveLEDWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    try {
      static DriveLED* p;
      switch (uMsg) {
      case WM_CREATE:
        {
          p = (DriveLED*)((CREATESTRUCT*)lParam)->lpCreateParams;
          return TRUE;
        }
      case WM_TIMER:
        {
          assert(p->pLED == NULL);
          Object* pVC1541 = (Object*)gObjectRoot.FindChild("VC1541");
          try {
            p->pLED = (Line*)pVC1541->FindChild("LED");
          } catch (...) {
            p->pLED = (Line*)((byte*)pVC1541 + 2620); // obsolete for v2.14
          }
          p->Display.ConnectTo(*p->pLED);
          win(KillTimer(hwnd, 1));
          return TRUE;
        }
      case WM_PAINT:
        {
          RECT rc;
          win(GetClientRect(hwnd, &rc));
          PAINTSTRUCT ps;
          HDC hdc;
          win(hdc = BeginPaint(hwnd, &ps));
          HBRUSH hbrush;
          win(hbrush = CreateSolidBrush(p->Display.IsHigh() ? 0x002020FF : 0x00202040));
          win(hbrush = (HBRUSH)SelectObject(hdc, hbrush));
          win(Ellipse(hdc, 0, 0, rc.right, rc.bottom));
          win(hbrush = (HBRUSH)SelectObject(hdc, hbrush));
          win(DeleteObject(hbrush));
          win(EndPaint(hwnd, &ps));
          return TRUE;
        }
      }
    } catch (...) {}
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
  }
};


// dll entry
BOOL WINAPI DllMain(HINSTANCE, DWORD dwReason, LPVOID) {
  static DriveLED* p;
  try {
    switch (dwReason) {
    case DLL_PROCESS_ATTACH:
      assert(p == NULL);
      p = new DriveLED;
      p->Init();
      break;
    case DLL_PROCESS_DETACH:
      assert(p != NULL);
      delete p;
      p = NULL;
      break;
    }
    return TRUE;
  } catch (...) {
    report();
    return FALSE;
  }
}
