/* date = July 30th 2023 7:45 pm */

#ifndef VN_SCENE_H
#define VN_SCENE_H

// sixten(IMPORTANT): The arena that the scene is compiled on
// is assumed to be the same it runs on, i.e. the interpreter relies 
// on the memory addresses being the same during compilation and
// runtime.
// We also assume that the original text and tokenization remains in
// memory, for the duration of the scene.

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

struct scene_compile_error_list
{
    scene_compile_error *First;
    scene_compile_error *Last;
    s64 Count;
};

typedef u64 scene_line_entry_flag;
enum
{
    S_LineEntryFlag_NoClear = (1<<0),
};

enum scene_opcode
{
    S_Op_Invalid = 0,
    
    S_Op_Constant,
    S_Op_Pop,
    
    S_Op_Nil,
    S_Op_True,
    S_Op_False,
    
    S_Op_Add,
    S_Op_Subtract,
    S_Op_Multiply,
    S_Op_Divide,
    
    S_Op_Equal,
    S_Op_Greater,
    S_Op_Less,
    
    S_Op_Negate,
    S_Op_Not,
    
    S_Op_DefineGlobal,
    S_Op_GetGlobal,
    S_Op_SetGlobal,
    
    S_Op_Jump,
    S_Op_JumpClose,
    S_Op_AddBranch,
    S_Op_Halt,
    
    S_Op_AwaitInput,
    
    S_Op_LineEntry = 0x80, // sixten(NOTE): All opcoodes above are reserved.
};

struct scene_bytecode_chunk
{
    scene_bytecode_chunk *Next;
    s64 Count;
    u8 Data[4096];
};

struct scene_annotated_bytecode_chunk
{
    scene_annotated_bytecode_chunk *Next;
    string Name;
    s64 Count;
    u8 Data[4096];
};

struct scene_annotated_bytecode_bucket
{
    scene_annotated_bytecode_chunk *First;
    scene_annotated_bytecode_chunk *Last;
    s64 Count;
};

enum scene_value_kind
{
    S_ValueKind_Nil = 0,
    S_ValueKind_Number,
    S_ValueKind_Boolean,
    S_ValueKind_Pointer,
    S_ValueKind_SourceRef,
    S_ValueKind_String,
    S_ValueKind_Offset,
};

struct scene_value
{
    scene_value_kind Kind;
    union
    {
        r64 Number;
        b32 Boolean;
        u64 Pointer;
        range1_s64 SourceRef;
        s64 Offset;
    };
};

struct scene_value_chunk
{
    scene_value_chunk *Next;
    s64 Count;
    scene_value Values[512];
};

enum scene_precedence
{
    S_Precedence_None,
    S_Precedence_Assignment,
    S_Precedence_Or,
    S_Precedence_And,
    S_Precedence_Equality,
    S_Precedence_Comparison,
    S_Precedence_Term,
    S_Precedence_Factor,
    S_Precedence_Unary,
    S_Precedence_Call,
    S_Precedence_Primary,
};

typedef void scene_parse_function(struct scene_compiler *Compiler, b32 CanAssign);

struct scene_parse_rule
{
    scene_parse_function *PrefixRule;
    scene_parse_function *InfixRule;
    scene_precedence Precedence;
};

enum scene_emission_target_type
{
    S_EmissionTarget_Raw,
    S_EmissionTarget_Named,
};

struct scene_emission_target
{
    memory_arena *Arena;
    scene_annotated_bytecode_bucket *Bucket;
    scene_emission_target_type Type;
    string Name;
};

struct scene_branch_case
{
    scene_branch_case *Next;
    token Name;
    scene_annotated_bytecode_bucket Bucket;
    scene_value *OffsetValue;
    scene_value *EndOffsetValue;
};

struct scene_character
{
    string Name;
    scene_character *Next;
    scene_character *Prev;
};

struct scene_character_bucket
{
    scene_character *First;
    scene_character *Last;
    s64 Count;
};

