src/gleeam_code@init.erl

-module(gleeam_code@init).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleeam_code/init.gleam").
-export([run/2]).

-file("src/gleeam_code/init.gleam", 184).
-spec result_try(
    {ok, nil} | {error, binary()},
    fun((nil) -> {ok, nil} | {error, binary()})
) -> {ok, nil} | {error, binary()}.
result_try(Result, Next) ->
    case Result of
        {ok, _} ->
            Next(nil);

        {error, Err} ->
            {error, Err}
    end.

-file("src/gleeam_code/init.gleam", 194).
-spec create_file(binary(), binary(), binary(), fun((binary()) -> nil)) -> {ok,
        nil} |
    {error, binary()}.
create_file(Path, Content, Label, Print) ->
    case gleeam_code@internal@file:exists(Path) of
        true ->
            Print(
                <<<<"  "/utf8, Label/binary>>/binary,
                    " already exists, skipping"/utf8>>
            ),
            {ok, nil};

        false ->
            case gleeam_code@internal@file:write(Path, Content) of
                {ok, _} ->
                    Print(<<"  Created "/utf8, Label/binary>>),
                    {ok, nil};

                {error, Err} ->
                    {error,
                        <<<<<<"Failed to create "/utf8, Label/binary>>/binary,
                                ": "/utf8>>/binary,
                            (gleeam_code@internal@file:describe_error(Err))/binary>>}
            end
    end.

-file("src/gleeam_code/init.gleam", 219).
-spec create_solutions_dir(binary(), binary(), fun((binary()) -> nil)) -> {ok,
        nil} |
    {error, binary()}.
create_solutions_dir(Path, Label, Print) ->
    case gleeam_code@internal@file:dir_exists(Path) of
        true ->
            Print(
                <<<<"  "/utf8, Label/binary>>/binary,
                    " already exists, skipping"/utf8>>
            ),
            {ok, nil};

        false ->
            case gleeam_code@internal@file:mkdir(Path) of
                {ok, _} ->
                    Print(<<"  Created "/utf8, Label/binary>>),
                    {ok, nil};

                {error, Err} ->
                    {error,
                        <<<<<<"Failed to create "/utf8, Label/binary>>/binary,
                                ": "/utf8>>/binary,
                            (gleeam_code@internal@file:describe_error(Err))/binary>>}
            end
    end.

-file("src/gleeam_code/init.gleam", 243).
-spec create_glc_toml(binary(), fun((binary()) -> nil)) -> {ok, nil} |
    {error, binary()}.
create_glc_toml(Path, Print) ->
    case gleeam_code@internal@file:exists(Path) of
        true ->
            Print(<<"  .glc.toml already exists, skipping"/utf8>>),
            {ok, nil};

        false ->
            case gleeam_code@internal@file:write(
                Path,
                <<"# glc project config
[project]
solutions_dir = \"solutions\"
"/utf8>>
            ) of
                {ok, _} ->
                    Print(<<"  Created .glc.toml"/utf8>>),
                    {ok, nil};

                {error, Err} ->
                    {error,
                        <<"Failed to create .glc.toml: "/utf8,
                            (gleeam_code@internal@file:describe_error(Err))/binary>>}
            end
    end.

