// C++ Headers
#include <ctime>

// Windows Headers
#ifdef _WIN32
#define Rectangle WinRectangle
#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#undef Rectangle 
#endif

// Project Headers
#include "Application.h"
#include "Window.h"
#include "Common.h"


BEGIN_NAMESPACE


struct ApplicationData
{
	int screenW, screenH;
	clock_t lastTick;
	Window* mainWindow;
};


#ifdef _WIN32


extern LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);


Application::Application()
{
	m_data = new ApplicationData;
	m_data->screenW = GetSystemMetrics(SM_CXSCREEN);
	m_data->screenH= GetSystemMetrics(SM_CYSCREEN);
	WNDCLASSEXW wcx = { sizeof(WNDCLASSEXA), CS_OWNDC | CS_HREDRAW | CS_VREDRAW, MainWndProc,
			0, 0, GetModuleHandle(0), NULL, NULL, NULL, NULL, L"MainWClass", NULL };
	RegisterClassExW(&wcx);
	//m_data->mainWindow = new Window("Test", a_fullscreen);
	//ShowCursor(FALSE);
}


Application::~Application()
{
	//delete m_data->mainWindow;
	delete m_data;
}


int Application::exec()
{
	normalLoop();
  return 0;
}


void Application::normalLoop()
{
	MSG msg;
	while (GetMessageW(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg); 
		DispatchMessageW(&msg); 
	}
}


void Application::gameLoop()
{
	MSG msg;
	while (true)
	{
		// Remove all messages off the queue ... makes it look like we are responding
		while ( GetQueueStatus(QS_ALLEVENTS) )
			PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);

		clock_t tick = clock();
		m_data->lastTick = tick;

		SHORT escape = GetAsyncKeyState(VK_ESCAPE);
		if ( escape )
			break;

		m_data->mainWindow->repaint();
	}
}


#else

END_NAMESPACE

//  C/C++ API for Mac OS X Windowing - windowing.cpp
//  Created by John Ryland (jryland@xiaofrog.com), 29/10/2017
//  Copyright (c) 2017 InvertedLogic
//  All rights reserved.
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <objc/message.h>
#include <objc/runtime.h>
#include <ApplicationServices/ApplicationServices.h>
//#include <OpenGL/gl.h>

extern id NSApp;
class NSString;
typedef NSString*  NSRunLoopMode;
typedef unsigned long long NSEventMask;
const NSEventMask NSAnyEventMask = UINT64_MAX;
extern "C" NSRunLoopMode NSDefaultRunLoopMode;


BEGIN_NAMESPACE


bool s_started = false;


BOOL AppDidFinishLaunching(id self, SEL _cmd, id notification)
{
  Application* appPtr = 0;
  object_getInstanceVariable(self, "ApplicationPtr", (void**)&appPtr);
  printf("AppDidFinishLaunching %i\n", __LINE__);
  s_started = true;
  printf("AppDidFinishLaunching %i\n", __LINE__);
  return YES;
}

struct NSPoint { double x, y; };
struct NSSize { double width, height; };
struct NSRect { double x, y, width, height; };
struct NSEvent;
void WindowOnDraw(id self, SEL _cmd, NSRect rect);
void WindowOnResize(id self, SEL _cmd, NSSize newSize);
void WindowOnMouseEvent(id self, SEL _cmd, NSEvent* event);
void ProcessTimerEvent(id self, SEL _cmd, void* timerEvent);

