//
// Copyright (C) 1999-2002 Toshikaz Hirabayashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// TOSHIKAZ HIRABAYASHI BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
// OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Except as contained in this notice, the name of Toshikaz Hirabayashi shall
// not be used in advertising or otherwise to promote the sale, use or other
// dealings in this Software without prior written authorization from
// Toshikaz Hirabayashi.

#include <WScom.h>
#include <WSCopenglForm.h>

#ifdef MSW
#define _WINGDI_
#include <windows.h>
#endif
#include <GL/gl.h>
#ifndef MacOS
#include <GL/glut.h>
#else //MacOS
#include <GLUT/glut.h>
#endif //MacOS

#ifndef MSW
#include <X11/Xlib.h>
#include <GL/glx.h>
#endif //MSW

#include <WSCclassInformation.h>
#include <WSCdevice.h>
#include <WSCblink.h>
#include <WSCcolorSet.h>
#include <WSCimageSet.h>
#include <WSDdev.h>
#include <WSDcolor.h>
#include <WSCfontSet.h>
#include <WSDappDev.h>

extern "C" {
void libwsopengl_func();
};
void libwsopengl_func(){
}


WSMFguiClassInitialize(WSCopenglForm, WSCform);
WSMFversion(WSCopenglForm, WSCform);

WSCopenglForm::WSCopenglForm(WSCbase* base, char* objname):
			WSCform(base, objname){
  _create_context_handler = NULL;
  _update_context_handler = NULL;
  _opengl_context = NULL;
  _begin_draw_fl = 0;
  _context_created_fl = 0;
#ifndef MSW
  _double_buffer = False;
#else //MSW
  _double_buffer = True;
#endif //MSW
  _pixmap_style = WS_DIRECT_WINDOW;
  WSMFpropertyCreateStart
    WSMFparentCheckVerSrc(WSCopenglForm);
    WSMFpropertyDelete(WSNpixmapStyle);

  WSMFpropertyCreateEnd

}
WSCopenglForm::~WSCopenglForm(){
  if (_opengl_context != NULL){
#ifndef MSW
    WSDdev* dev = getdev();
    if (dev != NULL){
      Display* disp = (Display*)dev->getDeviceResource();
      glXDestroyContext(disp,(GLXContext) _opengl_context);
    }
#else //MSW
    wglMakeCurrent(NULL, NULL);
    wglDeleteContext((HGLRC)_opengl_context);
#endif //MSW
    _opengl_context = NULL;
  }
}
void WSCopenglForm::_delete_context(){
  if (_opengl_context != NULL){
#ifndef MSW
    WSDdev* dev = getdev();
    if (dev != NULL){
      Display* disp = (Display*)dev->getDeviceResource();
      glXDestroyContext(disp,(GLXContext) _opengl_context);
    }
#else //MSW
    wglMakeCurrent(NULL, NULL);
    wglDeleteContext((HGLRC)_opengl_context);
#endif //MSW
    _opengl_context = NULL;
  }
}

void WSCopenglForm::setCreateContextHandler(long (*hd)()){
  _create_context_handler = hd;
}
void WSCopenglForm::setUpdateContextHandler(long (*hd)()){
  _update_context_handler = hd;
}

