module Winit.FFI

import Derive.Prelude
import System.FFI

%language ElabReflection

--------------------------------------------------------------------------------
-- Utils
--------------------------------------------------------------------------------

lib : String -> String
lib fn = "C:" ++ fn ++ ",libbind_winit"

--------------------------------------------------------------------------------
-- Data
--------------------------------------------------------------------------------

public export
RawEventLoop : Type
RawEventLoop = AnyPtr

public export
RawWindowId : Type
RawWindowId = Int

public export
RawInitActionTag : Type
RawInitActionTag = Bits8

-- TODO add Vec type for "rest"
public export
RawInitActions : Type
RawInitActions = Struct "InitActions"
  [ ("fst", RawInitActionTag), ("rest", AnyPtr) ]

public export
RawEventActionTag : Type
RawEventActionTag = Bits8

export
RawEventAction : Type
RawEventAction = Struct "EventAction"
  [ ("tag", RawEventActionTag), ("close_window", RawWindowId) ]

-- TODO add Vec type for this
public export
RawEventActions : Type
RawEventActions = Struct "EventActions"
  [ ("ptr", AnyPtr), ("length", Int), ("capacity", Int) ]

public export
RawWindowEventTag : Type
RawWindowEventTag = Bits16

public export
RawWindowEvent : Type
RawWindowEvent = Struct "WindowEvent"
  [ ("tag", RawWindowEventTag), ("body", AnyPtr) ]

public export
data RawWindowEventKind =
  ActivationTokenDone |
  SurfaceResized |
  Moved |
  CloseRequested |
  Destroyed |
  DragEntered |
  DragMoved |
  DragDropped |
  DragLeft |
  Focused |
  KeyboardInput |
  ModifiersChanged |
  Ime |
  PointerMoved |
  PointerEntered |
  PointerLeft |
  MouseWheel |
  PointerButton |
  PinchGesture |
  PanGesture |
  DoubleTapGesture |
  RotationGesture |
  TouchpadPressure |
  ScaleFactorChanged |
  ThemeChanged |
  Occluded |
  RedrawRequested

%runElab derive "RawWindowEventKind" [Show]

export
windowEventTag : RawWindowEvent -> RawWindowEventTag
windowEventTag event = getField event "tag"

export
windowEventBody : RawWindowEvent -> AnyPtr
windowEventBody event = getField event "body"

export
windowEventKind : RawWindowEvent -> RawWindowEventKind
windowEventKind event =
  case windowEventTag event of
    0 => ActivationTokenDone
    1 => SurfaceResized
    2 => Moved
    3 => CloseRequested
    4 => Destroyed
    5 => DragEntered
    6 => DragMoved
    7 => DragDropped
    8 => DragLeft
    9 => Focused
    10 => KeyboardInput
    11 => ModifiersChanged
    12 => Ime
    13 => PointerMoved
    14 => PointerEntered
    15 => PointerLeft
    16 => MouseWheel
    17 => PointerButton
    18 => PinchGesture
    19 => PanGesture
    20 => DoubleTapGesture
    21 => RotationGesture
    22 => TouchpadPressure
    23 => ScaleFactorChanged
    24 => ThemeChanged
    25 => Occluded
    26 => RedrawRequested
    tag => assert_total $ idris_crash $ "Unexpected WindowEvent tag: " ++ show tag

public export
WindowEventBody_SurfaceResize : Type
WindowEventBody_SurfaceResize = Struct "SurfaceResize" [ ("a", Bits32), ("b", Bits32) ]


--------------------------------------------------------------------------------
-- Run
--------------------------------------------------------------------------------

export
%foreign (lib "run_app")
prim__runApp :
  RawEventLoop -> (PrimIO (Ptr RawInitActions)) -> (RawWindowId -> RawWindowEvent -> PrimIO (Ptr RawEventActions)) -> PrimIO ()

||| INVARIANT: The pointer must be used in `prim_runApp` to take care of freeing memory
export
%foreign (lib "init_actions_singleton")
initActionsSingleton : RawInitActionTag -> Ptr RawInitActions

||| INVARIANT: The pointer must be used in `prim_runApp` to take care of freeing memory
export
%foreign (lib "event_actions_empty")
eventActionsEmpty : Ptr RawEventActions

export
%foreign (lib "event_actions_push")
-- TODO take RawEventAction not RawEventActionTag
eventActionsPush : Ptr RawEventActions -> RawEventActionTag -> Ptr RawEventActions

--------------------------------------------------------------------------------
-- Event loop
--------------------------------------------------------------------------------

export
%foreign (lib "event_loop_new")
prim__mkEventLoop : PrimIO AnyPtr

export
%foreign (lib "event_loop_drop")
prim__dropEventLoop : AnyPtr -> PrimIO ()