open Core_kernel


let get_imports_code ctx =
  match ctx.Context.ctx_config.Config.c_lib_path with
  | Some p -> In_channel.read_all p
  | None -> ""

(** Converts all statement in the [ctx] to Lua code. *)
let ctx_to_string ctx =
  let open Ast in
  let stmts_to_s ?(cr=true) stmts =
    List.fold_left
      stmts
      ~init:[]
      ~f:(fun acc stmt -> acc @ [stmt_to_s ctx.Context.ctx_config stmt ~cr:cr])
    |> String.concat ~sep:"\n"
  in
  let header = Printf.sprintf({|--------------------------------------------------------
-- This code was automatically generated by moonsmith --
-- https://github.com/jubnzv/moonsmith                --
--                                                    --
-- Seed: %20d                         --
--------------------------------------------------------
|}) ctx.ctx_seed
  and imports = get_imports_code ctx
  and datums_header = Printf.sprintf({|--------------------------------------------------------
-- Global datums definitions (%3d statements)         --
--------------------------------------------------------|}) (List.length ctx.ctx_datum_stmts)
  and datums_code = stmts_to_s ~cr:false ctx.ctx_datum_stmts
  and funcdefs_header = Printf.sprintf({|
--------------------------------------------------------
-- Function definitions (%3d functions)               --
--------------------------------------------------------|}) (List.length ctx.ctx_funcdef_stmts)
  and funcdefs_code = stmts_to_s ctx.ctx_funcdef_stmts
  and calls_header = Printf.sprintf({|--------------------------------------------------------
-- Calling functions                                  --
--------------------------------------------------------|})
  and calls_code = stmts_to_s ~cr:false ctx.ctx_call_stmts
  and result_header = Printf.sprintf({|
--------------------------------------------------------
-- Combining and printing result                      --
--------------------------------------------------------|})
  and result_code = stmts_to_s ~cr:false ctx.ctx_result_stmts
  in
  String.concat [header;
                 imports;
                 datums_header;
                 datums_code;
                 funcdefs_header;
                 funcdefs_code;
                 calls_header;
                 calls_code;
                 result_header;
                 result_code]
    ~sep:"\n"

(** Generates the whole program for the given [ctx].
    A typical generated program is represented by the following parts:
    - data definition
    - methods and free functions that mutates the data
    - some driver code to call generated functions and methods
    - code that combines and prints the global datums *)
let gen_program ctx =
  GeneratorDatums.generate ctx
  |> GeneratorFuncDefs.generate
  |> GeneratorCalls.generate
  |> GeneratorResult.generate
  |> ctx_to_string

let generate (c : Config.t) =
  let ctx = Context.mk_context c in
  Random.init ctx.ctx_seed;
  gen_program ctx
