/*
 * This file is part of Afterwarp Framework (http://afterwarp.io).
 * Copyright (c) 2015 - 2025 Dr. Yuriy Kotsarenko. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and limitations under the License.
 */

// This example illustrates the following features:
//  * Drawing normal and curved text using different fonts, color gradients, outlines and effects.

#ifdef _WIN32
  #include "Resource.h"
  #include <SDKDDKVer.h>

  #define WIN32_LEAN_AND_MEAN
  #include <windows.h>
  // Remove conflicting Windows macros.
  #undef KEY_EXECUTE
#endif

#include <stdio.h>

#include "Afterwarp.h"
#include "Afterwarp.Vectors.h"

#define AFTERWARP_VECTORS_IMPLEMENTATION
#include "Afterwarp.Vectors.h"

// Global variables.

// Display characteristics.
static Point displaySize = {1280, 720};
static float displayScale = 1.0f;

// Base Afterwarp interfaces.
static Application* application = NULL;
static Device* device = NULL;
static SwapChain* swapChain = NULL;
static Canvas* canvas = NULL;
static TextRenderer* textRenderer = NULL;
static TextModeller* textModeller = NULL;

static Texture* textureCanvas = NULL;
static Texture* textureScreen = NULL;

// Forward declarations.

static void outputError(char const* message);

// Implementation.

LibraryBool applicationCreate(Application_t* application, void* user)
{
  device = DeviceCreate(application, DEVICE_TECHNOLOGY_UNKNOWN, 0);
  if (!device)
  {
    outputError("Could not create graphics device.");
    return false;
  }

  swapChain = SwapChainCreate(device, ApplicationGetWindowHandle(application), &displaySize,
    PIXEL_FORMAT_RGBA8, PIXEL_FORMAT_UNKNOWN, 0, false);
  if (!swapChain)
  {
    outputError("Could not create rendering swap-chain.");
    return false;
  }

#ifndef _WIN32
  {
    char const* fileNames[] = {"../../../../Media/afterwarp.ico"};

    if (!ApplicationSetIconsFromFiles(application, fileNames, sizeof(fileNames) / sizeof(fileNames[0])))
    {
      outputError("Could not load application icon.");
      return false;
    }
  }
#endif

  canvas = CanvasCreate(device);
  if (!canvas)
  {
    outputError("Could not create rendering canvas.");
    return false;
  }

  textRenderer = TextRendererCreate(canvas, &(Point){512, 512}, PIXEL_FORMAT_LA8, false);
  if (!textRenderer)
  {
    outputError("Could not create text rendering module.");
    return false;
  }

  textModeller = TextModellerCreate(device);
  if (!textModeller)
  {
    outputError("Could not create vector-based text rendering module.");
    return false;
  }

  textureCanvas = TextureCreate(device, &(TextureParameters){
    .width = displaySize.x, .height = displaySize.y, .format = PIXEL_FORMAT_RGBA8,
    .attributes = TEXTURE_ATTRIBUTE_DRAWABLE, .multisamples = 8});

  textureScreen = TextureCreate(device, &(TextureParameters){
    .width = displaySize.x, .height = displaySize.y, .format = PIXEL_FORMAT_RGBA8,
    .attributes = TEXTURE_ATTRIBUTE_DRAWABLE});

  if (!textureCanvas || !textureScreen)
  {
    outputError("Could not create drawable textures.");
    return false;
  }

  return true;
}

void applicationDestroy(Application_t* application, void* user)
{
  TextureDestroy(textureScreen);
  TextureDestroy(textureCanvas);
  TextModellerDestroy(textModeller);
  TextRendererDestroy(textRenderer);
  CanvasDestroy(canvas);
  SwapChainDestroy(swapChain);
  DeviceDestroy(device);
}

void applicationResize(Application_t* application, void* user)
{
  Rect clientRect = {0};
  ApplicationGetClientRect(application, &clientRect);

  displaySize = rectGetSize(clientRect);

  SwapChainResize(swapChain, &displaySize);

  if (device)
  { // Re-create drawable textures.
    TextureDestroy(textureScreen);
    TextureDestroy(textureCanvas);

    textureCanvas = TextureCreate(device, &(TextureParameters){
      .width = displaySize.x, .height = displaySize.y, .format = PIXEL_FORMAT_RGBA8,
      .attributes = TEXTURE_ATTRIBUTE_DRAWABLE, .multisamples = 8});

    textureScreen = TextureCreate(device, &(TextureParameters){
      .width = displaySize.x, .height = displaySize.y, .format = PIXEL_FORMAT_RGBA8,
      .attributes = TEXTURE_ATTRIBUTE_DRAWABLE});

    if (!textureCanvas || !textureScreen)
    {
      outputError("Could not resize rendering textures.");
      ApplicationTerminate(application);
    }
  }
}

