/*

Quick and dirty sample application, showing how to setup and use Chromium Offscreen Renderer.
Rendering is done though OpenGL.

*/

#ifndef _WIN32_WINNT		// Allow use of features specific to Windows XP or later.
#define _WIN32_WINNT 0x0501	// Change this to the appropriate value to target other versions of Windows.
#endif

#include <windows.h>

#include <tchar.h>
#include <stdio.h>
#include <direct.h>

#include <math.h>

#include <gl/gl.h>														// Header File For The OpenGL32 Library
#include <gl/glu.h>														// Header File For The GLu32 Library

#include <API/WebCore.h>

#include <iostream>
#include <vector>

#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")

// ---------------------------------------------------------------------

void messageOutput(TCHAR const *msg)
{
  _tprintf(_T("%s"),msg);
}

LRESULT	CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

// ---------------------------------------------------------------------

const int DEFAULTMAXASYNCRENDERPERSEC = 100;

const bool VIEW_ASYNC_RENDER = true;

// ---------------------------------------------------------------------

const TCHAR* CLASSNAME = _T("SprötchGL");

HINSTANCE g_hInstance = NULL;
HWND      g_hWnd = NULL;
HDC       g_hdc = NULL;
HGLRC     g_hRC = NULL;

TCHAR const* g_application_path = NULL;
TCHAR const* index_html_path = NULL;

// ---------------------------------------------------------------------
// Awesomium

using namespace Awesomium;

double testOverrideTimeFunction()
{
  LARGE_INTEGER ticksPerSecond;
  QueryPerformanceFrequency(&ticksPerSecond);

  LARGE_INTEGER tick;
  QueryPerformanceCounter(&tick);
  return double(tick.QuadPart)/double(ticksPerSecond.QuadPart);
}

// ---------------------------------------------------------------------

class GLdrawer : public WebViewListener, public WebView::MessageLogger
{
  class ObjectPosition
  {
  public:
    float angle[3];
    float position[3];
  };

  std::vector<ObjectPosition> m_objectPosition;

  float m_angleView[3];
  float m_positionView[3];
  int m_drawCount;

public:
  WebCore* m_core;
  std::vector<WebView*> m_view;
  std::vector<GLuint> m_textures;
  HCURSOR currentCursor;

  int m_width;
  int m_height;
  int m_transparencyMode;
  unsigned char* m_framebuffer;

  GLdrawer(int width, int height) : m_core(0), m_transparencyMode(Awesomium::WebView::TM_OPAQUE), m_width(width), m_height(height), m_framebuffer(0)
  {
    m_angleView[0] = m_angleView[1] = m_angleView[2] = 0.f;
    m_positionView[0] = m_positionView[1] = 0.f; m_positionView[2] = -1.f;
    m_drawCount = 0;

    currentCursor = NULL;

    resize( width, height );

    std::string workingDirectory = "";
    char currentPath[_MAX_PATH];
    _getcwd(currentPath, _MAX_PATH);
    workingDirectory = currentPath;

    _tprintf(_T("Index file is '%s' (relative from current working directory)\n"), index_html_path);

    // setup awesomium

    m_core = new WebCore(LOG_VERBOSE);
    m_core->setBaseDirectory(workingDirectory);

    {
      TCHAR path[ MAX_PATH ];
      strcpy(path, g_application_path);

      ::PathRemoveFileSpec(path);
      ::PathAppend(path, _T("plugins"));

      int length = mbstowcs(NULL, path, strlen(path));
      std::vector<wchar_t> dest(length+1, 0);
      int i = mbstowcs(&dest[0], path, strlen(path));

      std::wstring wpath(&dest[0]);

      m_core->addExtraPluginDir( wpath );
    }

    // TEST // m_core->setExternCurrentTimeFunction(testOverrideTimeFunction);

    m_core->setCustomResponsePage(404, "404response.html");
  }

  ~GLdrawer()
  {
    for(unsigned i=0;i<m_view.size();i++)
      //if(m_view)
    {
      m_view[i]->destroy();
    }
    delete m_core;
    delete [] m_framebuffer;
  }