long WSCopenglForm::_create_context(){
  WSDdev* dev = getdev();
  if (dev == NULL){
    return WS_ERR;
  }
  _context_created_fl = True;
  if (_create_context_handler != NULL){
    return _create_context_handler();
  }
  return _default_create_context();
}
long WSCopenglForm::_default_create_context(){
  WSDdev* dev = getdev();
  if (dev == NULL){
    return WS_ERR;
  }
#ifndef MSW
  Window win = (Window)dev->getWindowResource();
  Display* disp = (Display*)dev->getDeviceResource();
//printf("create_context w=0x%x disp=0x%x\n",win,disp);
  //
  // OpenGL initialization
  //
  if(!glXQueryExtension(disp, NULL, NULL)){
    printf("Can't use GLX Extention.\n");
    return WS_ERR;
  }
  int snglBuf[] = {GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1,
                   GLX_BLUE_SIZE, 1, GLX_DEPTH_SIZE, 12, None};
  int dblBuf[]  = {GLX_RGBA, GLX_RED_SIZE, 4, GLX_GREEN_SIZE, 4,
                   GLX_BLUE_SIZE, 4, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None};
  int dblBuf2[]  = {GLX_RGBA, GLX_RED_SIZE, 4, GLX_GREEN_SIZE, 4,
                   GLX_BLUE_SIZE, 4, GLX_DEPTH_SIZE, 16, GLX_DOUBLEBUFFER, None};
  int dblBuf3[]  = {GLX_RGBA, GLX_RED_SIZE, 4, GLX_GREEN_SIZE, 4,
                   GLX_BLUE_SIZE, 4, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, None};
  XVisualInfo *visual_info;
  visual_info = glXChooseVisual(disp, DefaultScreen(disp), dblBuf);
  if(visual_info == NULL){
    visual_info = glXChooseVisual(disp, DefaultScreen(disp), dblBuf2);
  }
  if(visual_info == NULL){
    visual_info = glXChooseVisual(disp, DefaultScreen(disp), dblBuf3);
  }
  if(visual_info == NULL){
    visual_info = glXChooseVisual(disp, DefaultScreen(disp), snglBuf);
    if(visual_info == NULL){
      printf("Can't open Double nor Single buffer for OpenGL.\n");
      return WS_ERR;
    }
    _double_buffer = False;
  }else{
    _double_buffer = True;
  }
  if(visual_info->c_class != TrueColor){
    printf("The XVisual isn't TrueColor.\n");
    return WS_ERR;
  }

  _opengl_context = glXCreateContext(disp, visual_info, None, True);
  if(_opengl_context == NULL){
    printf("Can't open rendering context.\n");
    return WS_ERR;
  }
  XFree(visual_info);

  glXMakeCurrent(disp, win, (GLXContext)_opengl_context);

#else //MSW
  PIXELFORMATDESCRIPTOR pfd =
  {
    sizeof(PIXELFORMATDESCRIPTOR),
    1,
    PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
    PFD_TYPE_RGBA, 24,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0,
    PFD_MAIN_PLANE,
    0, 0, 0, 0
  };

  HWND win = (HWND)dev->getWindowResource();
//  HDC disp = (HDC)dev->getContextResource();
  HDC disp = GetDC(win);
  setUserData("HDC",(void*)disp);

  if(disp == NULL){
    printf("Can't get device context.\n");
    return WS_ERR;
  }

  int pixelFormat = ChoosePixelFormat(disp,&pfd);
  if(pixelFormat == 0){
    printf("Can't get pixelFormat.\n");
    return WS_ERR;
  }

  SetPixelFormat(disp, pixelFormat, &pfd);

  _opengl_context = wglCreateContext(disp);

  if(_opengl_context == NULL){
    printf("wglCreateContext fails..\n");
    return WS_ERR;
  }
  //wglMakeCurrent(disp, (HGLRC)_opengl_context);

#endif //MSW
  WSCrect rect;
  rect.x = 0;
  rect.y = 0;
  rect.width = _w;
  rect.height = _h;
  onResize(&rect);

  return WS_NO_ERR;
}

void WSCopenglForm::onVisibleChange(WSCbool vis){
  WSCform::onVisibleChange(vis);
  WSCbool fl = getVisible();
  if (vis == False){
    _delete_context();
  }else if (fl != False){
//printf("WSCopenglForm::onVisibleChange _create_context..\n");
    _create_context();
  }

}

void* WSCopenglForm::getOpenGLContext(){
  return _opengl_context;
}
long WSCopenglForm::draw(){
  return WSCform::draw();
}
void WSCopenglForm::update(){
  WSCform::update();
}
long WSCopenglForm::beginDraw(){
  if (getVisible() == False){
    return WS_NO_ERR;
  }
  WSDdev* dev = getowndev();
  if (dev == NULL){
    return WS_ERR;
  }

//  short x = _x;
//  short y = _y;
  WSCushort w = _w;
  WSCushort h = _h;

  WSCbool absolute = getAbsoluteDraw();
  if (absolute == False && dev->isExposed(0, 0, w, h) == False){
    return WS_ERR;
  }
  long err = dev->beginDraw(0, 0, w, h, absolute);
  if (err != WS_NO_ERR){
    return WS_ERR;
  }
  WSCbase::update();
  setAbsoluteDraw(False);
  _begin_draw_fl =1;


#ifndef MSW
  Window win = (Window)dev->getWindowResource();
  Display* disp = (Display*)dev->getDeviceResource();
  glXMakeCurrent(disp, win, (GLXContext)_opengl_context);
#else
  HDC disp = (HDC)getUserData("HDC");
  wglMakeCurrent(disp, (HGLRC)_opengl_context);
#endif

  //fprintf(stderr, "_begin_draw\n");


  return WS_NO_ERR;
}
long WSCopenglForm::endDraw(){
//printf("SwapBuffers... here1\n");
  if(_double_buffer == True){
#ifndef MSW
    WSDdev* dev = getdev();
    if (dev == NULL){
//printf("err2\n");
      return WS_ERR;
    }
    Window win = (Window)dev->getWindowResource();
    Display* disp = (Display*)dev->getDeviceResource();

    glXMakeCurrent(disp, win, (GLXContext)_opengl_context);

    glXSwapBuffers(disp, win);
//printf("SwapBuffers\n");
#else
//printf("SwapBuffers... start\n");
//    WSDdev* dev = getdev();
//    if (dev == NULL){
//printf("err2\n");
//      return WS_ERR;
//    }
//    HDC disp = (HDC)dev->getContextResource();

    HDC disp = (HDC)getUserData("HDC");
    //wglMakeCurrent(disp, (HGLRC)_opengl_context);

    SwapBuffers(disp);

    wglMakeCurrent(disp, NULL);

//printf("SwapBuffers...\n");
#endif
  }else{
#ifndef MSW
    WSDdev* dev = getdev();
    Window win = (Window)dev->getWindowResource();
    Display* disp = (Display*)dev->getDeviceResource();
    glXMakeCurrent(disp, win, (GLXContext)_opengl_context);
#else
    //HDC disp = (HDC)getUserData("HDC");
    //wglMakeCurrent(disp, (HGLRC)_opengl_context);
#endif

    glFlush();
  }
  if (getVisible() == False){
    return WS_NO_ERR;
  }
  WSDdev* dev = getowndev();
  if (dev == NULL){
    return WS_ERR;
  }
  if (_begin_draw_fl ==1){
    dev->endDraw();
    _begin_draw_fl =0;
  }
  return WS_NO_ERR;
}



