#>cpp
#pragma once

#include <unordered_map>

#include "communication/rpc/messages.hpp"
#include "database/distributed/serialization.hpp"
#include "distributed/updates_rpc_messages.capnp.h"
#include "durability/distributed/state_delta.hpp"
#include "storage/distributed/address_types.hpp"
#include "storage/distributed/gid.hpp"
#include "storage/distributed/rpc/serialization.hpp"
#include "transactions/type.hpp"
#include "rpc/serialization.hpp"
cpp<#

(lcp:namespace distributed)

(lcp:capnp-namespace "distributed")

(lcp:capnp-import 'db "/database/distributed/serialization.capnp")
(lcp:capnp-import 'storage "/storage/distributed/rpc/serialization.capnp")
(lcp:capnp-import 'utils "/rpc/serialization.capnp")

(lcp:capnp-type-conversion "tx::TransactionId" "UInt64")
(lcp:capnp-type-conversion "gid::Gid" "UInt64")
(lcp:capnp-type-conversion "storage::Label" "Storage.Label")
(lcp:capnp-type-conversion "storage::EdgeType" "Storage.EdgeType")
(lcp:capnp-type-conversion "storage::Property" "Storage.Property")
(lcp:capnp-type-conversion "storage::EdgeAddress" "Storage.Address")
(lcp:capnp-type-conversion "storage::VertexAddress" "Storage.Address")

(lcp:define-enum update-result
  (done
   serialization-error
   lock-timeout-error
   update-deleted-error
   unable-to-delete-vertex-error)
  (:documentation "The result of sending or applying a deferred update to a worker.")
  (:serialize))

(lcp:define-rpc update
    (:request ((member "database::StateDelta" :capnp-type "Db.StateDelta")))
  (:response ((member "UpdateResult"))))

(lcp:define-rpc update-apply
    (:request ((member "tx::TransactionId")))
  (:response ((member "UpdateResult"))))

(lcp:define-struct create-result ()
  ((result "UpdateResult")
   (cypher-id :int64_t :documentation "Only valid if creation was successful.")
   (gid "gid::Gid" :documentation "Only valid if creation was successful."))
  (:serialize (:slk) (:capnp)))

(lcp:define-struct create-vertex-req-data ()
  ((tx-id "tx::TransactionId")
   (labels "std::vector<storage::Label>"
     :capnp-save (lcp:capnp-save-vector "storage::capnp::Label" "storage::Label")
     :capnp-load (lcp:capnp-load-vector "storage::capnp::Label" "storage::Label"))
   (properties "std::unordered_map<storage::Property, PropertyValue>"
               :capnp-type "Utils.Map(Storage.Property, Storage.PropertyValue)"
               :capnp-save
               (lambda (builder member capnp-name)
                 (declare (ignore capnp-name))
                 #>cpp
                 utils::SaveMap<storage::capnp::Property, storage::capnp::PropertyValue>(
                     ${member}, &${builder},
                     [](auto *builder, const auto &entry) {
                       auto key_builder = builder->initKey();
                       Save(entry.first, &key_builder);
                       auto value_builder = builder->initValue();
                       storage::SaveCapnpPropertyValue(entry.second, &value_builder);
                     });
                 cpp<#)
               :capnp-load
               (lambda (reader member capnp-name)
                 (declare (ignore capnp-name))
                 #>cpp
                 utils::LoadMap<storage::capnp::Property, storage::capnp::PropertyValue>(
                     &${member}, ${reader},
                     [](const auto &reader) {
                       storage::Property prop;
                       storage::Load(&prop, reader.getKey());
                       PropertyValue value;
                       storage::LoadCapnpPropertyValue(reader.getValue(), &value);
                       return std::make_pair(prop, value);
                     });
                 cpp<#))
   (cypher-id "std::experimental::optional<int64_t>"
     :capnp-type "Utils.Optional(Utils.BoxInt64)"
     :capnp-save
     (lambda (builder member capnp-name)
       (declare (ignore capnp-name))
       #>cpp
       utils::SaveOptional<utils::capnp::BoxInt64, int64_t>(
         ${member}, &${builder}, [](auto *builder, const auto &value) {
                                   builder->setValue(value);
                                 });
       cpp<#)
     :capnp-load
     (lambda (reader member capnp-name)
       (declare (ignore capnp-name))
       #>cpp
       ${member} = utils::LoadOptional<utils::capnp::BoxInt64, int64_t>(
         ${reader}, [](const auto &reader) {
                      return reader.getValue();
                    });
       cpp<#)))
  (:serialize (:slk) (:capnp)))

(lcp:define-rpc create-vertex
    (:request ((member "CreateVertexReqData")))
  (:response ((member "CreateResult"))))

(lcp:define-struct create-edge-req-data ()
  ((from "gid::Gid")
   (to "storage::VertexAddress")
   (edge-type "storage::EdgeType")
   (tx-id "tx::TransactionId")
   (cypher-id "std::experimental::optional<int64_t>"
     :capnp-type "Utils.Optional(Utils.BoxInt64)"
     :capnp-save
     (lambda (builder member capnp-name)
       (declare (ignore capnp-name))
       #>cpp
       utils::SaveOptional<utils::capnp::BoxInt64, int64_t>(
         ${member}, &${builder}, [](auto *builder, const auto &value) {
                                   builder->setValue(value);
                                 });
       cpp<#)
     :capnp-load
     (lambda (reader member capnp-name)
       (declare (ignore capnp-name))
       #>cpp
       ${member} = utils::LoadOptional<utils::capnp::BoxInt64, int64_t>(
           ${reader}, [](const auto &reader) {
                        return reader.getValue();
                      });
       cpp<#)))
  (:serialize (:slk) (:capnp)))

(lcp:define-rpc create-edge
    (:request ((member "CreateEdgeReqData")))
  (:response ((member "CreateResult"))))

(lcp:define-struct add-in-edge-req-data ()
  ((from "storage::VertexAddress")
   (edge-address "storage::EdgeAddress")
   (to "gid::Gid")
   (edge-type "storage::EdgeType")
   (tx-id "tx::TransactionId"))
  (:serialize (:slk) (:capnp)))

(lcp:define-rpc add-in-edge
    (:request ((member "AddInEdgeReqData")))
  (:response ((member "UpdateResult"))))

(lcp:define-struct remove-vertex-req-data ()
  ((gid "gid::Gid")
   (tx-id "tx::TransactionId")
   (check-empty :bool))
  (:serialize (:slk) (:capnp)))

(lcp:define-rpc remove-vertex
    (:request ((member "RemoveVertexReqData")))
  (:response ((member "UpdateResult"))))

(lcp:define-struct remove-edge-data ()
  ((tx-id "tx::TransactionId")
   (edge-id "gid::Gid")
   (vertex-from-id "gid::Gid")
   (vertex-to-address "storage::VertexAddress"))
  (:serialize (:slk) (:capnp)))

(lcp:define-rpc remove-edge
    (:request ((member "RemoveEdgeData")))
  (:response ((member "UpdateResult"))))

(lcp:define-struct remove-in-edge-data ()
  ((tx-id "tx::TransactionId")
   (vertex "gid::Gid")
   (edge-address "storage::EdgeAddress"))
  (:serialize (:slk) (:capnp)))

(lcp:define-rpc remove-in-edge
    (:request ((member "RemoveInEdgeData")))
  (:response ((member "UpdateResult"))))

(lcp:define-struct created-info ()
  ((cypher-id "int64_t")
   (gid "gid::Gid"))
  (:public #>cpp
    CreatedInfo(int64_t cypher_id, gid::Gid gid)
        : cypher_id(cypher_id), gid(gid) {}
  cpp<#))

(lcp:define-struct created-vertex-info ()
  ((cypher-id "int64_t")
   (gid "gid::Gid"))
  (:public #>cpp
    CreatedVertexInfo(int64_t cypher_id, gid::Gid gid)
        : cypher_id(cypher_id), gid(gid) {}
  cpp<#))

(lcp:define-struct created-edge-info ()
  ((cypher-id "int64_t")
   (edge-address "storage::EdgeAddress"))
  (:public #>cpp
    CreatedEdgeInfo(int64_t cypher_id, storage::EdgeAddress edge_address)
        : cypher_id(cypher_id), edge_address(edge_address) {}
  cpp<#))

(lcp:pop-namespace) ;; distributed
