/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can
 * be found in the LICENSE file.
 *
 */

//
//
//

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

#include "common/cl/find_cl.h"
#include "common/cl/assert_cl.h"

#include "ts/transform_stack.h"

//
//
//

// #define SKC_TEST_SVG
#ifdef  SKC_TEST_SVG
//
// SVG
//
#include "svg/svg_doc.h"
#include "svg2skc/svg2skc.h"

#define SKC_TEST(f,...) svg_doc_##f(svg_doc,__VA_ARGS__)

void
svg_doc_toggle(struct svg_doc * sd) { ; };

#else

#include "tests/groups/groups.h"

#define SKC_TEST(f,...) groups_##f(__VA_ARGS__)

#endif

//
//
//

#include "platforms/cl_12/skc_cl.h"
#include "interop.h"

//
//
//

typedef enum skc_pipeline_start_at_e {
  SKC_PIPELINE_START_AT_DEFINE_PATHS = '1',
  SKC_PIPELINE_START_AT_RASTERIZE    = '2',
  SKC_PIPELINE_START_AT_COMPOSITION  = '3',
  SKC_PIPELINE_START_AT_RENDER       = '4'
} skc_pipeline_start_at_e;

//
// Callback for explicitly waiting for render completion
//

#if 0
static
void
is_render_complete(skc_surface_t     surface,
                   skc_styling_t     styling,
                   skc_composition_t composition,
                   skc_framebuffer_t fb,
                   void            * data)
{
  // exit while loop
  *(bool*)data = true;
}
#endif

//
// FIXME - for debugging purposes declare this internal prototype
//

void
skc_runtime_cl_12_debug(struct skc_context * const context);

//
//
//