  // test de callback, lors de l'ouverture d'une popup
  void onCreateNewWebView()
  {
    std::cout << "onCreateWebView() "  << std::endl;
    createWebView("about:blank",false);
  }

  void resize(int width, int height)
  {
    for(unsigned i=0;i<m_view.size();i++)
    {
      m_view[i]->resize(width, height);
    }
    m_width = width;
    m_height = height;

    delete [] m_framebuffer;
    m_framebuffer = new unsigned char[ m_width * m_height * 4 ];
  }

  void createWebView(std::string url, bool first)
  {
    WebView *mv = m_core->createWebView(m_width, m_height, VIEW_ASYNC_RENDER, DEFAULTMAXASYNCRENDERPERSEC);
    m_view.push_back( mv );

    GLuint textureID;
    ::glGenTextures(1, &textureID);
    m_textures.push_back(textureID);

    std::cout << "mgd: add one m_view\n";

    mv->setMessageLogger(this);
    mv->setProperty("welcomeMsg", "Hello from c++");
    mv->setProperty("renderSystem", "OpenGL Chromium rendering test");
    mv->setCallback("requestMsgFromJavascript");

    if(first)
    {
      mv->setListener(this);
    }
    mv->loadFile(url);

    // move the planes// on décale en z les plans actuels GL

    for(unsigned i=0;i<m_objectPosition.size();i++)
    {
      m_objectPosition[i].position[2] += -1.f;
    }
    // on setup le nouveau plan en zero

    ObjectPosition op;

    op.angle[0] = 0.f;
    op.angle[1] = 0.f;
    op.angle[2] = 0.f;

    op.position[0] = 0.f;
    op.position[1] = 0.f;
    op.position[2] = 0.f;

    m_objectPosition.push_back(op);
  }

  void drawAllWebViews()
  {
    m_drawCount += 1;

    glViewport(0,0,m_width,m_height);						// Reset The Current Viewport

    glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
    glLoadIdentity();							// Reset The Projection Matrix
    gluPerspective(90.0f, 1.0f, 0.1f, 100.0f);

    glMatrixMode(GL_MODELVIEW);						// Select The Modelview Matrix
    glLoadIdentity();

    glClearDepth(1.0f);
    glClearColor(0.5f,0.5f,0.5f,1.f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// Clear The Screen And The Depth Buffer

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_DEPTH_TEST);

    for(unsigned i=0; i<m_view.size(); i++)
    {
      if (m_view[i]->isDirty())
      {
        // paint html page
        m_view[i]->render(m_framebuffer, 0);

        // update opengl texture
        glBindTexture(GL_TEXTURE_2D, m_textures[i]);

        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_width, m_height, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, m_framebuffer);
        glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      }
    }

    glColor3f(1.0f,1.0f,1.0f);

    if (m_transparencyMode != Awesomium::WebView::TM_OPAQUE)
    {
      glEnable(GL_BLEND);
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }
    else
    {
      glDisable(GL_BLEND);
    }


    // -------------------------------------------------------------------------
    // draw planes

    for(unsigned i=0;i<m_view.size();i++)
    {
      glBindTexture(GL_TEXTURE_2D, m_textures[i]);

      glLoadIdentity();

      glTranslatef(m_positionView[0],m_positionView[1],m_positionView[2]);
      glRotatef(m_angleView[0],1.0f,0.0f,0.0f);
      glRotatef(m_angleView[1],0.0f,1.0f,0.0f);
      glRotatef(m_angleView[2],0.0f,0.0f,1.0f);

      glTranslatef(m_objectPosition[i].position[0],m_objectPosition[i].position[1],m_objectPosition[i].position[2]);
      glRotatef(m_objectPosition[i].angle[0],1.0f,0.0f,0.0f);
      glRotatef(m_objectPosition[i].angle[1],0.0f,1.0f,0.0f);
      glRotatef(m_objectPosition[i].angle[2],0.0f,0.0f,1.0f);

      if (m_transparencyMode != Awesomium::WebView::TM_OPAQUE)
      {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
      }
      else
      {
        glDisable(GL_BLEND);
      }

      glColor4f(1.0f,1.0f,1.0f,1.0f);

      float w=1.f;
      float h=1.f;
      glBegin(GL_QUADS);
      glTexCoord2f(0.0f, 1.0f); glVertex2f(-w,-h);				// Top Left
      glTexCoord2f(1.0f, 1.0f); glVertex2f( w,-h);				// Top Right
      glTexCoord2f(1.0f, 0.0f); glVertex2f( w, h);				// Bottom Right
      glTexCoord2f(0.0f, 0.0f); glVertex2f(-w, h);				// Bottom Left
      glEnd();

    }

