#include <EDK.h>
#include "General.h"
#include "CDisplay.h"
#include "CC64.h"

#define Class CDisplay

extern CC64* gpC64;

#include "C64Display.h"
C64Display gC64Display;

static flag gfSaveFullscreen;
void StopFullscreen(HWND hwnd) {
  gfSaveFullscreen = gC64Display.fFullscreen;
  if (gC64Display.fFullscreen) {
    gC64Display.Init(hwnd, false);
  }
}
void ResumeFullscreen(HWND hwnd) {
  if (gfSaveFullscreen) {
    gC64Display.Init(hwnd, true);
  }
}

int GetInt(const char* pcSection, const char* pcKey, int iDefault) {
  return gconf.GetInt(pcSection, pcKey, iDefault);
}

void WindowHasBeenCreated(HWND hwnd) {
  gC64Display.Init(hwnd, gconf.GetInt("Display", "Fullscreen (1 or 0)", 0));
  gpC64->pVIC->pbBitmap = gC64Display.GetC64Screen();
  gpC64->pVIC->pbLine = gC64Display.GetC64Screen();
  gpC64->pVIC->pbLineShift = gC64Display.GetC64Screen();
}

LRESULT CALLBACK DisplayWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  try {
    flag fReturn = FALSE;
    LRESULT lResult = gC64Display.WndProc(hwnd, uMsg, wParam, lParam, fReturn);
    if (fReturn) {
      return lResult;
    }
  } catch (...) {
    report();
    return 0;
  }
  CDisplay* p = (CDisplay*)GetWindowLong(hwnd, 0);
  switch(uMsg) {
  case WM_CREATE:
    p = (CDisplay*)((LPCREATESTRUCT)lParam)->lpCreateParams;
    SetWindowLong(hwnd, 0, (long)p);
    p->hwnd = hwnd;
    break;
  case WM_KEYDOWN:
  case WM_KEYUP:
  case WM_SYSKEYDOWN:
  case WM_SYSKEYUP:
    p->pKeyboard->C64Key(wParam, lParam);
    return 0;
  case WM_LBUTTONDOWN:
    p->pKeyboard->C64Key(VK_LBUTTON, 0x00000000);
    return 0;
  case WM_LBUTTONUP:
    p->pKeyboard->C64Key(VK_LBUTTON, 0x80000000);
    return 0;
  case WM_RBUTTONDOWN:
    p->pKeyboard->C64Key(VK_RBUTTON, 0x00000000);
    return 0;
  case WM_RBUTTONUP:
    p->pKeyboard->C64Key(VK_RBUTTON, 0x80000000);
    return 0;
  case WM_MBUTTONDOWN:
    p->pKeyboard->C64Key(VK_MBUTTON, 0x00000000);
    return 0;
  case WM_MBUTTONUP:
    p->pKeyboard->C64Key(VK_MBUTTON, 0x80000000);
    return 0;
  case WM_ERASEBKGND:
    return 0;
  case WM_GETMINMAXINFO:
    {
      MINMAXINFO* pmmi = (MINMAXINFO*)lParam;
      pmmi->ptMinTrackSize.x = 320 / 2 + GetSystemMetrics(SM_CXFRAME) * 2;
      pmmi->ptMinTrackSize.y = 200 / 2 + GetSystemMetrics(SM_CYCAPTION) - 1 + GetSystemMetrics(SM_CYFRAME) * 2;
      return 0;
    }
  case WM_SIZE:
    if (wParam != SIZE_MINIMIZED) {

      // save new window size
      if (wParam == SIZE_MAXIMIZED) {
        gconf.SetInt("Display", "Window Maximize (1 or 0)", 1);
      } else {
        gconf.SetInt("Display", "Window Width", LOWORD(lParam));
        gconf.SetInt("Display", "Window Height", HIWORD(lParam));
        gconf.SetInt("Display", "Window Maximize (1 or 0)", 0);
      }

    }
    break;

  case WM_SETFOCUS:
  case WM_KILLFOCUS:
    p->pKeyboard->ClearKeys();
    return 0;

  case WM_ACTIVATEAPP:
    if (wParam) {
      SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
    } else {
      SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
    }
    p->pKeyboard->ClearKeys();
    return 0;

  case WM_CLOSE:
    extern CC64* gpC64;
    gpC64->Keyboard.bStopEmulation = 1;
    break;

  case WM_USER:
    ((void (*)())lParam)();
    break;

  }
  return DefWindowProc(hwnd, uMsg, wParam, lParam);
}