struct scene_compiler
{
    memory_arena *Arena;
    
    b32 InPanicMode;
    b32 EncounteredError;
    scene_compile_error_list Errors;
    
    string Text;
    token *TokensBegin;
    token *TokensEnd;
    token *At;
    
    scene_annotated_bytecode_bucket GlobalScope;
    scene_annotated_bytecode_bucket ProcBuckets[32];
    
    scene_emission_target TargetStack[16];
    s32 TargetStackIndex;
    
    scene_value_chunk *FirstValueChunk;
    scene_value_chunk *LastValueChunk;
    s64 ValueCount;
};

////////////////////////////////
//~ sixten: Compiled Scene Types
struct scene_proc
{
    // sixten: scene data
    string Name;
    u8 *Data;
    s64 Count;
    
    // sixten: hash link
    scene_proc *Next;
};

struct scene_proc_bucket
{
    scene_proc *First;
    scene_proc *Last;
};

struct compiled_scene
{
    scene_proc_bucket Buckets[16];
    scene_value *Values;
    s64 ValueCount;
    string Source;
    scene_compile_error_list Errors;
    b32 IsValid;
};

////////////////////////////////
//~ sixten: Scene Runtime Types
struct scene_runtime_error
{
    scene_runtime_error *Next;
    scene_runtime_error *Prev;
    string Message;
};

struct scene_runtime_result
{
    b32 HadError;
    b32 ReachedAwait;
};

enum textbox_action_kind
{
    TextboxActionKind_Set,
    TextboxActionKind_Append,
};

struct textbox_action
{
    textbox_action *Next;
    textbox_action_kind Kind;
    string String;
};

struct branch_case
{
    string Name;
    s64 Offset;
};

struct scene_runtime_stack
{
    scene_value Stack[128];
    s32 Count;
};

struct scene_runtime
{
    compiled_scene Compiled;
    
    // sixten: runtime state
    scene_proc *CurrentProc;
    s64 IP;
    memory_arena *Arena;
    scene_runtime_stack Stack;
    
    // sixten: errors
    memory_arena *ErrorArena;
    scene_runtime_error *FirstError;
    scene_runtime_error *LastError;
    
    // sixten: branches
    branch_case Branches[16];
    s64 BranchCount;
    
    // sixten: result
    textbox_action *FirstTextboxAction;
    textbox_action *LastTextboxAction;
    branch_case *FirstBranchCase;
    scene_runtime_result LastResult;
};

////////////////////////////////
//~ sixten: Scene Compiler Functions

//- sixten: value helpers
inline scene_value S_MakeNil(void)
{
    scene_value Result;
    Result.Kind = S_ValueKind_Nil;
    return(Result);
}

inline scene_value S_MakeNumber(r64 Value)
{
    scene_value Result;
    Result.Kind = S_ValueKind_Number;
    Result.Number = Value;
    return(Result);
}

inline scene_value S_MakeBoolean(b32 Value)
{
    scene_value Result;
    Result.Kind = S_ValueKind_Boolean;
    Result.Boolean = Value;
    return(Result);
}

inline scene_value S_MakePointer(void *Value)
{
    scene_value Result;
    Result.Kind = S_ValueKind_Pointer;
    Result.Pointer = PointerToU64(Value);
    return(Result);
}

inline scene_value S_MakeSourceRef(token Token)
{
    scene_value Result;
    Result.Kind = S_ValueKind_SourceRef;
    Result.SourceRef = Token.Range;
    return(Result);
}

inline scene_value S_MakeOffset(s64 Offset)
{
    scene_value Result;
    Result.Kind = S_ValueKind_Offset;
    Result.Offset = Offset;
    return(Result);
}

//- sixten: error messaging
static void S_ParseError(scene_compiler *Compiler, char *Message, s64 TokenOffset = -1);