    // -------------------------------------------------------------------------
    // draw cube

    glLoadIdentity();
    glTranslatef(-0.25f,-0.25f,-0.5f);
    glRotatef(timeGetTime() / 50.f,1.0f,0.0f,0.0f);
    glRotatef(timeGetTime() / 50.f,0.0f,1.0f,0.0f);
    glRotatef(timeGetTime() / 50.f,0.0f,0.0f,1.0f);

    float seg_size = 0.1f;

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glColor4f(1,1,1,0.5f);

    glBegin(GL_QUADS);
    glBindTexture(GL_TEXTURE_2D, m_textures[0]);

    glTexCoord2f(0.0f, 1.0f);glVertex3f( seg_size, seg_size,-seg_size);	// Top Right Of The Quad (Top)
    glTexCoord2f(1.0f, 1.0f);glVertex3f(-seg_size, seg_size,-seg_size);	// Top Left Of The Quad (Top)
    glTexCoord2f(1.0f, 0.0f);glVertex3f(-seg_size, seg_size, seg_size);	// Bottom Left Of The Quad (Top)
    glTexCoord2f(0.0f, 0.0f);glVertex3f( seg_size, seg_size, seg_size);	// Bottom Right Of The Quad (Top)

    if(m_view.size() > 1 ) glBindTexture(GL_TEXTURE_2D, m_textures[1]);
    glTexCoord2f(0.0f, 1.0f);glVertex3f( seg_size,-seg_size, seg_size);	// Top Right Of The Quad (Bottom)
    glTexCoord2f(1.0f, 1.0f);glVertex3f(-seg_size,-seg_size, seg_size);	// Top Left Of The Quad (Bottom)
    glTexCoord2f(1.0f, 0.0f);glVertex3f(-seg_size,-seg_size,-seg_size);	// Bottom Left Of The Quad (Bottom)
    glTexCoord2f(0.0f, 0.0f);glVertex3f( seg_size,-seg_size,-seg_size);	// Bottom Right Of The Quad (Bottom)

    if(m_view.size() > 2 ) glBindTexture(GL_TEXTURE_2D, m_textures[2]);
    glTexCoord2f(0.0f, 1.0f);glVertex3f( seg_size, seg_size, seg_size);	// Top Right Of The Quad (Front)
    glTexCoord2f(1.0f, 1.0f);glVertex3f(-seg_size, seg_size, seg_size);	// Top Left Of The Quad (Front)
    glTexCoord2f(1.0f, 0.0f);glVertex3f(-seg_size,-seg_size, seg_size);	// Bottom Left Of The Quad (Front)
    glTexCoord2f(0.0f, 0.0f);glVertex3f( seg_size,-seg_size, seg_size);	// Bottom Right Of The Quad (Front)

    if(m_view.size() > 3 ) glBindTexture(GL_TEXTURE_2D, m_textures[3]);
    glTexCoord2f(0.0f, 1.0f);glVertex3f( seg_size,-seg_size,-seg_size);	// Top Right Of The Quad (Back)
    glTexCoord2f(1.0f, 1.0f);glVertex3f(-seg_size,-seg_size,-seg_size);	// Top Left Of The Quad (Back)
    glTexCoord2f(1.0f, 0.0f);glVertex3f(-seg_size, seg_size,-seg_size);	// Bottom Left Of The Quad (Back)
    glTexCoord2f(0.0f, 0.0f);glVertex3f( seg_size, seg_size,-seg_size);	// Bottom Right Of The Quad (Back)

