/* date = December 30th 2023 8:26 pm */

#ifndef VN_SCENE2_H
#define VN_SCENE2_H

struct scene2_runtime;
struct compiled_scene2;
#define S2_CALL_FUNCTION(name) void name(scene2_runtime *Runtime, compiled_scene2 *Compiled)
typedef S2_CALL_FUNCTION(scene2_call_function);
static S2_CALL_FUNCTION(S2_Call_Stub) {}

static u64 S2_CallFunctionIndexFromString(string String);
static scene2_call_function *S2_CallFunctionFromString(string String);

#include "generated/vn_scene2.meta.h"

////////////////////////////////
//~ sixten: Scene Message Types
struct scene2_message
{
    scene2_message *Next;
    string Message;
    token Token;
};

struct scene2_message_list
{
    scene2_message *First;
    scene2_message *Last;
    u64 Count;
};


////////////////////////////////
//~ sixten: Scene Parsing Types
enum scene2_precedence
{
    S2_Precedence_None,
    S2_Precedence_Assignment,
    S2_Precedence_Or,
    S2_Precedence_And,
    S2_Precedence_Equality,
    S2_Precedence_Comparison,
    S2_Precedence_Term,
    S2_Precedence_Factor,
    S2_Precedence_Unary,
    S2_Precedence_Call,
    S2_Precedence_Primary,
};

typedef void scene2_parse_function(struct scene2_compiler *Compiler, b32 CanAssign);

struct scene2_parse_rule
{
    scene2_parse_function *PrefixRule;
    scene2_parse_function *InfixRule;
    scene2_precedence Precedence;
};

struct scene2_instruction
{
    scene2_opcode Opcode;
    union
    {
        struct
        {
            u32 Arg1;
            u32 Arg2;
        };
        struct
        {
            u32 Min;
            u32 Max;
        };
        u64 Arg;
        r64 Number;
    };
};

struct scene2_instruction_chunk
{
    scene2_instruction_chunk *Next;
    u64 InstructionsUsed;
    scene2_instruction Instructions[1];
};

struct scene2_proc_info
{
    string Name;
    u64 InstructionBegin;
    u64 InstructionEnd;
};

global scene2_proc_info S_EmptyProcInfo = {StrComp(""), 0, 0};

struct scene2_proc_node
{
    scene2_proc_node *Next;
    scene2_proc_info Info;
};

struct scene2_proc_list
{
    scene2_proc_node *First;
    scene2_proc_node *Last;
    u64 Count;
};

struct scene2_compiler
{
    arena *Arena;
    
    scene2_message_list Messages;
    
    b32 InPanicMode;
    
    scene2_instruction_chunk *FirstChunk;
    scene2_instruction_chunk *LastChunk;
    u64 InstructionPointer;
    
    scene2_proc_list ProcList;
    
    string Source;
    token_array Tokens;
    s64 At;
};

struct compiled_scene2
{
    string Source;
    
    scene2_message_list Messages;
    
    u64 InstructionCount;
    scene2_instruction *Instructions;
    
    scene2_proc_list ProcList;
};


////////////////////////////////
//~ sixten: Scene Message Functions
static void S2_PushMessage(arena *Arena, scene2_message_list *List, token Token, string Message);
static void S2_PushMessageFV(arena *Arena, scene2_message_list *List, token Token, char *Format, va_list Arguments);
static void S2_PushMessageF(arena *Arena, scene2_message_list *List, token Token, char *Format, ...);
static scene2_message_list S2_CopyMessageList(arena *Arena, scene2_message_list *Source);
static void S2_ConcatMessageList(arena *Arena, scene2_message_list *Dest, scene2_message_list *Source);


////////////////////////////////
//~ sixten: Scene Action Types
enum scene2_action_kind
{
    S2_ActionKind_None = 0,
};

struct scene2_action
{
    scene2_action *Next;
    scene2_action_kind Kind;
};

struct scene2_action_list
{
    scene2_action *First;
    scene2_action *Last;
    u64 Count;
};


////////////////////////////////
//~ sixten: Scene Action Functions
static void S2_SceneActionPush(scene2_action_list *List, scene2_action Action);

////////////////////////////////
//~ sixten: Scene Runtime Types
enum scene2_object_kind
{
    S2_ObjectKind_Nil,
    S2_ObjectKind_Bool,
    S2_ObjectKind_Number,
    S2_ObjectKind_String,
};

global read_only string S2_ObjectKindNameLookup[] =
{
    StrComp("nil"),
    StrComp("bool"),
    StrComp("number"),
    StrComp("string"),
};