-file("src/gleeam_code/init.gleam", 151).
-spec run(binary(), fun((binary()) -> nil)) -> {ok, nil} | {error, binary()}.
run(Base_dir, Print) ->
    Gleam_toml = <<Base_dir/binary, "/gleam.toml"/utf8>>,
    Src_solutions = <<Base_dir/binary, "/src/solutions"/utf8>>,
    Test_solutions = <<Base_dir/binary, "/test/solutions"/utf8>>,
    Glc_toml = <<Base_dir/binary, "/.glc.toml"/utf8>>,
    Types_gleam = <<Base_dir/binary, "/src/types.gleam"/utf8>>,
    Types_ffi = <<Base_dir/binary, "/src/types_ffi.erl"/utf8>>,
    case gleeam_code@internal@file:exists(Gleam_toml) of
        false ->
            {error,
                <<"gleam.toml not found. Run 'gleam new <project>' first."/utf8>>};

        true ->
            result_try(
                create_solutions_dir(
                    Src_solutions,
                    <<"src/solutions/"/utf8>>,
                    Print
                ),
                fun(_) ->
                    result_try(
                        create_solutions_dir(
                            Test_solutions,
                            <<"test/solutions/"/utf8>>,
                            Print
                        ),
                        fun(_) ->
                            result_try(
                                create_glc_toml(Glc_toml, Print),
                                fun(_) ->
                                    result_try(
                                        create_file(
                                            Types_gleam,
                                            <<"import gleam/list
import gleam/option.{type Option, None, Some}

pub type TreeNode {
  TreeNode(val: Int, left: Option(TreeNode), right: Option(TreeNode))
}

pub type ListNode {
  ListNode(val: Int, next: Option(ListNode))
}

pub fn tree_from_level_order(values: List(Option(Int))) -> Option(TreeNode) {
  case values {
    [] -> None
    [None, ..] -> None
    [Some(root_val), ..rest] -> {
      let #(flat, _) = bfs_assign([0], rest, [#(Some(root_val), -1, -1)], 1)
      build_tree_from_flat(flat, 0)
    }
  }
}

fn bfs_assign(
  queue: List(Int),
  remaining: List(Option(Int)),
  flat: List(#(Option(Int), Int, Int)),
  next_idx: Int,
) -> #(List(#(Option(Int), Int, Int)), List(Option(Int))) {
  case queue {
    [] -> #(flat, remaining)
    [parent_idx, ..rest_queue] -> {
      let #(left_val, after_left) = take_next(remaining)
      let #(right_val, after_right) = take_next(after_left)
      let #(left_idx, flat2, queue2, idx2) = case left_val {
        None -> #(-1, flat, rest_queue, next_idx)
        Some(_) -> #(
          next_idx,
          list.append(flat, [#(left_val, -1, -1)]),
          list.append(rest_queue, [next_idx]),
          next_idx + 1,
        )
      }
      let #(right_idx, flat3, queue3, idx3) = case right_val {
        None -> #(-1, flat2, queue2, idx2)
        Some(_) -> #(
          idx2,
          list.append(flat2, [#(right_val, -1, -1)]),
          list.append(queue2, [idx2]),
          idx2 + 1,
        )
      }
      let flat4 =
        list.index_map(flat3, fn(entry, i) {
          case i == parent_idx {
            True -> #(entry.0, left_idx, right_idx)
            False -> entry
          }
        })
      bfs_assign(queue3, after_right, flat4, idx3)
    }
  }
}

fn take_next(
  values: List(Option(Int)),
) -> #(Option(Int), List(Option(Int))) {
  case values {
    [] -> #(None, [])
    [v, ..rest] -> #(v, rest)
  }
}

fn build_tree_from_flat(
  flat: List(#(Option(Int), Int, Int)),
  idx: Int,
) -> Option(TreeNode) {
  case idx < 0 {
    True -> None
    False ->
      case list.drop(flat, idx) {
        [] -> None
        [#(None, _, _), ..] -> None
        [#(Some(val), left_idx, right_idx), ..] -> {
          let left = build_tree_from_flat(flat, left_idx)
          let right = build_tree_from_flat(flat, right_idx)
          Some(TreeNode(val: val, left: left, right: right))
        }
      }
  }
}

pub fn list_from_list(values: List(Int)) -> Option(ListNode) {
  case values {
    [] -> None
    _ -> Some(do_list_from_list(values))
  }
}

fn do_list_from_list(values: List(Int)) -> ListNode {
  case values {
    [] -> panic as \"unreachable: empty list\"
    [x] -> ListNode(val: x, next: None)
    [x, ..rest] -> ListNode(val: x, next: Some(do_list_from_list(rest)))
  }
}
"/utf8>>,
                                            <<"src/types.gleam"/utf8>>,
                                            Print
                                        ),
                                        fun(_) ->
                                            create_file(
                                                Types_ffi,
                                                <<"-module(types_ffi).
-export([tree_to_record/1, tree_from_record/1,
         list_to_record/1, list_from_record/1]).

-record(tree_node, {val = 0, left = null, right = null}).
-record(list_node, {val = 0, next = null}).

tree_to_record(none) ->
    null;
tree_to_record({some, {tree_node, Val, Left, Right}}) ->
    #tree_node{val = Val,
               left = tree_to_record(Left),
               right = tree_to_record(Right)};
tree_to_record({tree_node, Val, Left, Right}) ->
    #tree_node{val = Val,
               left = tree_to_record(Left),
               right = tree_to_record(Right)}.

tree_from_record(null) ->
    none;
tree_from_record(#tree_node{val = Val, left = Left, right = Right}) ->
    {some, {tree_node, Val, tree_from_record(Left), tree_from_record(Right)}}.

list_to_record(none) ->
    null;
list_to_record({some, {list_node, Val, Next}}) ->
    #list_node{val = Val, next = list_to_record(Next)};
list_to_record({list_node, Val, Next}) ->
    #list_node{val = Val, next = list_to_record(Next)}.

list_from_record(null) ->
    none;
list_from_record(#list_node{val = Val, next = Next}) ->
    {some, {list_node, Val, list_from_record(Next)}}.
"/utf8>>,
                                                <<"src/types_ffi.erl"/utf8>>,
                                                Print
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
    end.