Application::Application()
{
	m_data = new ApplicationData;
	//m_data->screenW = xx;
	//m_data->screenH = xx;
  Class AppDelClass;
  AppDelClass = objc_allocateClassPair((Class)objc_getClass("NSObject"), "AppDelegate", 0);
  class_addMethod(AppDelClass, sel_getUid("applicationDidFinishLaunching:"), (IMP)AppDidFinishLaunching, "i@:@");
  class_addIvar(AppDelClass, "ApplicationPtr", sizeof(Application*), sizeof(Application*), "Application*");
  objc_registerClassPair(AppDelClass);
  Class ViewClass;
#if 1 // USE_OPENGL
  ViewClass = objc_allocateClassPair((Class)objc_getClass("NSOpenGLView"), "View", 0);
#else
  ViewClass = objc_allocateClassPair((Class)objc_getClass("NSView"), "View", 0);
#endif
  class_addMethod(ViewClass, sel_getUid("drawRect:"), (IMP)WindowOnDraw, "v@:");
  class_addMethod(ViewClass, sel_getUid("setFrameSize:"), (IMP)WindowOnResize, "v@:");
  class_addMethod(ViewClass, sel_getUid("mouseDown:"), (IMP)WindowOnMouseEvent, "v@:");
  class_addMethod(ViewClass, sel_getUid("mouseDragged:"), (IMP)WindowOnMouseEvent, "v@:");
  class_addMethod(ViewClass, sel_getUid("mouseUp:"), (IMP)WindowOnMouseEvent, "v@:");
  class_addMethod(ViewClass, sel_getUid("mouseMoved:"), (IMP)WindowOnMouseEvent, "v@:");
  class_addMethod(ViewClass, sel_getUid("processTimerEvent:"), (IMP)ProcessTimerEvent, "v@:");
  class_addIvar(ViewClass, "WindowPtr", sizeof(Window*), sizeof(Window*), "Window*");

  objc_registerClassPair(ViewClass);
  objc_msgSend((id)objc_getClass("NSApplication"), sel_getUid("sharedApplication"));

  id appDelObj = objc_msgSend((id)objc_getClass("AppDelegate"), sel_getUid("alloc"));
  appDelObj = objc_msgSend(appDelObj, sel_getUid("init"));
  object_setInstanceVariable(appDelObj, "ApplicationPtr", (void*)this);
  objc_msgSend(NSApp, sel_getUid("setDelegate:"), appDelObj);

  printf("waiting for AppDidFinishLaunching\n");
  objc_msgSend(NSApp, sel_getUid("finishLaunching"));

  while (!s_started)
  {
    printf("waiting for event\n");
    //CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES);
    id event = objc_msgSend(NSApp, sel_getUid("nextEventMatchingMask:untilDate:inMode:dequeue:"), NSAnyEventMask, nil, NSDefaultRunLoopMode, YES);
    objc_msgSend(NSApp, sel_getUid("sendEvent:"), event);
    objc_msgSend(NSApp, sel_getUid("updateWindows"));
    printf("got event\n");
  }

  printf("finished waiting for AppDidFinishLaunching\n");
}


Application::~Application()
{
  // any shut down code needed
	delete m_data;
}


int Application::exec()
{
/*
  // Objective-C version that emulates what "run" does internally
  - (void)run
  {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    [self finishLaunching];

    shouldKeepRunning = YES;
    do
    {
      [pool release];
      pool = [[NSAutoreleasePool alloc] init];

      NSEvent *event =
        [self
        nextEventMatchingMask:NSAnyEventMask
          untilDate:[NSDate distantFuture]
          inMode:NSDefaultRunLoopMode
          dequeue:YES];

      [self sendEvent:event];
      [self updateWindows];
    } while (shouldKeepRunning);

    [pool release];
  }

  - (void)terminate:(id)sender
  {
    shouldKeepRunning = NO;
  }
*/
  printf("run loop\n");
  id pool = objc_msgSend((id)objc_getClass("NSAutoreleasePool"), sel_getUid("alloc"));
  pool = objc_msgSend(pool, sel_getUid("init"));
  objc_msgSend(NSApp, sel_getUid("run"));
  objc_msgSend(pool, sel_getUid("release"));
  return 0;
}


void Application::normalLoop()
{
}


void Application::gameLoop()
{
}


#endif


END_NAMESPACE