struct scene2_object
{
    scene2_object_kind Kind;
    union
    {
        b32 Bool;
        r64 Number;
        string String;
    } As;
};

struct scene2_variable
{
    scene2_variable *Next;
    scene2_variable *Prev;
    string Name;
    scene2_object Value;
};

struct scene2_runtime
{
    scene2_proc_info CurrentProc;
    u64 InstructionPointer;
    
    //- siten: variables
    arena *VariableArena;
    scene2_variable *FirstGlobalVariable;
    scene2_variable *LastGlobalVariable;
    scene2_variable *FirstFreeVariable;
    scene2_variable *LastFreeVariable;
    
    //- sixten: runtime stack
    u64 StackPointer;
    scene2_object Stack[128];
    
    //- sixten: custom callback data
    void *Data;
};

struct scene2_run_result
{
    scene2_message_list Messages;
    scene2_action_list Actions;
};

////////////////////////////////
//~ sixten: Scene Parsing Helpers
static token S2_Token(scene2_compiler *Compiler);
static token S2_Consume(scene2_compiler *Compiler, token_kind Expected, string Message);
static void S2_Advance(scene2_compiler *Compiler);
static scene2_parse_rule S2_ParseRuleFromToken(scene2_compiler *Compiler, token Token);
static scene2_instruction S2_Instruction(scene2_opcode Opcode);
static scene2_instruction S2_Instruction(scene2_opcode Opcode, u64 Arg);
static scene2_instruction S2_Instruction(scene2_opcode Opcode, r64 Number);
static scene2_instruction S2_Instruction(scene2_opcode Opcode, u32 Arg1, u32 Arg2);
static scene2_instruction S2_Instruction(scene2_opcode Opcode, range1_s64 Range);
static scene2_instruction *S2_Emit(scene2_compiler *Compiler, scene2_instruction Instruction);

////////////////////////////////
//~ sixten: Scene Parsing Functions
static void S2_ParseTopLevel(scene2_compiler *Compiler);
static void S2_ParseProc(scene2_compiler *Compiler);
static void S2_ParseBlock(scene2_compiler *Compiler);
static void S2_ParseStatement(scene2_compiler *Compiler);
static void S2_ParseWaitStatement(scene2_compiler *Compiler);
static void S2_ParseIfStatement(scene2_compiler *Compiler);
static void S2_ParseWhileStatement(scene2_compiler *Compiler);
static void S2_ParseLetStatement(scene2_compiler *Compiler);
static void S2_ParseShorthand(scene2_compiler *Compiler);
static void S2_ParseExpression(scene2_compiler *Compiler);
static void S2_ParsePrecedence(scene2_compiler *Compiler, scene2_precedence Precedence);
static void S2_ParseStatement(scene2_compiler *Compiler);
static void S2_ParseGrouping(scene2_compiler *Compiler, b32 CanAssign);
static void S2_ParseUnary(scene2_compiler *Compiler, b32 CanAssign);
static void S2_ParseBinary(scene2_compiler *Compiler, b32 CanAssign);
static void S2_ParseLiteral(scene2_compiler *Compiler, b32 CanAssign);
static void S2_ParseNumber(scene2_compiler *Compiler, b32 CanAssign);
static void S2_ParseString(scene2_compiler *Compiler, b32 CanAssign);
static void S2_ParseVariable(scene2_compiler *Compiler, b32 CanAssign);
static void S2_ParseCall(scene2_compiler *Compiler, b32 CanAssign);


////////////////////////////////
//~ sixten: Scene Runtime Helper Functions
static string S2_StringFromObject(arena *Arena, scene2_object Object);
static void S2_StackPush(scene2_runtime *Runtime, scene2_object Object);
static scene2_object S2_StackPop(scene2_runtime *Runtime);
static scene2_object S2_MakeNil(void);
static scene2_object S2_MakeNumber(r64 Number);
static scene2_object S2_MakeString(string String);
static b32 S2_ObjectIsTrue(scene2_object Object);


////////////////////////////////
//~ sixten: Scene Interface Functions
static compiled_scene2 S2_CompiledFromString(arena *Arena, string String);
static compiled_scene2 S2_CopyCompiledScene(arena *Arena, compiled_scene2 *Source);

static void S2_SetCurrentProc(scene2_runtime *Runtime, scene2_proc_info Info);
static scene2_proc_info S2_ProcFromName(compiled_scene2 *Compiled, string Name);

#endif //VN_SCENE2_H
