library ndkopengltest;

uses cmem,gles,egl,ctypes,
     native_activity,native_window,looper,input,
     android_native_app_glue,
     log;

type
  Psaved_state = ^Tsaved_state;
  Tsaved_state = packed record
    angle : cfloat;
    x : cint32;
    y : cint32;
  end;

  Pengine = ^Tengine;
  Tengine = packed record
    app : Pandroid_app;
    animating : cint;
    display : EGLDisplay;
    surface : EGLSurface;
    context : EGLContext;
    width : cint32;
    height : cint32;
    state : Tsaved_state;
  end;

const
 attribs: array[0..8] of EGLint = (
           EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
           EGL_BLUE_SIZE, 8,
           EGL_GREEN_SIZE, 8,
           EGL_RED_SIZE, 8,
           EGL_NONE);

function engine_init_display(engine: Pengine): cint;
var w, h, dummy, format,numConfigs: EGLint;
    config: EGLConfig;
    surface: EGLSurface;
    context: EGLContext;
    display: Pointer;
begin
   // initialize OpenGL ES and EGL

   (*
    * Here specify the attributes of the desired configuration.
    * Below, we select an EGLConfig with at least 8 bits per color
    * component compatible with on-screen windows
     *)

   display := eglGetDisplay(EGL_DEFAULT_DISPLAY);

   eglInitialize(display, nil,nil);

(* Here, the application chooses the configuration it desires. In this
    * sample, we have a very simplified selection process, where we pick
    * the first EGLConfig that matches our criteria  *)

   eglChooseConfig(display, attribs, @config, 1, @numConfigs);

(* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
    * guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
    * As soon as we picked a EGLConfig, we can safely reconfigure the
    * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID.  *)

   eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, @format);

   ANativeWindow_setBuffersGeometry(engine^.app^.window, 0, 0, format);

   surface := eglCreateWindowSurface(display, config, engine^.app^.window, nil);
   context := eglCreateContext(display, config, nil, nil);

   if eglMakeCurrent(display, surface, surface, context) = EGL_FALSE then
   begin
       LOGW('Unable to eglMakeCurrent');
       exit(-1);
   end;

   eglQuerySurface(display, surface, EGL_WIDTH, @w);
   eglQuerySurface(display, surface, EGL_HEIGHT, @h);

   engine^.display := display;
   engine^.context := context;
   engine^.surface := surface;
   engine^.width := w;
   engine^.height := h;
   engine^.state.angle := 0;

   // Initialize GL state.
   glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
   glEnable(GL_CULL_FACE);
   glShadeModel(GL_SMOOTH);
   glDisable(GL_DEPTH_TEST);

   result := 0;
end;

procedure engine_draw_frame(engine: Pengine);
begin
   if engine^.display = nil then
      exit;

   // Just fill the screen with a color.
   glClearColor(engine^.state.x/engine^.width, engine^.state.angle, engine^.state.y/engine^.height, 1);
   glClear(GL_COLOR_BUFFER_BIT);

   eglSwapBuffers(engine^.display, engine^.surface);
end;


