// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)

#ifndef TXTI_H
#define TXTI_H

////////////////////////////////
//~ NOTE(rjf): Text Info Layer Overview (8/30/2023)
//
// This layer's purpose is to provide access to a mutable cache of parsed
// information about textual data, backed by filesystem contents. This
// cache associates unique handles (`TXTI_Handle`) with "entities", where
// entities contain information about textual contents of each line, how many
// lines of text the entity contains, and the tokenization of each line.
//
// While it is generally correct for a debugger to only provide read-only UIs
// for text viewing (so that the user does not mistakenly modify a buffer and
// continue debugging with it, even if the line or symbol info is no longer
// reflective of the source code), there are cases where read/write buffers are
// useful, and so this layer needs to support that, and the disabling of writes
// can remain as a high-level UI decision (rather than a lack of capability of
// the debugger). Mutable buffers may be used for editing config files (or
// otherwise any files that are not relevant to actively-used debug info),
// or debug logs.
//
// This layer is also responsible for hot-reloading changed files from disk,
// *and* for reporting conflicts, if a buffer was mutated within the process,
// but was also modified on disk.
//
// In order to avoid hanging UI while larger files are being edited or lexed,
// all buffer loading, mutation, & parsing happen on "mutator threads". These
// threads consume messages (`TXTI_Msg`), which command them to reload a file
// from disk, or to replace textual ranges in a buffer. After completing those
// operations, the buffer is lexed/parsed.
//
// Entities have *two* buffer data structures -- this allows a mutator thread
// to apply edits to one while the other can be read by user threads. For each
// editing operation, the mutator threads apply identical operations in a
// *rotation-based* order. If the currently-viewable buffer is slot 0, the edit
// will first be applied to slot 1, and before edits are reflected in slot 0,
// the mutator thread will bump a "buffer mutation counter", such that before
// any edits are made to slot 0, the viewable buffer is changed to being within
// slot *1*.
//
// Importantly, entities map to a *unique* mutator thread -- it is not possible
// for multiple mutator threads to be attempting to write to the same entity at
// the same time, as this could not produce meaningful or coherent results.
// This way, all edits to each entity are applied serially.

////////////////////////////////
//~ rjf: Handle Type

typedef struct TXTI_Handle TXTI_Handle;
struct TXTI_Handle
{
  U64 u64[2];
};

////////////////////////////////
//~ rjf: Buffer Entity Types

#define TXTI_ENTITY_BUFFER_COUNT 2

typedef struct TXTI_Buffer TXTI_Buffer;
struct TXTI_Buffer
{
  // rjf: arenas
  Arena *data_arena;
  Arena *analysis_arena;
  
  // rjf: raw textual data
  String8 data;
  
  // rjf: line range info
  U64 lines_count;
  Rng1U64 *lines_ranges;
  U64 lines_max_size;
  
  // rjf: tokens
  TXT_TokenArray tokens;
};

typedef struct TXTI_Entity TXTI_Entity;
struct TXTI_Entity
{
  // rjf: top-level info
  TXTI_Entity *next;
  String8 path;
  U64 id;
  U64 timestamp;
  U64 mut_gen;
  
  // rjf: metadata
  TXT_LineEndKind line_end_kind;
  TXT_LangKind lang_kind;
  U64 bytes_processed;
  U64 bytes_to_process;
  U64 working_count;
  
  // rjf: double-buffered mutable text buffers
  U64 buffer_apply_gen;
  TXTI_Buffer buffers[TXTI_ENTITY_BUFFER_COUNT];
};

typedef struct TXTI_EntitySlot TXTI_EntitySlot;
struct TXTI_EntitySlot
{
  TXTI_Entity *first;
  TXTI_Entity *last;
};

typedef struct TXTI_EntityMap TXTI_EntityMap;
struct TXTI_EntityMap
{
  U64 slots_count;
  TXTI_EntitySlot *slots;
};

////////////////////////////////
//~ rjf: Striped Access Types

typedef struct TXTI_Stripe TXTI_Stripe;
struct TXTI_Stripe
{
  Arena *arena;
  OS_Handle cv;
  OS_Handle rw_mutex;
};

typedef struct TXTI_StripeTable TXTI_StripeTable;
struct TXTI_StripeTable
{
  U64 count;
  TXTI_Stripe *v;
};

////////////////////////////////
//~ rjf: Entity Introspection Result Types

