/* TODO(allen) - main.c
** 
** Functions that need to do log output end up with complicated
**  signatures or bad coupling of the arena usage and the log output.
**  The user is forced to manually manage the log in-out parameters.
**  Build a better logging system and update the functions labeled
**  with "Better logging here" TODOs.
**
** Unicode conversions should take string lists.
**  Find "Better unicode conversions here"
**
** EXPERIMENT IDEAS
**  Roll a codebase-wide custodian system
**  Arena header for dynamically interfacing arenas between 
**   fully separated layers
**
** RESEARCH
**  Is there any way to do OffsetOfMember without
**   relying on undefined behavior?
**
*/

// TODO(allen): 
//
// [ ] "render" should give a prescribed way for a user to
//     down shift into lower level APIs (i.e. writing opengl
//     or d3d11 directly).
//     Each backend would have a concrete 'downshift' API that
//     lets you seamlessly extract the parts of the system you
//     need. --- This would *HAVE* to be per-backend. There is
//     no way to describe downshifting into a lower level API
//     that does not involve coupling to the lower level API.
//     `` "glBlendFunc" statefulness compositional problems. ``
//     ^^ This problem is actually a specific case of what I
//        just described as a downshifting problem above.
//        Someone mixing their own features in with render
//        would need a prescribed way to restore *render*'s
//        chose defaults.
//
// [ ] optimization pass over opengl render backend
//
// [ ] UTF-8 decoding version of "draw_string"
//
// [ ] A way to check equipment
// [ ] "Check equipment here" (tagged in several locations)
// [ ] optimized (HWND -> GFX_Window) mapping
//
// [ ] How should the resize handler be written?
//     [ ] Make it look good when we resize
//     [ ] Does it need to know about the equipped renderer?

#include "base/base_inc.h"

#include "wave/riff_format.h"
#include "wave/wave_format.h"
#include "wave/wave_parser.h"
#include "temp_wave.h"
#include "audio_gen.h"
#include "art/color.h"

#include "font/font_types.h"
#include "font/font_parse_by_freetype.h"
#include "icon_codes.h"

#include "os/os_inc.h"
#include "gfx/gfx_inc.h"
#include "render/render_inc.h"

#include "d3d11/d3d11_inc.h"

#include "base/base_inc.c"
#include "base/base_big_functions.c"

#include "wave/wave_parser.c"
#include "temp_wave.c"
#include "audio_gen.c"
#include "art/color.c"

#include "font/font_types.c"
#include "font/font_parse_by_freetype.c"

#include "os/os_inc.c"
#include "gfx/gfx_inc.c"
#include "render/render_inc.c"


////////////////////////////////
// NOTE(allen): Main

function void
window_resize_handler(GFX_Window *window, U32 width, U32 height){
  ogl_begin(window);
  r_canvas_init(v2s32(width, height));
  r_show_canvas_on_activated_window();
  ogl_end();
}

#define color_linu32_from_srgba(r,g,b,a) \
color_linear_from_srgb_u32(color_u32_from_4f32(r,g,b,a))
#define color_linu32_from_srgba_v(v) \
color_linear_from_srgb_u32(color_u32_from_4f32(V4_EXPANDED(v)))

