
local C =  ffi.C

ffi.cdef [[
  typedef enum {
    GLFW_CURSOR = 0x00033001,
    GLFW_CURSOR_NORMAL = 0x00034001,
    GLFW_CURSOR_HIDDEN = 0x00034002,
    GLFW_CURSOR_DISABLED = 0x00034003
  } Constants;

  typedef struct GLFWwindow GLFWwindow;
  typedef void(*GLFWmousebuttonfun)(GLFWwindow*, int, int, int);
  typedef void(*GLFWcursorposfun)(GLFWwindow*, double, double);
  typedef void(*GLFWscrollfun)(GLFWwindow*, double, double);
  typedef void(*GLFWpentabletdatafun)(double, double, double, double, double,double,double);
  typedef void(*GLFWwindowfocusfun) (GLFWwindow *, int);
  typedef void(*GLFWwindowsizefun) (GLFWwindow *, int, int);
  typedef void(*GLFWframebuffersizefun) (GLFWwindow *, int, int);

  GLFWwindow* glfwGetCurrentContext(void);
  void glfwSetCursor  ( GLFWwindow *  window, GLFWcursor *  cursor );
  void glfwSwapBuffers (GLFWwindow *window);
  void glfwSwapInterval(int interval);
  void glfwSetWindowSize(GLFWwindow* window, int width,int height);
  void glfwGetInputMode(GLFWwindow* window, int mode);
  void glfwSetInputMode(GLFWwindow* window, int mode, int value);
  void glfwGetCursorPos(GLFWwindow* window, double* x, double* y);
  void glfwSetCursorPos(GLFWwindow* window, double x, double y);
  int glfwGetMouseButton(GLFWwindow* window, int button);
  void glfwGetWindowSize(GLFWwindow* window, int* width, int* height);
  GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun callback);
  GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun callback);
  GLFWcursorposfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback);
  GLFWpentabletdatafun glfwSetPenTabletDataCallback(GLFWpentabletdatafun callback);
  
  //GLFWwindowsizefun   glfwSetWindowSizeCallback (GLFWwindow *window, GLFWwindowsizefun cbfun);
  GLFWframebuffersizefun glfwSetFramebufferSizeCallback (GLFWwindow *window, GLFWframebuffersizefun callback);
  GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun callback);
]]

local window = C.glfwGetCurrentContext()

local mouse = {}

-- Lovr uses framebuffer scale for everything, but glfw uses window scale for events.
-- It is necessary to convert between the two at all boundaries.
function mouse.getScale()
  local x, _ = ffi.new('int[1]'), ffi.new('int[1]')
  C.glfwGetWindowSize(window, x, _)
  return screenWidth/x[0]
end

function mouse.getX()
  local x = ffi.new('double[1]')
  C.glfwGetCursorPos(window, x, nil)
  return x[0] * mouse.getScale()
end

function mouse.getY()
  local y = ffi.new('double[1]')
  C.glfwGetCursorPos(window, nil, y)
  if _flipView then 
    y[0] = y[0]
  end
  return y[0] * mouse.getScale()
end

function mouse.getPosition()
  local x, y = ffi.new('double[1]'), ffi.new('double[1]')
  local scale = mouse.getScale()
  C.glfwGetCursorPos(window, x, y)
  return x[0] * scale, y[0] * scale
end

function mouse.setX(x)
  local y = mouse.getY()
  local scale = mouse.getScale()
  C.glfwSetCursorPos(window, x/scale, y/scale)
end

function mouse.setY(y)
  local x = mouse.getX()
  local scale = mouse.getScale()
  C.glfwSetCursorPos(window, x/scale, y/scale)
end

function mouse.setPosition(x, y)
  local scale = mouse.getScale()
  C.glfwSetCursorPos(window, x/scale, y/scale)
end

function mouse.isDown(button, ...)
  if not button then return false end
  return C.glfwGetMouseButton(window, button - 1) > 0 or mouse.isDown(...)
end

function mouse.getRelativeMode()
  return mouse.isRelative
end

function mouse.setRelativeMode(enable)
  C.glfwSetInputMode(window, C.GLFW_CURSOR, enable and C.GLFW_CURSOR_DISABLED or C.GLFW_CURSOR_NORMAL)
  mouse.isRelative = enable
end

function gfx.setVsync(vsync)
  C.glfwSwapInterval(vsync and 1 or 0)
end

function gfx.setWindowSize(width,height)
  C.glfwSetWindowSize(window,width,height)
end

C.glfwSetMouseButtonCallback(window, function(target, button, action, mods)
  if target == window then
    local x, y = mouse.getPosition()
    _G.mouse[action > 0 and 'pressed' or 'released']( x, y, button + 1)
  end
end)

local px, py = 0,0
C.glfwSetCursorPosCallback(window, function(target, x, y)
  if target == window then
    local scale = mouse.getScale()
    x = x * scale
    y = y * scale
    _G.mouse.mousemoved( x, y, x - px, y - py, false)
    px, py = x, y
  end
end)

C.glfwSetPenTabletDataCallback(function(x,y,z,pressure,pitch,yaw,roll)
  _G.mouse.penmoved(x,y,z,pressure,pitch,yaw,roll)
end)

C.glfwSetWindowFocusCallback(window,function(window,int)
  engine.focus(int==1)
end)

C.glfwSetFramebufferSizeCallback(window,function(window,w,h)
    -- print("SIZE",w,h)
    if not CURRENT_PROJECT or animationPanel then 
      -- engine.resize(w,h)
      resizingWindow = true
      local dx,dy = w-screenWidth , h-screenHeight
      print("dx,dy",dx,dy)
      screenWidth , screenHeight  = w,h
      game.aspectRatio = w/h
      renderWidth   = screenWidth*game.resolution
      renderHeight  = screenHeight*game.resolution
      renderScale = screenWidth/renderWidth
      renderScaleY = screenHeight/renderHeight
      engine.update(rl.GetFrameTime())
      rl.rlLoadIdentity()
      rl.rlTranslatef(-dx,-dy, 0)
      engine.render()
      rl.rlglDraw()
      C.glfwSwapBuffers(window)
      renderScaleY = nil
    end
end)


C.glfwSetScrollCallback(window, function(target, x, y)
  if target == window then
    local scale = mouse.getScale()
    _G.mouse.wheelmoved( x*scale, y*scale)
  end
end)

return mouse