//- sixten: bytecode helpers
static void S_EmitByte(scene_compiler *Compiler, u8 Byte);
static void S_EmitU32(scene_compiler *Compiler, u32 Value);
static void S_EmitBucket(scene_compiler *Compiler, scene_annotated_bytecode_bucket *Bucket);
static u64 S_MakeConstant(scene_compiler *Compiler, scene_value Value);
static void S_EmitConstant(scene_compiler *Compiler, scene_value Value);
static void S_SetEmissionTarget(scene_compiler *Compiler, scene_emission_target Target);
static void S_PushEmissionTarget(scene_compiler *Compiler, scene_emission_target Target);
static void S_PopEmissionTarget(scene_compiler *Compiler);
static scene_annotated_bytecode_chunk *S_FindBytecodeChunkByName(scene_compiler *Compiler, string Name);

inline scene_emission_target S_RawEmissionTarget(memory_arena *Arena, scene_annotated_bytecode_bucket *Bucket)
{
    scene_emission_target Target = {};
    Target.Arena = Arena;
    Target.Bucket = Bucket;
    Target.Type = S_EmissionTarget_Raw;
    return(Target);
}

inline scene_emission_target S_NamedEmissionTarget(memory_arena *Arena, scene_annotated_bytecode_bucket *Bucket, string Name)
{
    scene_emission_target Target = {};
    Target.Arena = Arena;
    Target.Bucket = Bucket;
    Target.Type = S_EmissionTarget_Named;
    Target.Name = Name;
    return(Target);
}

//- sixten: parsing helpers
static void S_AdvanceCompiler(scene_compiler *Compiler);
static scene_parse_rule S_ParseRuleFromToken(scene_compiler *Compiler, token Token);
static b32 S_MatchToken(scene_compiler *Compiler, token Token, token_kind Kind);
static token S_ConsumeToken(scene_compiler *Compiler, token_kind Kind, char *Message);

//- sixten: parsing
static void S_ParseTopLevelDeclaration(scene_compiler *Compiler);
static void S_ParseProcedure(scene_compiler *Compiler);
static void S_ParseDeclaration(scene_compiler *Compiler);
static void S_ParseVariableDeclaration(scene_compiler *Compiler);
static void S_ParseVariable(scene_compiler *Compiler, b32 CanAssign);
static void S_ParseNamedVariable(scene_compiler *Compiler, token Token, b32 CanAssign);
static void S_ParseLineEntry(scene_compiler *Compiler);
static void S_ParseJumpStatement(scene_compiler *Compiler);
static void S_ParseBranchStatement(scene_compiler *Compiler);
static scene_branch_case *S_ParseBranchCase(scene_compiler *Compiler, memory_arena *Arena);
static void S_ParseStatement(scene_compiler *Compiler);
static void S_ParseExpression(scene_compiler *Compiler);
static void S_ParseLiteral(scene_compiler *Compiler, b32 CanAssign);
static void S_ParseNumber(scene_compiler *Compiler, b32 CanAssign);
static void S_ParseString(scene_compiler *Compiler, b32 CanAssign);
static void S_ParseGrouping(scene_compiler *Compiler, b32 CanAssign);
static void S_ParseUnary(scene_compiler *Compiler, b32 CanAssign);
static void S_ParseBinary(scene_compiler *Compiler, b32 CanAssign);
static void S_ParsePrecedence(scene_compiler *Compiler, scene_precedence Precedence);

//- sixten: debugging
static string S_DisassembleBytecode(scene_compiler *Compiler, scene_annotated_bytecode_chunk *Chunk, memory_arena *Arena);

//- sixten: compilation
static compiled_scene S_ScriptFromText(memory_arena *Arena, string Text);
static compiled_scene S_CopyCompiledScene(memory_arena *Arena, compiled_scene *Compiled);

////////////////////////////////
//~ sixten: Scene Runtime Functions
static scene_proc *S_FindProcByName(compiled_scene *Compiled, string Name);
static scene_runtime_result S_Run(scene_runtime *Runtime, memory_arena *FrameArena, b32 AdvanceOnAwait);

#endif //VN_SCENE_H
