// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file tools.license for terms.

#ifndef toolx_X11_session
#define toolx_X11_session

#include "base_session"

#include <GL/glx.h>

namespace toolx {
namespace X11 {

class session : public base_session {
  typedef base_session parent;
public:
  //virtual bool make_current(Window a_window) const {
  //  if(!m_display) return false;
  //  if(::glXMakeCurrent(m_display,a_window,m_ctx)==False) {
  //    m_out << "toolx::X11::session::make_current : glXMakeCurrent failed." << std::endl;
  //    return false;
  //  }
  //  return true;
  //}
  //virtual bool swap_buffers(Window a_window) const {
  //  if(!m_display) return false;
  //  ::glXSwapBuffers(m_display,a_window);
  //  return true;
  //}
public:
  session(std::ostream& a_out,unsigned int a_monitor = 0)
  :parent(a_out,a_monitor)
  ,m_vinfo(0)
  ,m_ctx(0)
  ,m_colormap(0)
  {
    if(!m_display) return;

   {int glxMajor, glxMinor;
    ::glXQueryVersion(m_display,&glxMajor,&glxMinor);
    if(glxMajor<=0) {
      m_out << "toolx::X11::session::session : bad GLX-Version " << glxMajor << "." << glxMinor << std::endl;
      ::XCloseDisplay(m_display);
      m_display = 0;
      m_vinfo = 0;
      m_ctx = 0;
      return;
    }}

    static const int atbs_alpha[] = {
      GLX_RGBA,
      GLX_RED_SIZE,   1,
      GLX_GREEN_SIZE, 1,
      GLX_BLUE_SIZE,  1,
      GLX_ALPHA_SIZE, 1,
      GLX_DEPTH_SIZE, 1,
      GLX_DOUBLEBUFFER,
      None};

    //NOTE : macOS : glXChooseVisual leaks 640 bytes.
    m_vinfo = ::glXChooseVisual(m_display,m_monitor,(int*)atbs_alpha);
    if(!m_vinfo) {
      //m_out << "toolx::X11::session::initialize :"
      //      << " can't get a visual with alpha on screen " << m_monitor << ". Try without alpha..."
      //      << std::endl;
      //m_out << "toolx::X11::session::initialize :"
      //      << " DefaultScreen(m_display): " << DefaultScreen(m_display) << "."
      //      << std::endl;

      static const int atbs[] = {
        GLX_RGBA,
        GLX_RED_SIZE,   1,
        GLX_GREEN_SIZE, 1,
        GLX_BLUE_SIZE,  1,
        GLX_DEPTH_SIZE, 1,
        GLX_DOUBLEBUFFER,
        None};

      m_vinfo = ::glXChooseVisual(m_display,m_monitor,(int*)atbs);
      if(!m_vinfo) {
        m_out << "toolx::X11::session::session :"
              << " can't choose a visual on screen " <<  m_monitor << "."
              << std::endl;
        ::XCloseDisplay(m_display);
        m_display = 0;
        m_vinfo = 0;
        m_ctx = 0;
        return;
      }
    }

    m_ctx = ::glXCreateContext(m_display,m_vinfo,NULL,GL_TRUE);
    if(!m_ctx) {
      m_out << "toolx::X11::session::session :"
            << " can't create a glX context with direct rendering."
            << std::endl;
      m_ctx = ::glXCreateContext(m_display,m_vinfo,NULL,GL_FALSE);
      if(!m_ctx) {
        m_out << "toolx::X11::session::session :"
              << " can't create a glX context."
              << std::endl;
        ::XCloseDisplay(m_display);
        m_display = 0;
        m_vinfo = 0;
        m_ctx = 0;
        return;
      }
  //} else {
      //m_out << "toolx::X11::session::session :"
      //      << " glX context with direct rendering created."
      //      << std::endl;
    }

    // It is better to create a colormap adapted to the visual.
    m_colormap = ::XCreateColormap(m_display,::XRootWindow(m_display,m_monitor),m_vinfo->visual,AllocNone);
    //m_colormap = ::XDefaultColormap(m_display,m_monitor);
    if(m_colormap==0L) {
      m_out << "toolx::X11::session::session : XCreateColormap failed." << std::endl;
      ::XCloseDisplay(m_display);
      m_display = 0;
      m_vinfo = 0;
      m_ctx = 0;
      return;
    }
  }
  virtual ~session() {
    if(m_display) {
      if(m_ctx) {
        ::glXDestroyContext(m_display,m_ctx);
        m_ctx = 0;
      }
      if(m_colormap) {
        ::XFreeColormap(m_display,m_colormap);
        m_colormap = 0;
      }
      ::XCloseDisplay(m_display);
      m_display = 0;
    }
    if(m_vinfo) {
      ::XFree(m_vinfo);
      m_vinfo = 0;
    }
    //std::cout << "debug : ~session" << std::endl;
  }
protected:
  session(const session& a_from)
  :parent(a_from)
  ,m_vinfo(0)
  ,m_ctx(0)
  ,m_colormap(0)
  {}
  session& operator=(const session& a_from){
    if(&a_from==this) return *this;
    parent::operator=(a_from);
    return *this;
  }
public:
  GLXContext context() const {return m_ctx;}

  Window create_window(const char* a_title,int a_x,int a_y,unsigned int a_width,unsigned int a_height) {
    if(!m_display) return 0L;

    XSetWindowAttributes swa;
    swa.event_mask = StructureNotifyMask | ExposureMask
       | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
       | PointerMotionMask
       | KeyPressMask;

    swa.colormap = m_colormap;
    swa.border_pixel = 0L;

    Window window = ::XCreateWindow(m_display,
                                    ::XRootWindow(m_display,m_monitor),
                                    a_x,a_y,a_width,a_height,
				    0,
                                    m_vinfo->depth,
                                    InputOutput,
                                    m_vinfo->visual,
				    CWBorderPixel|CWColormap|CWEventMask,&swa);

    if(window==0L) {
      m_out << "toolx::X11::session::create_window :"
            << " can't create a X11 window."
            << std::endl;
      return 0L;
    }

    XTextProperty tp;
    ::XStringListToTextProperty((char**)&a_title,1,&tp);
    XSizeHints sh;
    sh.flags = USPosition | USSize;
    ::XSetWMProperties(m_display,window,&tp,&tp,0,0,&sh,0,0);
    ::XFree(tp.value);

    ::XSetWMProtocols(m_display,window,&m_WM_DELETE_WINDOW_atom,1);
    return window;
  }
protected:
  XVisualInfo*  m_vinfo;
  GLXContext    m_ctx;
  Colormap      m_colormap;
};

}}

#endif

