#include <EDK.h>
#define Config_cpp
#include "General.h"
#include "CC64.h"
#include "CVC1541.h"


Config gconf("Software\\Wolfgang Lorenz\\PC64Win", "PC64Win.ini");

int giClockReg;


HANDLE OpenFile(const char* pcName) {
  
  // try read and write
  HANDLE hFile = CreateFile(pcName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hFile != INVALID_HANDLE_VALUE) {
    return hFile;
  }
  
  // try read only
  if (GetLastError() == 5) {
    hFile = CreateFile(pcName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile != INVALID_HANDLE_VALUE) {
      return hFile;
    }
  }
  
  error("can't open %s", pcName);
  return 0; // calm compiler
}


void ReadFile(HANDLE hFile, void* pBuffer, int Size) {
  int Result;
  if (!ReadFile(hFile, pBuffer, Size, (DWORD*)&Result, NULL)) {
    error("can't read file");
  }
  if (Result != Size) {
    error("EOF reached");
  }
}

void WriteFile(HANDLE hFile, const void* pBuffer, int Size) {
  int Result;
  if (!WriteFile(hFile, pBuffer, Size, (DWORD*)&Result, NULL)) {
    error("can't write file");
  }
  if (Result != Size) {
    error("disk full");
  }
}


const CString& GetDefaultDirectory() {
  static CString Dir;
  if (Dir.IsEmpty()) {
    int iSize = GetCurrentDirectory(0, NULL);
    GetCurrentDirectory(iSize, Dir.GetBuffer(iSize));
    Dir.ReleaseBuffer();
  }
  return Dir;
}

CC64* gpC64;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int iCmdShow) {
  try {
    GetDefaultDirectory();
    ghinst = hInstance;

    // standalone control center
    if (stricmp(lpszCmdLine, "-control") == 0) {
      try {
        extern flag Control();
        Control();
      } catch (...) {
        report();
      }
      return 0;
    }

    if (gconf.GetInt(NULL, "Show Keys at Startup (1 or 0)", 1)) {
      MessageBox(NULL, "\
Joystick\t\tNumpad Arrows and Right Ctrl\n\
Swap Joysticks\tAlt+F10\n\
Change Disk\tF9\n\
Control Center\tF10\n\
Stop+Restore\tTab+F12\n\
Reset\t\tCtrl+Alt+Backspace\n\
\n\
You can switch off this message in the Control Center.", "PC4Win Keys", MB_ICONINFORMATION | MB_OK);
    }

    if (!hPrevInstance) {
      WNDCLASS wc;
      wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
      extern LRESULT CALLBACK DisplayWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
      wc.lpfnWndProc = DisplayWndProc;
      wc.cbClsExtra = 0;
      wc.cbWndExtra = 4;
      wc.hInstance = ghinst;
      wc.hIcon = LoadIcon(ghinst, MAKEINTRESOURCE(IDI_ICON1));
      wc.hCursor = LoadCursor(NULL, IDC_ARROW);
      wc.hbrBackground = NULL;
      wc.lpszMenuName = NULL;
      wc.lpszClassName = "CVIC656x Display";
      ATOM aClass = RegisterClass(&wc);
    }
    HMENU hmenu = NULL;//LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU1));
    HACCEL haccel = LoadAccelerators(ghinst, MAKEINTRESOURCE(IDR_ACCELERATOR1));

    gpC64 = new CC64;
    gpC64->Init("C64", NULL);
    gpC64->Keyboard.ReadKeyMapping();

    extern void ParseCommandLine(CC64* pC64, char* pcCmd);
    ParseCommandLine(gpC64, lpszCmdLine);

    RECT rect;
    rect.left = 0;
    rect.top = 0;
    extern int GetInt(const char* pcSection, const char* pcKey, int iDefault);
    rect.right = GetInt("Display", "Window Width", (320 + 8 * 2) * 2);
    rect.bottom = GetInt("Display", "Window Height", (200 + 8 * 2) * 2);
    flag fMaximize = GetInt("Display", "Window Maximize (1 or 0)", 0);
    AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);

    extern flag gfMoreSpeedButLessCompatibility;
    gfMoreSpeedButLessCompatibility = GetInt("Performance", "More Speed But Less Compatibility (1 or 0)", 0);

    ghwnd = CreateWindow("CVIC656x Display", TITLE, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top, NULL, hmenu, ghinst, &gpC64->Display);
    extern void WindowHasBeenCreated(HWND hwnd);
    WindowHasBeenCreated(ghwnd);
    ShowWindow(ghwnd, fMaximize ? SW_SHOWMAXIMIZED : iCmdShow);

    extern void InitSound(CC64* pC64, HWND hwnd);
    InitSound(gpC64, ghwnd);

    gpC64->pVIC->Emulate();

/*
    PostMessage(ghwnd, WM_COMMAND, ID_EMULATE_START, 0);
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
      if (!TranslateAccelerator(ghwnd, haccel, &msg)) {
        if (msg.message == WM_COMMAND && msg.wParam == ID_EMULATE_START) {
          gpC64->VIC.Emulate();
          break;
        }
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
    } */
  } catch (char* pcError) {
    if (*pcError != 0) {
      report();
    }
    if (gpC64 != NULL) {
      extern void UnloadExtensions();
      UnloadExtensions();
      SendMessage(ghwnd, WM_CLOSE, 0, 0);
      delete gpC64;
      gpC64 = NULL;
    }
  }

  extern flag gfSettingsChanged;
  if (gfSettingsChanged) {
    gfSettingsChanged = false;
    try {
      char ac[4096];
      win(GetModuleFileName(NULL, ac, sizeof ac));
      if (*lpszCmdLine != 0 && *lpszCmdLine != ' ') {
        strcat(ac, " ");
        strcat(ac, lpszCmdLine);
        assert(strlen(ac) < sizeof ac);
      }
      win(WinExec(ac, iCmdShow) >= 32);
    } catch (...) {
      report();
    }
  }

  return 0;
}

void Trace(int iPC) {
  static flag fTrace = FALSE;
  if (fTrace) { 
    char ac[10];
    sprintf(ac, "%04X\n", iPC);
    OutputDebugString(ac);
  } else if (iPC == 0xE5AA0) {
    fTrace = TRUE;
  }
}