void WSCopenglForm::makeCurrent(){
#ifndef MSW
#else
  HDC disp = (HDC)getUserData("HDC");
  wglMakeCurrent(disp, (HGLRC)_opengl_context);
#endif
}
void WSCopenglForm::noMakeCurrent(){
#ifndef MSW
#else
  HDC disp = (HDC)getUserData("HDC");
  wglMakeCurrent(disp, NULL);
#endif
}


void WSCopenglForm::onResize(WSCrect* rect){
//printf("WSCopenglForm::onResize..\n");
  WSCform::onResize(rect);
  if (getVisible() == False){
    return;
  }
#ifndef MSW
  WSDdev* dev = getdev();
  if (dev == NULL){
    return;
  }
  if (_opengl_context != NULL){
    Window win = (Window)dev->getWindowResource();
    Display* disp = (Display*)dev->getDeviceResource();
    glXMakeCurrent(disp, win, (GLXContext)_opengl_context);
  }
#else
  HDC disp = (HDC)getUserData("HDC");
  wglMakeCurrent(disp, (HGLRC)_opengl_context);
#endif

  _update_context();

#ifndef MSW
#else
  wglMakeCurrent(disp, NULL);
#endif

}
long WSCopenglForm::_update_context(){
  if (_context_created_fl == False){
    return WS_NO_ERR;
  }
  if (_update_context_handler != NULL){
    return _update_context_handler();
  }
  return _default_update_context();
}
long WSCopenglForm::_default_update_context(){
  double va = (double)30; //TEST
  double ratio = (double)_w / _h;

//printf("WSCopenglForm::onResize ...w,h=%d,%d\n",_w,_h);
  glViewport(0,0, _w, _h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(va, (GLfloat)ratio, 1.0, 10000.0); // valid depth [1,10000]
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(0.0, 0.0, 0.0,    // camera position
            0.0, 0.0, -1.0,   // a point at which the camera is looking
            0.0, 1.0, 0.0);   // upside direction of the camera
  return WS_NO_ERR;
}
long WSCopenglForm::execEventProc(long ev,void* data){
  if (ev == WSEV_EXPOSE){
#ifndef MSW
#else
    HDC disp = (HDC)getUserData("HDC");
    wglMakeCurrent(disp, (HGLRC)_opengl_context);
#endif
    if (data != NULL){
      WSCrect* area = (WSCrect*)data;
      onExpose(area);
      draw();
      if (_expose_op == 1){
        if (beginDraw()== WS_NO_ERR){
          execProcedure(ev);
          endDraw();
        }
      }
#ifndef MSW
#else
      wglMakeCurrent(disp, NULL);
#endif
      return WS_NO_ERR;
    }else{
#ifndef MSW
#else
    HDC disp = (HDC)getUserData("HDC");
    wglMakeCurrent(disp, (HGLRC)_opengl_context);
#endif 
      WSCrect area;
      area.setRect(0,0,_w,_h);
      onExpose(&area);
      draw();
      if (_expose_op == 1){
        if (beginDraw()== WS_NO_ERR){
          execProcedure(ev);
          endDraw();
        }
      }
#ifndef MSW
#else
      wglMakeCurrent(disp, NULL);
#endif
      return WS_NO_ERR;
    }
  }
  return WSCform::execEventProc(ev,data);
}