int
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR lpCmdLine,
        int nShowCmd){
  ProfOpen("profile.spall");
  ProfThreadBegin();
  ProfBegin("program-init");
  
  w32_WinMain_init(hInstance, hPrevInstance, lpCmdLine, nShowCmd);
  
  er_accum_begin();
  
  Arena *arena = arena_alloc();
  Arena *frame_arena = arena_alloc();
  
  // initialize graphics and rendering
  gfx_init();
  gfx_set_resizing_hook(window_resize_handler);
  r_init();
  
  // initialize font
#define FONT_COUNT 3
  R_Font *font[FONT_COUNT] = {0};
  
  {
    String8 font_names[FONT_COUNT] = {
      str8_lit("liberation-mono.ttf"),
      str8_lit("Exo-Regular.ttf"),
      str8_lit("icon_font.ttf"),
    };
    
    FONT_PackParams pack_params = {0};
    pack_params.max_glyph_side = 64;
    pack_params.initial_atlas_side = 64;
    pack_params.max_grow_count = 10;
    
    r_backend_begin(0);
    
    for (U64 i = 0; i < FONT_COUNT; i += 1){
      ArenaTemp scratch = arena_get_scratch(0, 0);
      
      String8 name = font_names[i];
      String8 data = os_file_read(scratch.arena, name);
      
      log_accum_begin();
      
      FONT_Loose *loose = font_parse_by_freetype(scratch.arena, data);
      font[i] = r_font_bake_and_build_texture(arena, loose, &pack_params);
      
      String8 log = log_accum_end(scratch.arena);
      if (log.size != 0){
        gfx_message_box(str8_lit("FreeType Log"), log);
      }
      
      arena_release_scratch(&scratch);
    }
    
    r_backend_end();
  }
  
  // setup a window
  GFX_Window *window = gfx_window_create();
  r_backend_equip_window(window);
  gfx_window_set_title(window, str8_lit("Render Window"));
  
  // calculate frame rate control factors
  U64 ratio_usecs = 0;
  U64 ratio_ticks = 0;
  os_microseconds_over_ticks(&ratio_usecs, &ratio_ticks);
  
  U64 frame_per_second = gfx_window_refresh_rate(window);
  
  U64 usec_per_frame = Million(1)/frame_per_second;
  U64 tick_per_frame = usec_per_frame*ratio_ticks/ratio_usecs;
  
  // convert to string
  String8 usec_per_frame_string = str8_pushf(arena, "%8llu", usec_per_frame);
  
  // previous frame length
  U64 frame_index = 0;
  
  // check for any errors during initialization
  {
    ArenaTemp scratch = arena_get_scratch(0, 0);
    String8 error = er_accum_end(scratch.arena);
    if (error.size != 0){
      gfx_message_box(str8_lit("Initialization Error"), error);
      os_exit_process(1);
    }
    arena_release_scratch(&scratch);
  }
  
  ProfEnd();
  
  // begin UI loop
  B32 loop_good = 1;
  U64 frame_begin_t = os_now_ticks();
  gfx_window_set_visible(window, 1);
  for (;loop_good;){
    // frame begin
    ProfBegin("frame-loop");
    er_accum_begin();
    arena_pop_to(frame_arena, 0);
    
    // calculate target end
    U64 frame_target_end_t = frame_begin_t + tick_per_frame;
    
    // wait for input
    if (!gfx_peek_input()){
      loop_good = 0;
      goto frame_end;
    }
    
    // draw gl window
    if (gfx_window_is_valid(window)){
      V2S32 window_dim = {0};
      {
        RectS32 rect = {0};
        if (gfx_window_get_inner_rect(window, &rect)){
          window_dim = dim_from_rects32(rect);
        }
      }
      
      r_backend_begin(window);
      r_canvas_init(window_dim);
      
      R_List rlist = {0};
      
      {
        U32 c0 = color_linu32_from_srgba(0.9f, 0.1f, 0.0f, 0.3f);
        U32 c1 = color_linu32_from_srgba(0.8f, 0.0f, 0.0f, 0.3f);
        U32 c2 = color_linu32_from_srgba(0.1f, 0.9f, 0.0f, 0.3f);
        U32 c3 = color_linu32_from_srgba(0.0f, 0.9f, 0.1f, 0.3f);
        U32 c4 = color_linu32_from_srgba(1.0f, 1.0f, 0.5f, 0.3f);
        U32 c5 = color_linu32_from_srgba(0.4f, 0.8f, 1.0f, 0.3f);
        
        R_Ctx rctx = r_ctx_make(frame_arena, &rlist);
        if (frame_index < 95){
          F32 clip_amt = frame_index*(1920.f/95.f);
          r_ctx_clip(&rctx, 1, rectf32(0, 0, clip_amt, clip_amt));
        }
        
        {
          R_Quad quad = {0};
          quad.xy = rectf32(400.f, 200.f, 430.f, 230.f);
          quad.radius = 500.f;
          quad.theta = tau_F32*0.125f;
          quad.c[0] = 0xFFFFFFFF;
          quad.c[1] = 0xFFFFFFFF;
          quad.flags = R_QuadFlag_SharpCornerBL;
          r_ctx_apply_clip(&rctx, &quad);
          r_quad(&rctx, &quad, 0);
        }
        
        r_rect(&rctx, rectf32(5.f, 5.f, 45.f, 45.f), 5.f, c0);
        r_rect(&rctx, rectf32(55.f, 5.f, 95.f, 45.f), 5.f, c0);
        
        r_rect(&rctx, rectf32(5.f, 50.f, 45.f, 70.f), 0.f, c2);
        r_rect(&rctx, rectf32(55.f, 50.f, 95.f, 70.f), 0.f, c3);
        
        r_ctx_font(&rctx, font[0]);
        //r_string(&rctx, str8_lit("Hello, World!!!"), v2f32(25.f, 50.f), c4);
        
        
        r_rect(&rctx, rectf32(5.f, 105.f, 45.f, 145.f), 5.f, c0);
        r_rect(&rctx, rectf32(55.f, 105.f, 95.f, 145.f), 5.f, c0);
        
        r_rect(&rctx, rectf32(5.f, 150.f, 45.f, 170.f), 0.f, c2);
        r_rect(&rctx, rectf32(55.f, 150.f, 95.f, 170.f), 0.f, c1);
        
        r_ctx_font(&rctx, font[1]);
        r_string(&rctx, str8_lit("Font Change!"), v2f32(25.f, 150.f), c4);
        
        r_ctx_font(&rctx, font[2]);
        r_char(&rctx, ICON_LOCK, v2f32(25.f, 250.f), c5);
        r_char(&rctx, ICON_LOCK_OPEN_ALT, v2f32(50.f, 250.f), c5);
        
        // gradient test
        {
          R_Quad quad = {0};
          quad.xy = rectf32(100.f, 400.f, 250.f, 415.f);
          quad.c[0] = color_linu32_from_srgba(0.f, 0.f, 0.f, 1.f);
          quad.c[1] = color_linu32_from_srgba(1.f, 1.f, 1.f, 1.f);
          quad.flags |= R_QuadFlag_HzGradient;
          r_ctx_apply_clip(&rctx, &quad);
          r_quad(&rctx, &quad, 0);
        }
        
        // text srgb test
        {
          r_ctx_font(&rctx, font[0]);
          r_string(&rctx, str8_lit("[ ABC, abc; 0123: XYZ | xyz ]"),
                   v2f32(400.f, 400.f), 0x8000FF80);
        }
        
        // segment test
        {
          U32 c = color_linu32_from_srgba(0.4f, 0.9f, 0.1f, 1.f);
          V2F32 p[5] = {
            v2f32(400.f, 450.f),
            v2f32(450.f, 500.f),
            v2f32(550.f, 450.f),
            v2f32(500.f, 650.f),
            v2f32(400.f, 550.f),
          };
          for (U32 i = 1; i < ArrayCount(p); i += 1){
            r_segment(&rctx, p[i - 1], p[i], 2.f, c);
          }
        }
      }
      
      r_flush(&rlist);
      
      r_show_canvas_on_activated_window();
      r_backend_end();
    }
    
    ProfEnd();
    ProfThreadFlush();
    
    // frame time controls
    {
      U64 now_t = os_now_ticks();
      
      // save frame length in microseconds
      {
        U64 frame_length_t = 0;
        if (now_t > frame_begin_t){
          frame_length_t = now_t - frame_begin_t;
        }
        U64 frame_length_usec = frame_length_t*ratio_usecs/ratio_ticks;
      }
      
      // rest away time
      if (now_t < frame_target_end_t){
        U64 left_over_t = frame_target_end_t - now_t;
        U64 left_over_usec = left_over_t*ratio_usecs/ratio_ticks;
        U64 left_over_msec = left_over_usec/1000;
        Sleep(left_over_msec);
        now_t = os_now_ticks();
      }
      
      // shift frame begin t up
      B32 bad_t = 0;
      {
        if (now_t < frame_target_end_t ||
            now_t - frame_target_end_t < tick_per_frame/16){
          frame_begin_t = frame_target_end_t;
        }
        else{
          frame_begin_t = now_t;
          bad_t = 1;
        }
      }
    }
    
    // increment frame index
    frame_index += 1;
    
    // frame end
    frame_end:
    {
      ArenaTemp scratch = arena_get_scratch(0, 0);
      String8 error = er_accum_end(scratch.arena);
      if (error.size != 0){
        gfx_message_box(str8_lit("Frame Error"), error);
        os_exit_process(1);
      }
      arena_release_scratch(&scratch);
    }
  }
  
  ProfThreadEnd();
  ProfClose();
  
  return(0);
}

//$ graphical freetype //