typedef struct TXTI_BufferInfo TXTI_BufferInfo;
struct TXTI_BufferInfo
{
  String8 path;
  U64 timestamp;
  TXT_LineEndKind line_end_kind;
  TXT_LangKind lang_kind;
  U64 total_line_count;
  U64 last_line_size;
  U64 max_line_size;
  U64 mut_gen;
  U64 buffer_apply_gen;
  U64 bytes_processed;
  U64 bytes_to_process;
};

typedef struct TXTI_Slice TXTI_Slice;
struct TXTI_Slice
{
  U64 line_count;
  String8 *line_text;
  Rng1U64 *line_ranges;
  TXT_TokenArray *line_tokens;
};

////////////////////////////////
//~ rjf: User -> Mutator Thread Messages

typedef enum TXTI_MsgKind
{
  TXTI_MsgKind_Null,
  TXTI_MsgKind_Append,
  TXTI_MsgKind_Reload,
  TXTI_MsgKind_COUNT
}
TXTI_MsgKind;

typedef struct TXTI_Msg TXTI_Msg;
struct TXTI_Msg
{
  TXTI_MsgKind kind;
  TXTI_Handle handle;
  String8 string;
};

typedef struct TXTI_MsgNode TXTI_MsgNode;
struct TXTI_MsgNode
{
  TXTI_MsgNode *next;
  TXTI_Msg v;
};

typedef struct TXTI_MsgList TXTI_MsgList;
struct TXTI_MsgList
{
  TXTI_MsgNode *first;
  TXTI_MsgNode *last;
  U64 count;
};

////////////////////////////////
//~ rjf: Central State

typedef struct TXTI_MutThread TXTI_MutThread;
struct TXTI_MutThread
{
  OS_Handle thread;
  Arena *msg_arena;
  TXTI_MsgList msg_list;
  OS_Handle msg_mutex;
  OS_Handle msg_cv;
};

typedef struct TXTI_State TXTI_State;
struct TXTI_State
{
  // rjf: arena
  Arena *arena;
  
  // rjf: entities state
  TXTI_EntityMap entity_map;
  TXTI_StripeTable entity_map_stripes;
  U64 entity_id_gen;
  
  // rjf: mutator threads
  U64 mut_thread_count;
  TXTI_MutThread *mut_threads;
  
  // rjf: detector thread
  U64 detector_thread_enabled;
  OS_Handle detector_thread;
};

////////////////////////////////
//~ rjf: Globals

global TXTI_State *txti_state = 0;

////////////////////////////////
//~ rjf: Main Layer Initialization

internal void txti_init(void);

////////////////////////////////
//~ rjf: Basic Helpers

internal U64 txti_hash_from_string(String8 string);

////////////////////////////////
//~ rjf: Message Type Functions

internal void txti_msg_list_push(Arena *arena, TXTI_MsgList *msgs, TXTI_Msg *msg);
internal void txti_msg_list_concat_in_place(TXTI_MsgList *dst, TXTI_MsgList *src);
internal TXTI_MsgList txti_msg_list_deep_copy(Arena *arena, TXTI_MsgList *src);

////////////////////////////////
//~ rjf: Entities API

//- rjf: opening entities & correllation w/ path
internal TXTI_Handle txti_handle_from_path(String8 path);

//- rjf: buffer introspection
internal TXTI_BufferInfo txti_buffer_info_from_handle(Arena *arena, TXTI_Handle handle);
internal TXTI_Slice txti_slice_from_handle_line_range(Arena *arena, TXTI_Handle handle, Rng1S64 line_range);
internal String8 txti_string_from_handle_txt_rng(Arena *arena, TXTI_Handle handle, TxtRng range);
internal String8 txti_string_from_handle_line_num(Arena *arena, TXTI_Handle handle, S64 line_num);
internal Rng1U64 txti_expr_range_from_line_off_range_string_tokens(U64 off, Rng1U64 line_range, String8 line_text, TXT_TokenArray *line_tokens);
internal TxtRng txti_expr_range_from_handle_pt(TXTI_Handle handle, TxtPt pt);

//- rjf: buffer mutations
internal void txti_reload(TXTI_Handle handle, String8 path);
internal void txti_append(TXTI_Handle handle, String8 string);

//- rjf: buffer external change detection enabling/disabling
internal void txti_set_external_change_detection_enabled(B32 enabled);

////////////////////////////////
//~ rjf: Mutator Threads

internal void txti_mut_thread_entry_point(void *p);

////////////////////////////////
//~ rjf: Detector Thread

internal void txti_detector_thread_entry_point(void *p);

#endif //TXTI_H