procedure engine_term_display(engine: Pengine);
begin
   if (engine^.display <> EGL_NO_DISPLAY) then
   begin
       eglMakeCurrent(engine^.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
       if (engine^.context <> EGL_NO_CONTEXT) then
           eglDestroyContext(engine^.display, engine^.context);
       if (engine^.surface <> EGL_NO_SURFACE) then
           eglDestroySurface(engine^.display, engine^.surface);
       eglTerminate(engine^.display);
   end;

   engine^.animating := 0;
   engine^.display := EGL_NO_DISPLAY;
   engine^.context := EGL_NO_CONTEXT;
   engine^.surface := EGL_NO_SURFACE;
end;

procedure engine_handle_cmd(app: Pandroid_app; cmd: cint32); cdecl;
var engine: Pengine;
begin
   engine := Pengine(app^.userData);
   case cmd of
      APP_CMD_SAVE_STATE:
         begin
            // The system has asked us to save our current state.  Do so.
            engine^.app^.savedState := malloc(sizeof(Tsaved_state));
            Psaved_state(engine^.app^.savedState)^ := engine^.state;
            engine^.app^.savedStateSize := sizeof(Tsaved_state);
         end;
      APP_CMD_INIT_WINDOW:
         begin
            // The window is being shown, get it ready.
            if (engine^.app^.window <> Nil) then
            begin
               LOGW('Initializing display');
               engine_init_display(engine);
               engine_draw_frame(engine);
            end;
         end;
      APP_CMD_TERM_WINDOW:
         begin
            // The window is being hidden or closed, clean it up.
            engine_term_display(engine);
         end;
      APP_CMD_GAINED_FOCUS:
         begin
            // When our app gains focus, we start monitoring the accelerometer.
            {if (engine^.accelerometerSensor <> Nil) then
            begin
               ASensorEventQueue_enableSensor(engine^.sensorEventQueue, engine^.accelerometerSensor);
               // We'd like to get 60 events per second (in us).
               ASensorEventQueue_setEventRate(engine^.sensorEventQueue, engine^.accelerometerSensor, (1000L/60)*1000);
            end;}
         end;
      APP_CMD_LOST_FOCUS:
         begin
            // When our app loses focus, we stop monitoring the accelerometer.
            // This is to avoid consuming battery while not being used.
            {if engine^.accelerometerSensor <> NULL then
               ASensorEventQueue_disableSensor(engine^.sensorEventQueue, engine^.accelerometerSensor);}
            // Also stop animating.
            engine^.animating := 0;
            engine_draw_frame(engine);
         end;
   end;
end;

function engine_handle_input(app: Pandroid_app; event: PAInputEvent): cint32; cdecl;
var engine: Pengine;
begin
   engine := Pengine(app^.userData);
   if AInputEvent_getType(event) = AINPUT_EVENT_TYPE_MOTION then
   begin
      engine^.animating := 1;
      {engine^.state.x := AMotionEvent_getX(event, 0);
      engine^.state.y := AMotionEvent_getY(event, 0);}
      result := 1;
   end
   else
      result := 0;
end;

procedure android_main(state: Pandroid_app); cdecl; export;
var engine: Tengine;
    ident,events: cint;
    source: Pandroid_poll_source;
    val: cint;
begin
   // Make sure glue isn't stripped.
   app_dummy();
   LOGW('Android main!');

   FillChar(engine, sizeof(Tengine), 0);
   LOGW('Android main 2!');

   state^.userData := @engine;
   state^.onAppCmd := @engine_handle_cmd;
   state^.onInputEvent := @engine_handle_input;
   engine.app := state;
   LOGW('Android main 3!');

   if state^.savedState <> nil then
      // We are starting with a previous saved state; restore from it.
      engine.state := Psaved_state(state^.savedState)^;

   LOGW('Entering loop');
   // loop waiting for stuff to do.

   while true do
   begin// Read all pending events.
      // If not animating, we will block forever waiting for events.
      // If animating, we loop until all events are read, then continue
      // to draw the next frame of animation.

      if engine.animating<>0 then
         val := 0
      else
         val := -1;
      ident := ALooper_pollAll(val, nil, @events,@source);
      while (ident >= 0) do
      begin
         // Process this event.
         if (source <> nil) then
            source^.process(state, source);

         // If a sensor has data, process it now.
         if (ident = LOOPER_ID_USER) then
         begin
            {if (engine.accelerometerSensor != nil) then
            begin
               ASensorEvent event;
               while (ASensorEventQueue_getEvents(engine.sensorEventQueue, &event, 1) > 0) do
               begin
                  LOGI("accelerometer: x=%f y=%f z=%f",
                          [event.acceleration.x, event.acceleration.y,
                          event.acceleration.z]);
               end;
            end;}
         end;

         // Check if we are exiting.
         if (state^.destroyRequested <> 0) then
         begin
            LOGW('Destroy requested');
            engine_term_display(@engine);
            exit;
         end;

         if engine.animating<>0 then
            val := 0
         else
            val := -1;
         ident := ALooper_pollAll(val, nil, @events,@source);
      end;

      if engine.animating <> 0 then
      begin
         // Done with events; draw next animation frame.
         engine.state.angle := engine.state.angle + 0.01;
         if (engine.state.angle > 1) then
            engine.state.angle := 0;
      end;

      // Drawing is throttled to the screen update rate, so there
      // is no need to do timing here.
      engine_draw_frame(@engine);
   end;
end;

// This code is unnecessary in FPC 2.6+,
// it was required when the 2.5.1 snapshot was created
{$ifdef ver2_5}
procedure PASCALMAIN; external name 'PASCALMAIN';

procedure FPC_SHARED_LIB_START; [public, alias: 'FPC_SHARED_LIB_START'];
begin
   PASCALMAIN;
end;
{$endif}

exports //android_main name 'android_main',
        ANativeActivity_onCreate name 'ANativeActivity_onCreate';

end.
