#ifndef _BROWSER_COMMON_H_
#define _BROWSER_COMMON_H_

#include <webgl.h>

#include <v8.h>
#include <node.h>
#include <nan.h>

#include <defines.h>

#include <memory>
#include <chrono>
#include <deque>
#include <thread>
#include <mutex>
#include <functional>

#include <Servo2D.h>

using namespace std;
using namespace v8;
using namespace node;

namespace browser {

typedef std::shared_ptr<Servo2D> EmbeddedBrowser;

enum class EmbeddedKeyModifiers {
  SHIFT,
  CTRL,
  ALT,
};

// bindings

bool initializeEmbedded(const std::string &dataPath, const string &frameworkPath);
EmbeddedBrowser createEmbedded(
  const std::string &url,
  WebGLRenderingContext *gl,
  NATIVEwindow *window,
  GLuint tex,
  int width,
  int height,
  float scale,
  int *textureWidth,
  int *textureHeight,
  std::function<EmbeddedBrowser()> getBrowser,
  std::function<void(EmbeddedBrowser)> setBrowser,
  std::function<void()> onloadstart,
  std::function<void(const std::string &)> onloadend,
  std::function<void(int, const std::string &, const std::string &)> onloaderror,
  std::function<void(const std::string &, const std::string &, int)> onconsole,
  std::function<void(const std::string &)> onmessage
);
void destroyEmbedded(EmbeddedBrowser browser_);
// void embeddedUpdate();
void embeddedDoMessageLoopWork();
std::pair<int, int> getEmbeddedSize(EmbeddedBrowser browser_);
void setEmbeddedSize(EmbeddedBrowser browser_, int width, int height);
int getEmbeddedWidth(EmbeddedBrowser browser_);
void setEmbeddedWidth(EmbeddedBrowser browser_, int width);
int getEmbeddedHeight(EmbeddedBrowser browser_);
void setEmbeddedHeight(EmbeddedBrowser browser_, int height);
float getEmbeddedScale(EmbeddedBrowser browser_);
void setEmbeddedScale(EmbeddedBrowser browser_, float scale);
void embeddedGoBack(EmbeddedBrowser browser_);
void embeddedGoForward(EmbeddedBrowser browser_);
void embeddedReload(EmbeddedBrowser browser_);
void embeddedMouseMove(EmbeddedBrowser browser_, int x, int y);
void embeddedMouseDown(EmbeddedBrowser browser_, int x, int y, int button);
void embeddedMouseUp(EmbeddedBrowser browser_, int x, int y, int button);
void embeddedMouseWheel(EmbeddedBrowser browser_, int x, int y, int deltaX, int deltaY);
void embeddedKeyDown(EmbeddedBrowser browser_, int key, int wkey, int modifiers);
void embeddedKeyUp(EmbeddedBrowser browser_, int key, int wkey, int modifiers);
void embeddedKeyPress(EmbeddedBrowser browser_, int key, int wkey, int modifiers);
void embeddedRunJs(EmbeddedBrowser browser_, const std::string &jsString, const std::string &scriptUrl, int startLine);

// helpers

void QueueOnBrowserThread(std::function<void()> fn);
// void QueueOnBrowserThreadFront(std::function<void()> fn);

// void RunOnMainThread(std::function<void()> fn);
void QueueOnMainThread(std::function<void()> fn);
void MainThreadAsync(uv_async_t *handle);

// variables

extern bool embeddedInitialized;
extern std::thread browserThread;

extern uv_sem_t constructSem;
extern uv_sem_t mainThreadSem;
extern uv_sem_t browserThreadSem;

extern std::mutex browserThreadFnMutex;
extern std::deque<std::function<void()>> browserThreadFns;

extern uv_async_t mainThreadAsync;
extern std::mutex mainThreadFnMutex;
extern std::deque<std::pair<std::function<void()>, bool>> mainThreadFns;

}

#endif