void applicationRender(Application_t* application, void* user)
{
  displayScale = ApplicationGetWindowScale(application);

  if (TextureBegin(textureCanvas, 0, 0))
  {
    FloatColor const clearColor = floatColor(0xFFE1E2E6u);
    DeviceClear(device, DEVICE_CLEAR_LAYER_COLOR, &clearColor, 1.0f, 0);

    if (CanvasBegin(canvas))
    {
      // Draw dark background on bottom part of the form.
      CanvasSetAttributes(canvas, 0);
      CanvasSetContextState(canvas, CANVAS_CONTEXT_STATE_FLAT_SCENE);
      CanvasFillRect(canvas,
        &(RectF){0.0f, displaySize.y * 0.5f, (float)displaySize.x, displaySize.y * 0.5f},
        &COLOR_RECT(0xFF40444Cu), BLENDING_EFFECT_COPY);

      PointF position = {truncf(displaySize.x * 0.5f), truncf(displaySize.y * 0.15f)};

      // For curved text, we have to use vector-based text renderer, which also required multisampled
      // render target to enable proper anti-aliasing.
      TextModellerSetFontParameters(textModeller, &(FontParameters){
        .family = "Cambria", .size = 16.0f * displayScale, .weight = FONT_WEIGHT_BOLD,
        .stretch = FONT_STRETCH_NORMAL});

      TextModellerDrawCurvedToCanvas(textModeller, canvas, &position,
        u8"The framework also supports rendering curved text to better accomodate for existing content.",
        1000.0f, NAN, &(ColorPair){0xFF000040u, 0xFF0040A0u}, NULL, BLENDING_EFFECT_NORMAL);

      // Prepare canvas for text output.
      CanvasSetContextState(canvas, CANVAS_CONTEXT_STATE_FLAT_TEXT);
      CanvasSetSamplerState(canvas, &(CanvasSamplerState){TEXTURE_FILTER_NEAREST, TEXTURE_FILTER_NEAREST});

      // Arial Semi-Bold 16px.
      {
        FontParameters parameters = {
          .family = "Arial", .size = 16.0f * displayScale, .weight = FONT_WEIGHT_SEMI_BOLD,
          .stretch = FONT_STRETCH_NORMAL, .effect = fontEffectDefault};
        parameters.effect.borderType = FONT_BORDER_NORMAL;
        parameters.effect.shadowOpacity = 1.0f;
        TextRendererSetFontParameters(textRenderer, &parameters);

        TextRenderModifiers modifiers = {0};
        // Extend further on a 2x HiDPI screen for demo purposes.
        modifiers.interleave = displayScale >= 2.0f ? 8.0f : 1.0f;

        TextRendererDrawCentered(textRenderer, &position,
          u8"欢迎使用演示不同文本字体和视觉效果的应用程序。",
          &(ColorPair){0xFF00E0FFu, 0xFF0000FFu}, 1.0f, true, &modifiers);
      }
      position.y += 32.0f * displayScale; // Advance vertical position.

      // Tahoma 14px.
      TextRendererSetFontParameters(textRenderer, &(FontParameters){
        .family = "Tahoma", .size = 14.0f * displayScale, .weight = FONT_WEIGHT_NORMAL,
        .stretch = FONT_STRETCH_NORMAL, .effect = fontEffectDefault});

      TextRendererDrawCentered(textRenderer, &position,
        u8"C'è il pieno supporto per le lingue straniere nel formato unicode UTF8.",
        &(ColorPair){0xFF008000u, 0xFFFF0000u}, 1.0f, true, NULL);

      position.y += 32.0f * displayScale; // Advance vertical position.

      { // Arial Regular 16px.
        FontParameters parameters = {
          .family = "Arial", .size = 16.0f * displayScale, .weight = FONT_WEIGHT_NORMAL,
          .stretch = FONT_STRETCH_NORMAL, .effect = fontEffectDefault};
        parameters.effect.borderType = FONT_BORDER_NORMAL;
        parameters.effect.shadowOpacity = 1.0f;
        TextRendererSetFontParameters(textRenderer, &parameters);

        TextRendererDrawCentered(textRenderer, &position,
          u8"Arial Regular 16px with Border, Shadow and Gradient.",
          &(ColorPair){0xFF00FF00u, 0xFFFFFFFFu}, 1.0f, true, NULL);

        position.y += 32.0f * displayScale; // Advance vertical position.
      }
      // Arial Regular 14px, Black.
      TextRendererSetFontParameters(textRenderer, &(FontParameters){
        .family = "Arial", .size = 14.0f * displayScale, .weight = FONT_WEIGHT_NORMAL,
        .stretch = FONT_STRETCH_NORMAL, .effect = fontEffectDefault});

      TextRendererDrawCentered(textRenderer, &position,
        u8"Arial Regular 14px without effects, black",
        &COLOR_PAIR(0xFF000000u), 1.0f, true, NULL);

      position.y += 28.0f * displayScale; // Advance vertical position.

      { // Segoe UI SemiBold 16px, Inverted with Border and Shadow.
        FontParameters parameters = {
          .family = "Segoe UI", .size = 16.0f * displayScale, .weight = FONT_WEIGHT_SEMI_BOLD,
          .stretch = FONT_STRETCH_NORMAL, .effect = fontEffectDefault};
        parameters.effect.borderType = FONT_BORDER_SEMI_HEAVY;
        parameters.effect.borderBrightness = 0.75f;
        parameters.effect.fillBrightness = 0.0f;
        parameters.effect.shadowBrightness = 0.5f;
        parameters.effect.shadowOpacity = 1.0f;
        TextRendererSetFontParameters(textRenderer, &parameters);

        TextRendererDrawCentered(textRenderer, &position,
          u8"Segoe UI SemiBold 16px inverted with Border and Shadow.",
          &COLOR_PAIR(0xFFFFFFFFu), 1.0f, true, NULL);

        position.y += 32.0f * displayScale; // Advance vertical position.
      }
      // Consolas 12px.
      TextRendererSetFontParameters(textRenderer, &(FontParameters){
        .family = "Consolas", .size = 12.0f * displayScale, .weight = FONT_WEIGHT_NORMAL,
        .stretch = FONT_STRETCH_NORMAL, .effect = fontEffectDefault});

      TextRendererDrawCentered(textRenderer, &position,
        u8"Consolas (monofont) Regular 12px with Gradient.",
        &(ColorPair){0xFF0000FFu, 0xFFA00000u}, 1.0f, true, NULL);

      position.y += 32.0f * displayScale; // Advance vertical position.

      { // Verdana Bold 20px + Border + Shadow + Gradient
        FontParameters parameters = {
          .family = "Verdana", .size = 20.0f * displayScale, .weight = FONT_WEIGHT_BOLD,
          .stretch = FONT_STRETCH_NORMAL, .effect = fontEffectDefault};
        parameters.effect.borderType = FONT_BORDER_NORMAL;
        parameters.effect.shadowOpacity = 1.0f;
        TextRendererSetFontParameters(textRenderer, &parameters);

        TextRendererDrawAligned(textRenderer, &position,
          u8"Verdana 20px aligned to left.",
          &(ColorPair){0xFF0000FFu, 0xFFFF00FFu},
          TEXT_ALIGNMENT_START, TEXT_ALIGNMENT_MIDDLE, 1.0f, true, NULL);
      }
      { // Cambria Bold Italic 18px + Border + Shadow + Gradient
        FontParameters parameters = {
          .family = "Cambria", .size = 18.0f * displayScale, .weight = FONT_WEIGHT_BOLD,
          .stretch = FONT_STRETCH_NORMAL, .slant = FONT_SLANT_ITALIC, .effect = fontEffectDefault};
        parameters.effect.borderType = FONT_BORDER_NORMAL;
        parameters.effect.shadowOpacity = 1.0f;
        TextRendererSetFontParameters(textRenderer, &parameters);

        TextRendererDrawAligned(textRenderer, &position,
          u8"Cambria Italic 18px aligned to right.",
          &(ColorPair){0xFF0000FFu, 0xFF00FFFFu},
          TEXT_ALIGNMENT_END, TEXT_ALIGNMENT_MIDDLE, 1.0f, true, NULL);

        position.y += 32.0f * displayScale; // Advance vertical position.
      }

      // Apply 2D transformation.
      CanvasSetAttributes(canvas, CANVAS_ATTRIBUTE_TRANSFORM);

      Matrix3x2 transform = matrixMultiply3x2(
        matrixRotate3x2((float)M_PI * -0.5f),
        matrixTranslate3x2((PointF){.x = displaySize.x - 8.0f * displayScale,
          .y = truncf(displaySize.y * 0.25f)}));

      CanvasSetTransform(canvas, &transform);

      { // Arial 16px + Border + Shadow + Gradient, Vertical (with correct shadow)
        FontParameters parameters = {
          .family = "Arial", .size = 16.0f * displayScale, .weight = FONT_WEIGHT_NORMAL,
        .stretch = FONT_STRETCH_NORMAL, .effect = fontEffectDefault};
        parameters.effect.borderType = FONT_BORDER_NORMAL;
        parameters.effect.shadowOpacity = 1.0f;
        parameters.effect.shadowDistance = (PointF){.x = -2.0f, .y = 2.0f};
        TextRendererSetFontParameters(textRenderer, &parameters);

        TextRendererDrawAligned(textRenderer, NULL,
          u8"Vertical text is also possible.",
          &(ColorPair){0xFFFF0080u, 0xFFFFFFFFu},
          TEXT_ALIGNMENT_MIDDLE, TEXT_ALIGNMENT_END, 1.0f, true, NULL);
      }
      transform = matrixMultiply3x2(
        matrixRotate3x2((float)M_PI * 0.5f),
        matrixTranslate3x2((PointF){.x = 8.0f * displayScale, .y = truncf(displaySize.y * 0.75f)}));

      CanvasSetTransform(canvas, &transform);

      { // Segoe UI 16px + Border + Shadow + Gradient, Vertical (with correct shadow)
        FontParameters parameters = {
          .family = "Segoe UI", .size = 16.0f * displayScale, .weight = FONT_WEIGHT_NORMAL,
        .stretch = FONT_STRETCH_NORMAL, .effect = fontEffectDefault};
        parameters.effect.borderType = FONT_BORDER_NORMAL;
        parameters.effect.shadowOpacity = 1.0f;
        parameters.effect.shadowDistance = (PointF){.x = 2.0f, .y = -2.0f};
        TextRendererSetFontParameters(textRenderer, &parameters);

        TextRendererDrawAligned(textRenderer, NULL,
          u8"Text in a different orientation.",
          &(ColorPair){0xFFA0A0A0u, 0xFFFFFFFFu},
          TEXT_ALIGNMENT_MIDDLE, TEXT_ALIGNMENT_END, 1.0f, true, NULL);
      }

      CanvasSetAttributes(canvas, 0);
      position = makePointF(truncf(displaySize.x * 0.5f), truncf(displaySize.y * 0.65f));

      { // Tahoma Regular 16px + Border + Shadow + Gradient
        FontParameters parameters = {
          .family = "Tahoma", .size = 16.0f * displayScale, .weight = FONT_WEIGHT_NORMAL,
          .stretch = FONT_STRETCH_NORMAL, .slant = FONT_SLANT_NONE, .effect = fontEffectDefault};
        parameters.effect.borderType = FONT_BORDER_NORMAL;
        parameters.effect.shadowOpacity = 1.0f;
        TextRendererSetFontParameters(textRenderer, &parameters);

        TextRendererDrawCentered(textRenderer, &position,
          u8"このアプリケーションは、異なる言語で書かれたテキストを完全にサポートしています。",
          &(ColorPair){0xFFC65D7Bu, 0xFFE0FCBFu}, 1.0f, true, NULL);

        position.y += 40.0f * displayScale; // Advance vertical position.
      }
      { // Cambria SemiBold 18px + Border + Shadow + Gradient
        FontParameters parameters = {
          .family = "Cambria", .size = 18.0f * displayScale, .weight = FONT_WEIGHT_SEMI_BOLD,
          .stretch = FONT_STRETCH_NORMAL, .slant = FONT_SLANT_NONE, .effect = fontEffectDefault};
        parameters.effect.borderType = FONT_BORDER_NORMAL;
        parameters.effect.shadowOpacity = 1.0f;
        TextRendererSetFontParameters(textRenderer, &parameters);

        TextRendererDrawCentered(textRenderer, &position,
          u8"Some Cambria 18px sample text with Gradient, Border and Shadow.",
          &(ColorPair){0xFFE6CFFFu, 0xFFE06E98u}, 1.0f, true, NULL);

        position.y += 32.0f * displayScale; // Advance vertical position.
      }
      { // Calibri 16px + Border + Shadow + Gradient
        FontParameters parameters = {
          .family = "Calibri", .size = 16.0f * displayScale, .weight = FONT_WEIGHT_SEMI_BOLD,
          .stretch = FONT_STRETCH_NORMAL, .slant = FONT_SLANT_NONE, .effect = fontEffectDefault};
        parameters.effect.borderType = FONT_BORDER_NORMAL;
        parameters.effect.shadowOpacity = 1.0f;
        TextRendererSetFontParameters(textRenderer, &parameters);

        TextRendererDrawCentered(textRenderer, &position,
          u8"Calibri Regular 16px with Border, Shadow and Gradient.",
          &(ColorPair){0xFFFF0000u, 0xFFFFE000u}, 1.0f, true, NULL);

        position.y += 32.0f * displayScale; // Advance vertical position.
      }
      { // Segoe UI SemiBold 16px, Inverted with Border and Shadow.
        FontParameters parameters = {
          .family = "Segoe UI", .size = 16.0f * displayScale, .weight = FONT_WEIGHT_SEMI_BOLD,
          .stretch = FONT_STRETCH_NORMAL, .slant = FONT_SLANT_NONE, .effect = fontEffectDefault};
        parameters.effect.borderType = FONT_BORDER_SEMI_HEAVY;
        parameters.effect.borderBrightness = 1.0f;
        parameters.effect.fillBrightness = 0.0f;
        parameters.effect.shadowBrightness = 0.75f;
        parameters.effect.shadowOpacity = 1.0f;
        TextRendererSetFontParameters(textRenderer, &parameters);

        TextRendererDrawCentered(textRenderer, &position,
          u8"Segoe UI SemiBold 16px inverted with Border and Shadow.",
          &COLOR_PAIR(0xFFFFFFFFu), 1.0f, true, NULL);

        position.y += 32.0f * displayScale; // Advance vertical position.
      }
      // Arial 14px.
      TextRendererSetFontParameters(textRenderer, &(FontParameters){
        .family = "Arial", .size = 14.0f * displayScale, .weight = FONT_WEIGHT_NORMAL,
        .stretch = FONT_STRETCH_NORMAL, .effect = fontEffectDefault});

      TextRendererDrawCentered(textRenderer, &position,
        u8"Καλώς ορίσατε στον έλεγχο γραμματοσειρών του πλαισίου.",
        &(ColorPair){0xFF00FF00u, 0xFFFFFFFFu}, 1.0f, true, NULL);

      // Set canvas state for vector-based text rendering.
      CanvasSetContextState(canvas, CANVAS_CONTEXT_STATE_FLAT_SCENE);

      TextModellerSetFontParameters(textModeller, &(FontParameters){
        .family = "Calibri", .size = 16.0f * displayScale, .weight = FONT_WEIGHT_SEMI_BOLD,
        .stretch = FONT_STRETCH_NORMAL, .slant = FONT_SLANT_NONE});

      TextModellerDrawCurvedToCanvas(textModeller, canvas, &position,
        u8"El framework es capaz de rasterizar texto en curva en cualquier orientación y con la transformación del canvas.",
        -1000.0f, NAN, &(ColorPair){0xFF00E0FFu, 0xFF0000FFu}, NULL, BLENDING_EFFECT_NORMAL);

      { // Draw technology string.
        FontParameters parameters = {
          .family = "Segoe UI", .size = 16.0f * displayScale, .weight = FONT_WEIGHT_SEMI_BOLD,
          .stretch = FONT_STRETCH_NORMAL, .slant = FONT_SLANT_NONE, .effect = fontEffectDefault};
        parameters.effect.borderType = FONT_BORDER_SEMI_HEAVY;
        parameters.effect.borderBrightness = 1.0f;
        parameters.effect.fillBrightness = 0.0f;
        parameters.effect.shadowBrightness = 0.75f;
        parameters.effect.shadowOpacity = 1.0f;
        TextRendererSetFontParameters(textRenderer, &parameters);

        char const* const textDirect3D = "Direct3D (%d.%d), features: %d.%d";
        char const* const textOpenGL = "OpenGL (%d.%d+), features: %d.%d";

        char const* const formatText = DeviceGetTechnology(device) == DEVICE_TECHNOLOGY_DIRECT3D ?
          textDirect3D : textOpenGL;

        char text[128];

        snprintf(text, sizeof(text), formatText,
          DeviceGetTechVersion(device) >> 8, (DeviceGetTechVersion(device) >> 4) & 0xF,
          DeviceGetTechFeatureVersion(device) >> 8, (DeviceGetTechFeatureVersion(device) >> 4) & 0xF);

        TextRendererDraw(textRenderer, &(PointF){8.0f * displayScale, 4.0f * displayScale}, text,
          &COLOR_PAIR(0xA0FFFFFFu), 1.0f, NULL);
      }
      CanvasEnd(canvas);
    }
    TextureEnd(textureCanvas);
  }
  // Resolve multisampled texture into a non-multisampled one so it can be rendered with the canvas.
  TextureCopy(textureScreen, textureCanvas, 0, NULL, 0, NULL, 0, 0);

  if (SwapChainBegin(swapChain))
  {
    FloatColor const clearColor = floatColor(0xFFE1E2E6u);
    DeviceClear(device, DEVICE_CLEAR_LAYER_COLOR, &clearColor, 1.0f, 0);

    if (CanvasBegin(canvas))
    {
      CanvasSetContextState(canvas, CANVAS_CONTEXT_STATE_FLAT_SCENE);
      CanvasSetSamplerState(canvas, &(CanvasSamplerState){TEXTURE_FILTER_NEAREST, TEXTURE_FILTER_NEAREST});
      CanvasSetAttributes(canvas, 0);

      // Render non-multisampled texture.
      Quad dest = makeQuad(0.0f, 0.0f, displaySize.x, displaySize.y);
      Quad src = quadUnity;

      if (DeviceGetTechnology(device) == DEVICE_TECHNOLOGY_OPENGL ||
        DeviceGetTechnology(device) == DEVICE_TECHNOLOGY_OPENGL_ES)
        src = quadFlip(src);

      CanvasTexturedQuad(canvas, textureScreen, &dest, &src, NULL, BLENDING_EFFECT_COPY);
      CanvasEnd(canvas);
    }
    SwapChainEnd(swapChain);
  }
}