int
main(int argc, char const * argv[])
{
  //
  //
  //
  if (argc <= 1)
    {
      fprintf(stderr,"-- missing filename\n");
      return EXIT_FAILURE; // no filename
    }

  //
  // load test file
  //
#ifdef SKC_TEST_SVG
  struct svg_doc * svg_doc = svg_doc_parse(argv[1],false);

  fprintf(stderr,"p/r/l = %u / %u / %u\n",
          svg_doc_path_count(svg_doc),
          svg_doc_raster_count(svg_doc),
          svg_doc_layer_count(svg_doc));
#endif

  //
  // fire up GL
  //
  struct skc_interop * interop = skc_interop_create();

  //
  // find platform and device by name
  //
  cl_platform_id platform_id_cl;
  cl_device_id   device_id_cl;

  cl(FindIdsByName("Intel","Graphics",
                   &platform_id_cl,
                   &device_id_cl,
                   0,NULL,NULL,
                   true));

  //
  // create the CL context with GL interop
  //
#ifdef _WIN32
  cl_context_properties context_properties_cl[] =
    {
      CL_CONTEXT_PLATFORM, (cl_context_properties)platform_id_cl,
      CL_GL_CONTEXT_KHR,   skc_interop_get_wgl_context(),
      CL_WGL_HDC_KHR,      skc_interop_get_wgl_dc(),
      0
    };
#else
#error "Missing a system-compatible context!"
#endif

  cl_int     cl_err;
  cl_context context_cl = clCreateContext(context_properties_cl,
                                          1,
                                          &device_id_cl,
                                          NULL,
                                          NULL,
                                          &cl_err); cl_ok(cl_err);
  //
  // register cl_context with GL interop
  //
  skc_interop_set_cl_context(interop,context_cl);

  //
  // create SKC context
  //
  skc_context_t context;

  skc_err err = skc_context_create_cl(&context,
                                      context_cl,
                                      device_id_cl);

  //
  // create path builder
  //
  skc_path_builder_t path_builder;

  err = skc_path_builder_create(context,&path_builder);

  //
  // create raster builder
  //
  skc_raster_builder_t raster_builder;

  err = skc_raster_builder_create(context,&raster_builder);

  //
  // create a composition
  //
  skc_composition_t composition;

  err = skc_composition_create(context,&composition);

  //
  // create a styling instance
  //
  skc_styling_t styling;

  err = skc_styling_create(context,
                           &styling,
                           SKC_TEST(layer_count),
                           1000,
                           2 * 1024 * 1024);

  //
  // create a surface
  //
  skc_surface_t surface;

  err = skc_surface_create(context,&surface);

  //
  // create a transform stack
  //
  struct ts_transform_stack * ts = ts_transform_stack_create(32);

  // prime the transform stack with subpixel scale
  ts_transform_stack_push_scale(ts,32.0,32.0);

  //
  // rasterize, render and reclaim svg until escape
  //
  skc_pipeline_start_at_e pipeline_start_at_base = SKC_PIPELINE_START_AT_DEFINE_PATHS;
  skc_pipeline_start_at_e pipeline_start_at_loop = SKC_PIPELINE_START_AT_DEFINE_PATHS;
  skc_path_t            * paths;
  skc_raster_t          * rasters;

  while (!skc_interop_should_exit(interop))
    {
      // redefine the paths?
      if (pipeline_start_at_loop <= SKC_PIPELINE_START_AT_DEFINE_PATHS)
        {
          // decode paths
          paths = SKC_TEST(paths_decode,path_builder);
        }

      // rasterize the paths?
      if (pipeline_start_at_loop <= SKC_PIPELINE_START_AT_RASTERIZE)
        {
          // save stack
          uint32_t const ts_save = ts_transform_stack_save(ts);

          // update transform
          skc_interop_transform(interop,ts);

          // decode rasters
          rasters = SKC_TEST(rasters_decode,ts,paths,raster_builder);

          // restore the transform stack
          ts_transform_stack_restore(ts,ts_save);
        }

      // decode the styling and composition?
      if (pipeline_start_at_loop <= SKC_PIPELINE_START_AT_COMPOSITION)
        {
          // reset styling
          skc_styling_reset(styling);

          // unseal and reset the composition
          skc_composition_unseal(composition,true);

          // decode layers -- places rasters
          SKC_TEST(layers_decode,rasters,composition,styling,true/*is_srgb*/);

          // seal the styling -- render will seal if not called
          skc_styling_seal(styling);

          // seal the composition -- render will seal if not called
          skc_composition_seal(composition);
        }

      uint32_t const clip[] = { 0, 0, 65535, 65535 };
      int32_t  const txty[] = { 0, 0 };

      // render the styled composition to the surface
      skc_surface_render(surface,
                         styling,
                         composition,
                         skc_interop_get_framebuffer(interop),
                         clip,
                         txty,
                         NULL,
                         NULL);

      //
      // poll for events and maybe start from a different point in the
      // pipeline
      //
      int key;

      // poll for window events
      bool const transform_changed = skc_interop_poll(interop,&key);

      // how many blocks are in use?
      if (key == 'I')
        skc_runtime_cl_12_debug(context);
      else if (key == 'T')
        SKC_TEST(toggle);

      // do we only want to run part of the pipeline?
      if ((key >= SKC_PIPELINE_START_AT_DEFINE_PATHS) && (key <= SKC_PIPELINE_START_AT_RENDER))
        pipeline_start_at_base = key;

      // valid for a loop
      pipeline_start_at_loop = pipeline_start_at_base;

      // if the transform changed then we must start at rasterize or before
      if (transform_changed)
        pipeline_start_at_loop = min(pipeline_start_at_loop,SKC_PIPELINE_START_AT_RASTERIZE);

      if (pipeline_start_at_loop <= SKC_PIPELINE_START_AT_COMPOSITION)
        {
          // rewind the svg doc
          SKC_TEST(rewind);

          if (pipeline_start_at_loop <= SKC_PIPELINE_START_AT_DEFINE_PATHS)
            {
              // release the paths
              SKC_TEST(paths_release,context,paths);
            }

          if (pipeline_start_at_loop <= SKC_PIPELINE_START_AT_RASTERIZE)
            {
              // release the rasters
              SKC_TEST(rasters_release,context,rasters);
            }
        }

#if 0
      //
      // Note that we don't need to explicitly wait for the render()
      // to complete since SKC is fully concurrent and the styling and
      // compsition unseal() operations will "clock" the render loop.
      //

      //
      // explicitly spin until framebuffer is rendered
      //
      bool quit = false;

      while (!quit) {
        // fprintf(stderr,"WAITING ON: !quit\n");
        skc_context_wait(context);
      }
#endif
    }

  //
  // dispose of mundane resources
  //
  ts_transform_stack_release(ts);

  //
  // dispose of all SKC resources
  //
  err = skc_surface_release(surface);
  err = skc_styling_release(styling);
  err = skc_composition_release(composition);
  err = skc_raster_builder_release(raster_builder);
  err = skc_path_builder_release(path_builder);
  err = skc_context_release(context);

  //
  // dispose of GL interop
  //
  skc_interop_destroy(interop);

  //
  //
  //
  return EXIT_SUCCESS;
}

//
//
//