#>cpp
#pragma once

#include <type_traits>

#include "query/frontend/ast/ast.hpp"
#include "query/frontend/ast/ast_serialization.capnp.h"
#include "query/serialization.hpp"
#include "storage/distributed/rpc/serialization.hpp"
cpp<#

(load "query/frontend/ast/ast.lcp")

(lcp:namespace query)

#>cpp
/// Primary function for saving Ast nodes via SLK.
void SaveAstPointer(const Tree *ast, slk::Builder *builder);

Tree *Load(AstStorage *ast, const capnp::Tree::Reader &tree);

Tree *Load(AstStorage *ast, slk::Reader *reader);

/// Primary function for loading Ast nodes via SLK.
template <class TAst>
TAst *LoadAstPointer(AstStorage *ast, slk::Reader *reader) {
  static_assert(std::is_base_of<query::Tree, TAst>::value);
  bool has_ptr = false;
  slk::Load(&has_ptr, reader);
  if (!has_ptr) {
    return nullptr;
  }
  auto *ret = utils::Downcast<TAst>(Load(ast, reader));
  if (!ret) {
    throw slk::SlkDecodeException("Loading unknown Ast node type");
  }
  return ret;
}
cpp<#

(lcp:in-impl
  #>cpp
  void SaveAstPointer(const Tree *ast, slk::Builder *builder) {
    slk::Save(static_cast<bool>(ast), builder);
    if (!ast) {
      return;
    }
    slk::Save(*ast, builder);
  }

  Tree *Load(AstStorage *ast, slk::Reader *reader) {
    std::unique_ptr<Tree> root;
    slk::ConstructAndLoad(&root, reader, ast);
    ast->storage_.emplace_back(std::move(root));
    return ast->storage_.back().get();
  }

  Tree *Load(AstStorage *ast, const capnp::Tree::Reader &tree) {
    std::unique_ptr<Tree> root;
    ::query::Load(&root, tree, ast);
    ast->storage_.emplace_back(std::move(root));
    return ast->storage_.back().get();
  }
  cpp<#)

(lcp:pop-namespace) ;; namespace query