    if(m_view.size() > 4 ) glBindTexture(GL_TEXTURE_2D, m_textures[4]);
    glTexCoord2f(0.0f, 1.0f);glVertex3f(-seg_size, seg_size, seg_size);	// Top Right Of The Quad (Left)
    glTexCoord2f(1.0f, 1.0f);glVertex3f(-seg_size, seg_size,-seg_size);	// Top Left Of The Quad (Left)
    glTexCoord2f(1.0f, 0.0f);glVertex3f(-seg_size,-seg_size,-seg_size);	// Bottom Left Of The Quad (Left)
    glTexCoord2f(0.0f, 0.0f);glVertex3f(-seg_size,-seg_size, seg_size);	// Bottom Right Of The Quad (Left)

    if(m_view.size() > 5 ) glBindTexture(GL_TEXTURE_2D, m_textures[5]);
    glTexCoord2f(0.0f, 1.0f);glVertex3f( seg_size, seg_size,-seg_size);	// Top Right Of The Quad (Right)
    glTexCoord2f(1.0f, 1.0f);glVertex3f( seg_size, seg_size, seg_size);	// Top Left Of The Quad (Right)
    glTexCoord2f(1.0f, 0.0f);glVertex3f( seg_size,-seg_size, seg_size);	// Bottom Left Of The Quad (Right)
    glTexCoord2f(0.0f, 0.0f);glVertex3f( seg_size,-seg_size,-seg_size);	// Bottom Right Of The Quad (Right)
    glEnd();

    glDisable(GL_BLEND);

    // -------------------------------------------------------------------------
    // -------------------------------------------------------------------------

