#if !defined(DN_H)
#define DN_H

// NOTE: DN
//  Getting Started
//    Include this mega header `dn.h` and define the following symbols to `1` to conditionally
//    enable the interfaces for those features. Additionally in the same or different translation
//    unit, include `dn.cpp` with the same symbols defined to enable the implementation of these
//    features.
//
//    See the configuration section for more information on other symbols that can be defined.
//
//    The following is a single translation unit example:
/*
    #define DN_H_WITH_OS      1
    #define DN_H_WITH_CORE    1
    #define DN_H_WITH_HELPERS 1
    #define DN_H_WITH_ASYNC   1
    #define DN_H_WITH_NET     1
    #include "dn.h"

    #define DN_CPP_WITH_TESTS 1
    #define DN_CPP_WITH_DEMO 1
    #include "dn.cpp"
*/
//    Then initialise the library at runtime by calling DN_Init(...). The library is laid out as:
//
//    - The base layer (dn_base.h) which provides primitives that do not require a host operating
//      system (e.g. freestanding) such as string manipulation, compiler intrinsics and containers.
//      This layer is unconditionallly always available by compiling with this library.
//
//    - The OS layer (dn_os.h) which provides primitives that use the OS such as file IO, threading
//      synchronisation, memory allocation. This layer is OPTIONAL.
//
//    - Extra layer provides helper utilities that are opt-in. These layers are OPTIONAL.
//
//  Configuration
//    Platform Target
//      Define one of the following directives to configure this library to compile for that
//      platform. By default, the library will auto-detect the current host platform and select that
//      as the target platform.
//
//        DN_PLATFORM_EMSCRIPTEN
//        DN_PLATFORM_POSIX
//        DN_PLATFORM_WIN32
//
//      For example
//
//        #define DN_PLATFORM_WIN32
//
//      Will ensure that <Windows.h> is included and the OS layer is implemented using Win32
//      primitives.
//
//    Static functions
//      All public functions in the DN library are prefixed with the macro '#define DN_API'. By
//      default 'DN_API' is not defined to anything. Define
//
//        DN_STATIC_API
//
//      To replace all the functions prefixed with DN_API to be prefixed with 'static' ensuring that
//      the functions in the library do not export an entry into the linking table.
//      translation units.
//
//    Disabling the in-built <Windows.h> (if #define DN_H_WITH_OS 1)
//      If you are building DN for the Windows platform, <Windows.h> is a large legacy header that
//      applications have to include to use Windows APIs. By default this library uses a replacement
//      header for all the Windows functions that it uses in the OS layer removing the need to
//      include <Windows.h> to improve compilation times. This mini header will conflict with
//      <Windows.h> if it needs to be included in your project. The mini header can be disabled by
//      defining:
//
//        DN_NO_WINDOWS_H_REPLACEMENT_HEADER
//
//      To instead use <Windows.h>. DN automatically detects if <Windows.h> is included in an earlier
//      translation unit and will automatically disable the in-built replacement header in which case
//      this does not need to be defined.
//
//    Freestanding
//      The base layer can be used without an OS implementation by defining DN_FREESTANDING like:
//
//        #define DN_FREESTANDING
//
//      This means functionality that relies on the OS like printing, memory allocation, stack traces
//      and so forth are disabled.

#include "Base/dn_base.h"
#include "Base/dn_base_assert.h"
#include "Base/dn_base_containers.h"
#include "Base/dn_base_leak.h"

#if DN_H_WITH_OS
#if defined(DN_PLATFORM_WIN32)
  #include "OS/dn_os_windows.h"
  #include "OS/dn_os_w32.h"
#elif defined(DN_PLATFORM_POSIX) || defined(DN_PLATFORM_EMSCRIPTEN)
  #include "OS/dn_os_posix.h"
#else
  #error Please define a platform e.g. 'DN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
#endif
#include "OS/dn_os.h"
#endif

struct DN_InitArgs
{
  DN_TCInitArgs thread_context_init_args;
};

typedef DN_USize DN_InitFlags;
enum DN_InitFlags_
{
  DN_InitFlags_Nil            = 0,
  DN_InitFlags_OS             = (1 << 0),
  DN_InitFlags_LeakTracker    = (1 << 1) | DN_InitFlags_OS,
  DN_InitFlags_LogLibFeatures = (1 << 2),
  DN_InitFlags_LogCPUFeatures = (1 << 3) | DN_InitFlags_OS,
  DN_InitFlags_ThreadContext  = (1 << 4) | DN_InitFlags_OS,
  DN_InitFlags_LogAllFeatures = DN_InitFlags_LogLibFeatures | DN_InitFlags_LogCPUFeatures,
};

struct DN_Core
{
  DN_InitFlags     init_flags;
  DN_TCCore        main_tc;
  DN_USize         mem_allocs_frame;
  DN_LeakTracker   leak;

  DN_LogPrintFunc* print_func;
  void*            print_func_context;

  bool             os_init;
  #if defined(DN_OS_H)
  DN_OSCore        os;
  #endif
};

DN_API void     DN_Init      (DN_Core *dn, DN_InitFlags flags, DN_InitArgs *args);
DN_API void     DN_Set       (DN_Core *dn);
DN_API DN_Core *DN_Get       ();
DN_API void     DN_BeginFrame();

#if DN_H_WITH_HELPERS
#include "Extra/dn_helpers.h"
#endif

#if DN_H_WITH_ASYNC
#include "Extra/dn_async.h"
#endif

#if DN_H_WITH_NET
#include "Extra/dn_net.h"
#endif
#endif // !defined(DN_H)