void outputError(char const* message)
{
#ifdef _WIN32
  static char const* initErrorTitle = "Initialization Error";
  MessageBoxA(NULL, message, initErrorTitle, MB_ICONERROR | MB_OK | MB_DEFAULT_DESKTOP_ONLY);
#else
  printf("%s\n", message);
#endif
}

// Main application entry.

#ifdef _WIN32

int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prevInstance, _In_ LPWSTR cmdLine,
  _In_ int showCmd)
{
  UNREFERENCED_PARAMETER(prevInstance);
  UNREFERENCED_PARAMETER(cmdLine);
  UNREFERENCED_PARAMETER(showCmd);

  char const* windowClassName = "AfterwarpFontsDemo";

#else

int main(int argc, char **argv)
{

#endif

  ApplicationConfiguration configuration = {0};
  configuration.size = displaySize;
  configuration.position = (Point){.x = INT32_MAX, .y = INT32_MAX}; // default position
#ifdef _WIN32
  configuration.startup.win.handleInstance = instance;
  configuration.startup.win.handleIconSmall = LoadIcon(instance, MAKEINTRESOURCE(IDI_AFTERWARP));
  configuration.startup.win.handleIconBig = LoadIcon(instance, MAKEINTRESOURCE(IDI_AFTERWARP));

  strncpy(configuration.startup.win.windowClassName, windowClassName,
    sizeof(configuration.startup.win.windowClassName));
#else
  configuration.startup.nx.parameterCount = argc;
  configuration.startup.nx.parameterStrings = argv;
#endif

  application = ApplicationCreate(&configuration);
  if (!application)
  {
    outputError("Could not create Application object.");
    return -1;
  }

  ApplicationEvents events = {0};
  events.eventCreate = applicationCreate;
  events.eventDestroy = applicationDestroy;
  events.eventResize = applicationResize;
  events.eventRender = applicationRender;
  ApplicationSetEvents(application, &events, NULL);

  displayScale = ApplicationGetWindowScale(application);
  ApplicationSetTitle(application, "Afterwarp: FontsDemo example");

  { // Retrieve client size back in case it has changed since startup.
    Rect clientRect = {0};
    ApplicationGetClientRect(application, &clientRect);

    displaySize = rectGetSize(clientRect);
  }

  bool const executed = ApplicationExecute(application);
  ApplicationDestroy(application);

  return executed ? 0 : -1;
}