    glDisable(GL_TEXTURE_2D);
    SwapBuffers(g_hdc);
  }

  void update()
  {
    drawAllWebViews();

    m_core->update();
  }

  void onChangeTargetURL(const std::string& url)
  {
    std::cout << "Changing target URL: " << url << std::endl;
  }

  void onBeginNavigation(const std::string& url, const std::wstring& frameName)
  {
    std::cout << "Navigating to URL: " << url << std::endl;
  }

  void onBeginLoading(const std::string& url, const std::wstring& frameName, int statusCode, const std::wstring& mimeType)
  {
    std::cout << "Begin Loading URL: " << url << "\n\twith a status code of: " << statusCode << "\n\tand a mime-type of: ";
    std::wcout << mimeType << std::endl;
  }

  void onFinishLoading()
  {
    std::cout << "Finished Loading" << std::endl;
  }

  void onCallback(const std::string& name, const Awesomium::JSArguments& args)
  {
    std::cout << "callBack" << std::endl;

    if(name == "requestMsgFromJavascript")
    {
      for(unsigned i=0;i<m_view.size();i++)
      {
        m_view[i]->setProperty("msg", m_drawCount);
        m_view[i]->executeJavascript("updateMsgFromCpp()");
      }
    }
  }

  void onReceiveTitle(const std::wstring& title, const std::wstring& frameName)
  {
    std::wcout << _T("Receieved Title: ") << title << std::endl;
  }

  void onChangeCursor(HCURSOR cursor)
  {
    currentCursor = cursor;
    SetCursor(currentCursor);
  }

  void onChangeTooltip(const std::wstring& tooltip)
  {
    if(tooltip.length())
      std::wcout << "Tooltip: " << tooltip << std::endl;
  }

  void onChangeKeyboardFocus(bool isFocused)
  {
    if(isFocused)
      std::cout << "Keyboard is Focused" << std::endl;
    else
      std::cout << "Keyboard is Un-Focused" << std::endl;
  }

  // Quick and dirty, does not take orientation and camera position in account
  bool webPlaneHitTest(int mouse_x, int mouse_y, int window_width, int window_height, int& outX, int& outY)
  {
    outX = ( mouse_x * m_width )/ window_width;
    outY = ( mouse_y * m_height ) / window_height;
    return true;
  }

  bool mouseMoved(int x,int y)
  {
    int localX, localY;

    if(webPlaneHitTest(x, y, m_width, m_height, localX, localY))
    {
      for(unsigned i=0;i<m_view.size();i++)
        m_view[i]->injectMouseMove(localX, localY);
    }

    SetCursor(currentCursor);
    return true;
  }

  bool mouseWheel(int x, int y)
  {
    //std::wcout << _T("Inject mouse wheel") << std::endl;
    for(unsigned i=0;i<m_view.size();i++)
      m_view[i]->injectMouseWheelXY(x, y);

    return true;
  }

  bool mousePressed(MouseButton mb)
  {
    //std::wcout << _T("Inject mousedown") << std::endl;
    for(unsigned i=0;i<m_view.size();i++)
      m_view[i]->injectMouseDown(mb);

    return true;
  }

  bool mouseReleased(MouseButton mb)
  {
    //std::wcout << _T("Inject mouseup") << std::endl;
    for(unsigned i=0;i<m_view.size();i++)
      m_view[i]->injectMouseUp(mb);

    return true;
  }

  void handleKeyMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  {
    //std::wcout << _T("handleKeyMessage ") << msg <<  std::endl;

    for(unsigned i=0;i<m_view.size();i++)
    {
      //std::wcout << _T("Inject KeyboardEvent") <<  std::endl;
      m_view[i]->injectKeyboardEvent(hwnd, msg, wParam, lParam);
    }

    if( msg == WM_KEYDOWN)
    {
      switch(wParam)
      {

      // handle shortcuts

      case VK_ESCAPE:
        {
          if (!m_view.empty())
          {
            m_view.back()->loadFile(index_html_path);
          }
        }
        break;
      case VK_F2:
        {
          m_transparencyMode = m_transparencyMode + 1;
          if (m_transparencyMode == Awesomium::WebView::TM_MAX_VALUE)
            m_transparencyMode = 0;

          for(unsigned i=0;i<m_view.size();i++)
            m_view[i]->setTransparencyMode( static_cast<Awesomium::WebView::TransparencyMode>(m_transparencyMode) );

        };break;
      case VK_F3:
        {
          for(unsigned i=0;i<m_view.size();i++)
          {
            m_view[i]->refresh();
          }
        };break;
      case VK_F4:
        {
          for(unsigned i=0;i<m_view.size();i++)
          {
            m_view[i]->zoomIn();
          }
        };break;
      case VK_F5:
        {
          for(unsigned i=0;i<m_view.size();i++)
            m_view[i]->zoomOut();
        };break;

      case VK_F6:
        {
          // create a new webview
          createWebView(index_html_path,false);
          //createWebView("resources\\inspector\\devtools.html",false);
        };break;

      
      // handle navigation

      case VK_PRIOR:
        {
          m_angleView[0] += 1.f;
        };break;
      case VK_NEXT:
        {
          m_angleView[0] -= 1.f;
        };break;
      case VK_NUMPAD9:
        {
          m_angleView[1] += 1.f;
        };break;
      case VK_NUMPAD7:
        {
          m_angleView[1] -= 1.f;
        };break;
      case VK_END:
        {
          m_angleView[2] += 1.f;
        };break;
      case VK_HOME:
        {
          m_angleView[2] -= 1.f;
        };break;
      case VK_NUMPAD0:
        {
          m_positionView[0] = m_positionView[1] = 0.f; m_positionView[2] = -1.f;
          m_angleView[0] = m_angleView[1] = m_angleView[2] = 0.f;
        }
        break;
      case VK_NUMPAD4:
        {
          m_positionView[0] += 0.1f;
        };break;
      case VK_NUMPAD6:
        {
          m_positionView[0] -= 0.1f;
        };break;
      case VK_SUBTRACT:
        {
          m_positionView[1] += 0.1f;
        };break;
      case VK_ADD:
        {
          m_positionView[1] -= 0.1f;
        };break;
      case VK_NUMPAD8:
        {
          m_positionView[2] += 0.1f;
        };break;
      case VK_NUMPAD2:
        {
          m_positionView[2] -= 0.1f;
        };break;

      };
    }
  }

  // from WebView::MessageLogger
  virtual void log(const char* message)
  {
    printf("[ JS] %s\n", message);
  }

};