/*
  HWND hwnd;            // display window
  CKeyboard* pKeyboard; // because of common WndProc

  int iFrames;          // total frame counter for current second
  int iDisplayedFrames; // visible frame counter for current second
  int iMinFrames;       // lowest allowed fps rate
  int iMinFramesSum;    // Bresenham counter, display frame if < 0
  flag fDisplayFrame;   // false = skip the next frame to get more speed

  int iClocksPer50Frames;
  int iLastClocks;      // saved LowWord of QueryPerformanceCounter
  int iLastPercent;     // for recognizing changes in window title
  int iLastDisplayedFrames;
*/

CDisplay::CDisplay() {
  hwnd = NULL;
  pKeyboard = NULL;
  iFrames = 0;
  iDisplayedFrames = 0;
  iMinFrames = 5;
  iMinFramesSum = 50;
  fDisplayFrame = false;
  LARGE_INTEGER Clocks;
  QueryPerformanceFrequency(&Clocks);
  double dPCClocksPerC64Clock = (double)Clocks.LowPart / (double)985248;
  iClocksPer50Frames = (int)(dPCClocksPerC64Clock * 63 * 312 * 50);
  QueryPerformanceCounter(&Clocks);
  iLastClocks = Clocks.LowPart;
  iLastPercent = 0;
  iLastDisplayedFrames = 0;
}

flag CDisplay::Refresh() {

  // flush bitmap to screen
  if (fDisplayFrame) {
    gC64Display.OnFrame();
    iDisplayedFrames++;
  }

  // windows multitasking
  MSG msg;
  while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
    DispatchMessage(&msg);
  }

  // pointer to bitmap will change at <Alt+Enter>
  if (gpC64->pVIC->pbBitmap != gC64Display.GetC64Screen()) {
    gpC64->pVIC->pbBitmap = gC64Display.GetC64Screen();
    gpC64->pVIC->pbLine = gC64Display.GetC64Screen();
    gpC64->pVIC->pbLineShift = gC64Display.GetC64Screen();
  }

  // pass keys to CIAs
  pKeyboard->SetKeyMatrix();
  if (pKeyboard->bStopEmulation) {
    pKeyboard->bStopEmulation = 0;
    throw "";
  }

  // display frame if there is enough time left
  extern flag Synchronize();
  fDisplayFrame = Synchronize();
  #ifdef FULL_FRAME_RATE
    fDisplayFrame = true;
  #endif

  // force display to minimal frame rate
  if (fDisplayFrame) {
    iMinFramesSum = 50;
  } else {
    iMinFramesSum -= iMinFrames;
    if (iMinFramesSum < 0) {
      iMinFramesSum += 50;
      fDisplayFrame = true;
    }
  }

  // update window title each 50 frames
  iFrames++;
  if (iFrames == 50) {
    LARGE_INTEGER Clocks;
    QueryPerformanceCounter(&Clocks);
    int iClocks = Clocks.LowPart - iLastClocks;
    iLastClocks = Clocks.LowPart;
    int iPercent = iClocksPer50Frames * 101 / iClocks;
    if (iPercent == 99 || iPercent == 101) {
      iPercent = 100;
    }
    if (iPercent != iLastPercent || iDisplayedFrames != iLastDisplayedFrames) {
      char ac[256];
      extern char gacSound[80];
      sprintf(ac, TITLE " - %d%% at %d fps using %s and %s", iPercent, iDisplayedFrames, gC64Display.GetMode(), gacSound);
      SetWindowText(hwnd, ac);
      iLastPercent = iPercent;
      iLastDisplayedFrames = iDisplayedFrames;
    }
    iFrames = 0;
    iDisplayedFrames = 0;
  }

  // for the VIC
  return fDisplayFrame;
}

// destructor
CDisplay::~CDisplay() {
}