// ---------------------------------------------------------------------
// ---------------------------------------------------------------------

BOOL CreateGLWindow(TCHAR* title, int width, int height)
{
  GLuint		PixelFormat;
  WNDCLASS	wc;
  DWORD		dwExStyle;
  DWORD		dwStyle;
  RECT		WindowRect;

  WindowRect.left=(long)0;
  WindowRect.right=(long)width;
  WindowRect.top=(long)0;
  WindowRect.bottom=(long)height;

  g_hInstance			  = GetModuleHandle(NULL);
  wc.style			    = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  wc.lpfnWndProc	  = WndProc;
  wc.cbClsExtra		  = 0;
  wc.cbWndExtra		  = 0;
  wc.hInstance		  = g_hInstance;
  wc.hIcon			    = LoadIcon(NULL, IDI_WINLOGO);
  wc.hCursor			  = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground	= NULL;
  wc.lpszMenuName		= NULL;
  wc.lpszClassName	= CLASSNAME;

  if (!RegisterClass(&wc))
  {
    messageOutput(_T("RegisterClass(...) error"));
    return FALSE;
  }

  dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
  dwStyle=WS_OVERLAPPEDWINDOW  | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

  RECT oldWindowRect = WindowRect;
  AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);

  // Create The Window
  if (!(g_hWnd=CreateWindowEx(	dwExStyle,
    CLASSNAME,
    title,
    dwStyle,
    CW_USEDEFAULT, CW_USEDEFAULT,
    WindowRect.right - WindowRect.left,
    WindowRect.bottom - WindowRect.top,
    NULL,
    NULL,
    g_hInstance,
    NULL)))
  {
    _tprintf(_T("CreateWindowEx(...) error %d\n"), GetLastError());
    return FALSE;
  }

  static	PIXELFORMATDESCRIPTOR pfd=
  {
    sizeof(PIXELFORMATDESCRIPTOR),
    1,
    PFD_DRAW_TO_WINDOW |
    PFD_SUPPORT_OPENGL |
    PFD_DOUBLEBUFFER,
    PFD_TYPE_RGBA,
    32,
    0, 0, 0, 0, 0, 0,
    0,
    0,
    0,
    0, 0, 0, 0,
    16,
    0,
    0,
    PFD_MAIN_PLANE,
    0,
    0, 0, 0
  };

  if (!(g_hdc=GetDC(g_hWnd)))							// Did We Get A Device Context?
  {
    messageOutput(_T("GetDC(...) error"));
    return FALSE;
  }

  if (!(PixelFormat=ChoosePixelFormat(g_hdc,&pfd)))	// Did Windows Find A Matching Pixel Format?
  {
    messageOutput(_T("ChoosePixelFormat(...) error"));
    return FALSE;
  }

  if(!SetPixelFormat(g_hdc,PixelFormat,&pfd))
  {
    messageOutput(_T("SetPixelFormat(...) error"));
    return FALSE;
  }

  if (!(g_hRC=wglCreateContext(g_hdc)))
  {
    messageOutput(_T("wglCreateContext(...) error"));
    return FALSE;
  }

  if(!wglMakeCurrent(g_hdc,g_hRC))
  {
    messageOutput(_T("wglMakeCurrent(...) error"));
    return FALSE;
  }

  ShowWindow(g_hWnd,SW_SHOW);
  SetForegroundWindow(g_hWnd);
  SetFocus(g_hWnd);
  glViewport(0,0,width,height);

  return TRUE;
}

// ---------------------------------------------------------------------
// ---------------------------------------------------------------------

LRESULT CALLBACK WndProc(	HWND	hWnd,					// Handle For This Window
                         UINT	uMsg,					// Message For This Window
                         WPARAM	wParam,					// Additional Message Information
                         LPARAM	lParam)					// Additional Message Information
{
  GLdrawer* drawer = reinterpret_cast<GLdrawer*>(GetWindowLong(hWnd, GWL_USERDATA));

  if (drawer == NULL)
  {
    // as this is called buring CreateWindow, handle this 'gracefully'
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
  }

  switch (uMsg)
  {
  case WM_MOUSEWHEEL:
    {
      int y = GET_WHEEL_DELTA_WPARAM(wParam);
      drawer->mouseWheel(0, y);
    }
    break;

  case WM_MOUSEMOVE:
    {
      int xPos = LOWORD(lParam);
      int yPos = HIWORD(lParam);
      drawer->mouseMoved(xPos,yPos);
    }
    break;

  case WM_RBUTTONDOWN:
    {
      drawer->mousePressed(RIGHT_MOUSE_BTN);
    }
    break;

  case WM_MBUTTONDOWN:
    {
      drawer->mousePressed(MIDDLE_MOUSE_BTN);
    }
    break;

  case WM_LBUTTONDOWN:
    {
      drawer->mousePressed(LEFT_MOUSE_BTN);
    }
    break;

  case WM_RBUTTONUP:
    {
      drawer->mouseReleased(RIGHT_MOUSE_BTN);
    }
    break;

  case WM_MBUTTONUP:
    {
      drawer->mouseReleased(MIDDLE_MOUSE_BTN);
    }
    break;

  case WM_LBUTTONUP:
    {
      drawer->mouseReleased(LEFT_MOUSE_BTN);
    }
    break;

  case WM_SIZE:
    {
      drawer->resize( LOWORD(lParam), HIWORD(lParam) );
    }
    break;

  case WM_KEYDOWN:
  case WM_KEYUP:
  case WM_CHAR:
    //case WM_DEADCHAR:
  case WM_SYSKEYDOWN:
  case WM_SYSKEYUP:
    //case WM_SYSDEADCHAR:
  case WM_SYSCHAR:
  case WM_IME_CHAR:
    //case WM_IME_COMPOSITION:
    //case WM_IME_COMPOSITIONFULL:
    //case WM_IME_CONTROL:
    //case WM_IME_ENDCOMPOSITION:
    //case WM_IME_KEYDOWN:
    //case WM_IME_KEYUP:
    //case WM_IME_NOTIFY:
    //case WM_IME_REQUEST:
    //case WM_IME_SELECT:
    //case WM_IME_SETCONTEXT:
    //case WM_IME_STARTCOMPOSITION:
    //case WM_HELP:
    //case WM_CANCELMODE:
    {
      drawer->handleKeyMessage(hWnd, uMsg, wParam, lParam);
    };
    break;

  case WM_CLOSE:								// Did We Receive A Close Message?
    {
      PostQuitMessage(0);						// Send A Quit Message
    };
    break;

  default:
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
    break;
  }
  return 0;
}

// ---------------------------------------------------------------------
// ---------------------------------------------------------------------

int _tmain(int argc, TCHAR* argv[])
{
  g_application_path = argv[0];

  if (argc < 2)
  {
    _tprintf(_T("Usage :: '%s' <index_html_local_file>\n\n"), g_application_path);
    return 1;
  }

  index_html_path = argv[1];

  if (!CreateGLWindow(_T("GLexample viewer"), 800, 600))
  {
    messageOutput(_T("CreateGLWindow Failed"));
    return -1;
  }

  GLdrawer drawer(800, 600);
  ::SetWindowLong(g_hWnd, GWL_USERDATA, (LONG)&drawer);

  // create first webview

  drawer.createWebView(index_html_path, true);

  // main message loop

  bool done = false;
  while(!done)									// Loop That Runs While done=FALSE
  {
    MSG msg;
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
      do
      {
        if (msg.message == WM_QUIT)
        {
          done = true;
        }

        TranslateMessage(&msg);									// xlates virt keycodes
        DispatchMessage(&msg);									// Dispatches msg to window
      }
      while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
    }

    drawer.update();
  }

  // we should delete the OpenGL context, and close the window

  return 0;
}

