// Axel '0vercl0k' Souchet - December 29 2022
#pragma once
#include "rng.h"
#include <array>
#include <cstdint>
#include <cstdlib>
#include <fmt/format.h>
#include <fmt/printf.h>
#include <map>
#include <optional>
#include <string>
#include <variant>
#include <vector>

namespace oua {
    class Deserializer_t {
        const std::vector<uint8_t>& Buffer_;
        size_t Index_ = 0;

    public:
        Deserializer_t(const std::vector<uint8_t>& Buffer) : Buffer_(Buffer) {}

        void Advance(const size_t Offset) {
            Index_ += Offset;
            if (Index_ > Buffer_.size()) {
                throw std::runtime_error("Advance is past the end of the buffer");
            }
        }

        void Read(const uint8_t* Storage, const size_t StorageLen) {
            if (Index_ + StorageLen > Buffer_.size()) {
                throw std::runtime_error("Not enough space to Read");
            }

            std::memcpy((void*)Storage, Buffer_.data() + Index_, StorageLen);
            Advance(StorageLen);
        }

        [[nodiscard]] uint8_t Read8() {
            uint8_t Value = 0;
            Read(&Value, sizeof(Value));
            return Value;
        }

        [[nodiscard]] uint16_t Read16() {
            uint16_t Value = 0;
            Read((uint8_t*)&Value, sizeof(Value));
            return Value;
        }

        [[nodiscard]] uint32_t Read32() {
            uint32_t Value = 0;
            Read((uint8_t*)&Value, sizeof(Value));
            return Value;
        }

        [[nodiscard]] uint64_t Read64() {
            uint64_t Value = 0;
            Read((uint8_t*)&Value, sizeof(Value));
            return Value;
        }

        [[nodiscard]] size_t Offset() const { return Index_; }
    };

    enum KnownNodeIds_t : uint32_t {
        AnonymousIdentityToken = 0x141,
        NodeAttributes = 0x15f,
        ObjectAttributes = 0x162,
        VariableAttributes = 0x165,
        MethodAttributes = 0x168,
        ObjectTypeAttributes = 0x16b,
        VariableTypeAttributes = 0x16e,
        ReferenceTypeAttributes = 0x171,
        DataTypeAttributes = 0x174,
        ViewAttributes = 0x177,
        FindServersRequest = 0x1a6,
        GetEndpointsRequest = 0x1ac,
        GetEndpointsResponse = 0x1af,
        OpenSecureChannelRequest = 0x1be,
        OpenSecureChannelResponse = 0x1c1,
        CreateSessionRequest = 0x1cd,
        CreateSessionResponse = 0x1d0,
        ActivateSessionRequest = 0x1d3,
        ActivateSessionResponse = 0x1d6,
        CloseSessionRequest = 0x1d9,
        CancelRequest = 0x1df,
        AddNodesRequest = 0x1e8,
        BrowseRequest = 0x20f,
        BrowseNextRequest = 0x215,
        ReadRequest = 0x277,
        ReadResponse = 0x27a,
        WriteRequest = 0x2a1,
        CallRequest = 0x2c8
    };

    // https://reference.opcfoundation.org/Core/Part4/v105/docs/7.20#Table143
    enum MessageSecurityMode_t : uint32_t {
        INVALID = 0,
        NONE = 1,
        SIGN = 2,
        SIGNANDENCRYPT = 3
    };

    //
    // Trivial types.
    //

    struct Uint8_t {
        uint8_t Value = 0;

        Uint8_t() = default;

        Uint8_t(const size_t Uint8) : Value(uint8_t(Uint8)) {}

        Uint8_t& operator^=(const size_t N) {
            Value ^= N;
            return *this;
        }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}Uint8_t({})\n", " ", Spaces, Value);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Buffer.insert(Buffer.end(), &Value, &Value + 1);
        }

        void DeserializeFrom(Deserializer_t& Buffer) { Value = Buffer.Read8(); }
    };

    struct Uint16_t {
        uint16_t Value = 0;

        Uint16_t() = default;

        Uint16_t(const uint16_t Uint16) : Value(Uint16) {}

        Uint16_t& operator^=(const size_t N) {
            Value ^= N;
            return *this;
        }

        operator uint16_t() const { return Value; }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}Uint16_t({})\n", " ", Spaces, Value);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Buffer.insert(Buffer.end(), (uint8_t*)&Value, (uint8_t*)(&Value + 1));
        }

        void DeserializeFrom(Deserializer_t& Buffer) { Value = Buffer.Read16(); }
    };

    struct Uint32_t {
        uint32_t Value = 0;

        Uint32_t() = default;

        Uint32_t(const uint32_t Uint32) : Value(Uint32) {}

        Uint32_t operator++(int) {
            Uint32_t Copy(Value);
            Value++;
            return Copy;
        }

        Uint32_t& operator+=(const uint32_t N) {
            Value += N;
            return *this;
        }

        Uint32_t& operator|=(const uint32_t N) {
            Value |= N;
            return *this;
        }

        Uint32_t& operator^=(const uint32_t N) {
            Value ^= N;
            return *this;
        }

        operator uint32_t() const { return Value; }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}Uint32_t({})\n", " ", Spaces, Value);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Buffer.insert(Buffer.end(), (uint8_t*)&Value, (uint8_t*)(&Value + 1));
        }

        void DeserializeFrom(Deserializer_t& Buffer) { Value = Buffer.Read32(); }
    };

    struct Uint64_t {
        uint64_t Value = 0;

        Uint64_t() = default;

        Uint64_t(const uint64_t Uint64) : Value(Uint64) {}

        Uint64_t operator++(int) {
            Uint64_t Copy(Value);
            Value++;
            return Copy;
        }

        Uint64_t& operator+=(const size_t N) {
            Value += N;
            return *this;
        }

        Uint64_t& operator|=(const size_t N) {
            Value |= N;
            return *this;
        }

        Uint64_t& operator^=(const size_t N) {
            Value ^= N;
            return *this;
        }

        operator uint64_t() const { return Value; }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}Uint64_t({})\n", " ", Spaces, Value);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Buffer.insert(Buffer.end(), (uint8_t*)&Value, (uint8_t*)(&Value + 1));
        }

        void DeserializeFrom(Deserializer_t& Buffer) { Value = Buffer.Read64(); }
    };

    struct Int32_t {
        int32_t Value = 0;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}Int32_t({})\n", " ", Spaces, Value);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Buffer.insert(Buffer.end(), (uint8_t*)&Value, (uint8_t*)(&Value + 1));
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            Value = int32_t(Buffer.Read32());
        }
    };

    struct Double_t {
        double Value = 0;

        Double_t() = default;

        Double_t(const double _Value) : Value(_Value) {}

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}Double_t({})\n", " ", Spaces, Value);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Buffer.insert(Buffer.end(), (uint8_t*)&Value, (uint8_t*)(&Value + 1));
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            Value = double(Buffer.Read64());
        }
    };

    struct Float_t {
        float Value = 0;

        Float_t() = default;

        Float_t(const float _Value) : Value(_Value) {}

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}Float_t({})\n", " ", Spaces, Value);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Buffer.insert(Buffer.end(), (uint8_t*)&Value, (uint8_t*)(&Value + 1));
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            Value = float(Buffer.Read32());
        }
    };

    //
    // A bit less trivial types.
    //

    struct StatusCode_t {
        uint32_t Value;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}StatusCode_t({})\n", " ", Spaces, Value);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Buffer.insert(Buffer.end(), (uint8_t*)&Value, (uint8_t*)(&Value + 1));
        }

        void DeserializeFrom(Deserializer_t& Buffer) { Value = Buffer.Read32(); }
    };

    struct DateTime_t {
        uint64_t Value = 0;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}DateTime_t({})\n", " ", Spaces, Value);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Buffer.insert(Buffer.end(), (uint8_t*)&Value, (uint8_t*)(&Value + 1));
        }

        void DeserializeFrom(Deserializer_t& Buffer) { Value = Buffer.Read64(); }
    };

    struct Guid_t {
        struct {
            Uint32_t Data1;
            Uint32_t Data2;
            Uint32_t Data3;
            Uint32_t Data4;
        } Value;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}Guid_t({:x}, {:x}, {:x}, {:x})\n", " ", Spaces,
                Value.Data1.Value, Value.Data2.Value, Value.Data3.Value,
                Value.Data4.Value);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Value.Data1.SerializeInto(Buffer);
            Value.Data2.SerializeInto(Buffer);
            Value.Data3.SerializeInto(Buffer);
            Value.Data4.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            Value.Data1.DeserializeFrom(Buffer);
            Value.Data2.DeserializeFrom(Buffer);
            Value.Data3.DeserializeFrom(Buffer);
            Value.Data4.DeserializeFrom(Buffer);
        }
    };

    struct None_t {
        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}None_t\n", " ", Spaces);
        }
        void SerializeInto(std::vector<uint8_t>& Buffer) const {}
        void DeserializeFrom(Deserializer_t& Buffer) {}
    };

    //
    // More complex types.
    //

    struct String_t : public std::string {
        String_t() = default;

        String_t(const char* S) : std::string(S) {}

        String_t(const std::string& S) : std::string(S) {}

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}String_t(\"", " ", Spaces);
            size_t Counter = 0;
            for (const auto& Byte : *this) {
                fmt::print("{}", Byte);
                if (Counter++ > 64) {
                    fmt::print("...");
                    break;
                }
            }
            fmt::print("\")\n");
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            auto Size = uint32_t(this->size());
            if (Size == 0) {
                Size = 0xff'ff'ff'ff;
            }

            Uint32_t(Size).SerializeInto(Buffer);
            Buffer.insert(Buffer.end(), this->data(), this->data() + this->size());
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            uint32_t Size = Buffer.Read32();
            if (Size == 0xff'ff'ff'ff) {
                Size = 0;
            }

            this->resize(Size);
            Buffer.Read((uint8_t*)this->data(), Size);
        }

        static String_t Make(Rng_t& Rng) {
            const std::vector<char> Printables = {
                '0',  '1',  '2',    '3',    '4',   '5', '6', '7', '8',  '9', 'a', 'b',
                'c',  'd',  'e',    'f',    'g',   'h', 'i', 'j', 'k',  'l', 'm', 'n',
                'o',  'p',  'q',    'r',    's',   't', 'u', 'v', 'w',  'x', 'y', 'z',
                'A',  'B',  'C',    'D',    'E',   'F', 'G', 'H', 'I',  'J', 'K', 'L',
                'M',  'N',  'O',    'P',    'Q',   'R', 'S', 'T', 'U',  'V', 'W', 'X',
                'Y',  'Z',  '!',    '"',    '#',   '$', '%', '&', '\'', '(', ')', '*',
                '+',  ',',  '-',    '.',    '/',   ':', ';', '<', '=',  '>', '?', '@',
                '[',  '\'', ']',    '^',    '_',   '`', '{', '|', '}',  '~', ' ', '\t',
                '\n', '\r', '\x0b', '\x0c', '\x00' };
            const size_t Sz = Rng.Int(50);
            String_t String;
            for (size_t Idx = 0; Idx < Sz; Idx++) {
                String.push_back(Rng.Pick(Printables));
            }
            return String;
        }

        static String_t MakeUri(Rng_t& Rng) {
            const std::vector<const char*> Schema = { "opc.tcp", "opc.udp", "opc.da" };
            const auto& Uri =
                fmt::format("{}://{}:{}", Rng.Pick(Schema), String_t::Make(Rng), 31337);
            return String_t(Uri);
        }

        static String_t MakeLocale(Rng_t& Rng) {
            const std::vector<const char*> Locales = {
                "af-ZA",
                "am-ET",
                "ar-AE",
                "ar-BH",
                "ar-DZ",
                "ar-EG",
                "ar-IQ",
                "ar-JO",
                "ar-KW",
                "ar-LB",
                "ar-LY",
                "ar-MA",
                "arn-CL",
                "ar-OM",
                "ar-QA",
                "ar-SA",
                "ar-SD",
                "ar-SY",
                "ar-TN",
                "ar-YE",
                "as-IN",
                "az-az",
                "az-Cyrl-AZ",
                "az-Latn-AZ",
                "ba-RU",
                "be-BY",
                "bg-BG",
                "bn-BD",
                "bn-IN",
                "bo-CN",
                "br-FR",
                "bs-Cyrl-BA",
                "bs-Latn-BA",
                "ca-ES",
                "co-FR",
                "cs-CZ",
                "cy-GB",
                "da-DK",
                "de-AT",
                "de-CH",
                "de-DE",
                "de-LI",
                "de-LU",
                "dsb-DE",
                "dv-MV",
                "el-CY",
                "el-GR",
                "en-029",
                "en-AU",
                "en-BZ",
                "en-CA",
                "en-cb",
                "en-GB",
                "en-IE",
                "en-IN",
                "en-JM",
                "en-MT",
                "en-MY",
                "en-NZ",
                "en-PH",
                "en-SG",
                "en-TT",
                "en-US",
                "en-ZA",
                "en-ZW",
                "es-AR",
                "es-BO",
                "es-CL",
                "es-CO",
                "es-CR",
                "es-DO",
                "es-EC",
                "es-ES",
                "es-GT",
                "es-HN",
                "es-MX",
                "es-NI",
                "es-PA",
                "es-PE",
                "es-PR",
                "es-PY",
                "es-SV",
                "es-US",
                "es-UY",
                "es-VE",
                "et-EE",
                "eu-ES",
                "fa-IR",
                "fi-FI",
                "fil-PH"
                "fo-FO",
                "fr-BE",
                "fr-CA",
                "fr-CH",
                "fr-FR",
                "fr-LU",
                "fr-MC",
                "fy-NL",
                "ga-IE",
                "gd-GB",
                "gd-ie",
                "gl-ES",
                "gsw-FR",
                "gu-IN",
                "ha-Latn-NG",
                "he-IL",
                "hi-IN",
                "hr-BA",
                "hr-HR",
                "hsb-DE",
                "hu-HU",
                "hy-AM",
                "id-ID",
                "ig-NG",
                "ii-CN",
                "in-ID",
                "is-IS",
                "it-CH",
                "it-IT",
                "iu-Cans-CA",
                "iu-Latn-CA",
                "iw-IL",
                "ja-JP",
                "ka-GE",
                "kk-KZ",
                "kl-GL",
                "km-KH",
                "kn-IN",
                "kok-IN",
                "ko-KR",
                "ky-KG",
                "lb-LU",
                "lo-LA",
                "lt-LT",
                "lv-LV",
                "mi-NZ",
                "mk-MK",
                "ml-IN",
                "mn-MN",
                "mn-Mong-CN",
                "moh-CA",
                "mr-IN",
                "ms-BN",
                "ms-MY",
                "mt-MT",
                "nb-NO",
                "ne-NP",
                "nl-BE",
                "nl-NL",
                "nn-NO",
                "no-no",
                "nso-ZA"
                "oc-FR",
                "or-IN",
                "pa-IN",
                "pl-PL",
                "prs-AF",
                "ps-AF",
                "pt-BR",
                "pt-PT",
                "qut-GT",
                "quz-BO",
                "quz-EC",
                "quz-PE",
                "rm-CH",
                "ro-mo",
                "ro-RO",
                "ru-mo",
                "ru-RU",
                "rw-RW",
                "sah-RU",
                "sa-IN",
                "se-FI",
                "se-NO",
                "se-SE",
                "si-LK",
                "sk-SK",
                "sl-SI",
                "sma-NO",
                "sma-SE",
                "smj-NO",
                "smj-SE",
                "smn-FI",
                "sms-FI",
                "sq-AL",
                "sr-BA",
                "sr-CS",
                "sr-Cyrl-BA",
                "sr-Cyrl-CS",
                "sr-Cyrl-ME",
                "sr-Cyrl-RS",
                "sr-Latn-BA",
                "sr-Latn-CS",
                "sr-Latn-ME",
                "sr-Latn-RS",
                "sr-ME",
                "sr-RS",
                "sr-sp",
                "sv-FI",
                "sv-SE",
                "sw-KE",
                "syr-SY",
                "ta-IN",
                "te-IN",
                "tg-Cyrl-TJ",
                "th-TH",
                "tk-TM",
                "tlh-QS",
                "tn-ZA",
                "tr-TR",
                "tt-RU",
                "tzm-Latn-DZ",
                "ug-CN",
                "uk-UA",
                "ur-PK",
                "uz-Cyrl-UZ",
                "uz-Latn-UZ",
                "uz-uz",
                "vi-VN",
                "wo-SN",
                "xh-ZA",
                "yo-NG",
                "zh-CN",
                "zh-HK",
                "zh-MO",
                "zh-SG",
                "zh-TW",
                "zu-ZA",
            };
            return String_t(Rng.Pick(Locales));
        }
    };

    struct ByteString_t : public std::string {
        ByteString_t() = default;

        ByteString_t(const char* S) : std::string(S) {}

        ByteString_t(const char* S, size_t Length) : std::string(S, Length) {}

        ByteString_t(const uint8_t* S, size_t Length)
            : std::string((char*)S, Length) {}

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}ByteString_t(\"", " ", Spaces);
            size_t Counter = 0;
            for (const auto& Byte : *this) {
                fmt::print("{:02x}", uint8_t(Byte));
                if (Counter++ > 64) {
                    fmt::print("...");
                    break;
                }
            }
            fmt::print("\")\n");
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            auto Size = uint32_t(this->size());
            if (Size == 0) {
                Size = 0xff'ff'ff'ff;
            }

            Uint32_t(Size).SerializeInto(Buffer);
            Buffer.insert(Buffer.end(), this->data(), this->data() + this->size());
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            uint32_t Size = Buffer.Read32();
            if (Size == 0xff'ff'ff'ff) {
                Size = 0;
            }

            this->resize(Size);
            Buffer.Read((uint8_t*)this->data(), Size);
        }

        static ByteString_t Make(Rng_t& Rng) {
            ByteString_t ByteString;
            const size_t Size = Rng.Int(1, 10);
            ByteString.resize(Size);
            for (size_t N = 0; N < 10; N++) {
                const size_t BitIdx = Rng.Int((Size * 8) - 1);
                ByteString[BitIdx / 8] = 1 << (BitIdx % 8);
            }
            return ByteString;
        }
    };

    struct XmlElement_t {
        ByteString_t Value;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}XmlElement({})\n", " ", Spaces, Value);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Value.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            Value.DeserializeFrom(Buffer);
        }
    };

    struct NodeId_t {
        Uint16_t NamespaceIndex;
        std::variant<Uint32_t, String_t, ByteString_t, Guid_t> Identifier =
            Uint32_t();

        NodeId_t() = default;

        NodeId_t(const char* Id) : NamespaceIndex(0), Identifier(String_t(Id)) {}

        NodeId_t(const uint32_t Id) : NamespaceIndex(0), Identifier(Id) {}

        NodeId_t(const uint16_t Namespace, const uint32_t Id)
            : NamespaceIndex(Namespace), Identifier(Id) {}

        NodeId_t(const std::string& Id) : NamespaceIndex(0), Identifier(Id) {}

        NodeId_t(const uint16_t Namespace, const std::string& Id)
            : NamespaceIndex(Namespace), Identifier(Id) {}

        NodeId_t(const uint16_t Namespace, const Guid_t& Id)
            : NamespaceIndex(Namespace), Identifier(Id) {}

        bool operator<(const NodeId_t& Other) const {
            if (NamespaceIndex != Other.NamespaceIndex) {
                return NamespaceIndex < Other.NamespaceIndex;
            }

            if (Is<Uint32_t>() && Other.Is<Uint32_t>()) {
                return As<Uint32_t>() < Other.As<Uint32_t>();
            }

            fmt::print("NodeId_t::< not implemented for that combination");
            throw std::runtime_error("");
        }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}NodeId_t\n", " ", Spaces);
            fmt::print("{:{}}  Namespace:", " ", Spaces);
            NamespaceIndex.Print(1);
            fmt::print("{:{}}  Identifier:", " ", Spaces);
            std::visit([&](auto&& V) { V.Print(1); }, Identifier);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            if (Is<Uint32_t>()) {
                if (FitsInTwoBytes()) {
                    Uint8_t(0).SerializeInto(Buffer);
                    Uint8_t(As<Uint32_t>()).SerializeInto(Buffer);
                }
                else if (FitsInFourBytes()) {
                    Uint8_t(1).SerializeInto(Buffer);
                    Uint8_t(NamespaceIndex.Value).SerializeInto(Buffer);
                    Uint16_t(As<Uint32_t>()).SerializeInto(Buffer);
                }
                else {
                    Uint8_t(2).SerializeInto(Buffer);
                    Uint16_t(NamespaceIndex.Value).SerializeInto(Buffer);
                    As<Uint32_t>().SerializeInto(Buffer);
                }
            }
            else if (Is<String_t>()) {
                Uint8_t(3).SerializeInto(Buffer);
                Uint16_t(NamespaceIndex.Value).SerializeInto(Buffer);
                As<String_t>().SerializeInto(Buffer);
            }
            else if (Is<Guid_t>()) {
                Uint8_t(4).SerializeInto(Buffer);
                Uint16_t(NamespaceIndex.Value).SerializeInto(Buffer);
                As<Guid_t>().SerializeInto(Buffer);
            }
            else if (Is<ByteString_t>()) {
                Uint8_t(5).SerializeInto(Buffer);
                Uint16_t(NamespaceIndex.Value).SerializeInto(Buffer);
                As<ByteString_t>().SerializeInto(Buffer);
            }
            else {
                throw std::runtime_error("cannot encode node id");
            }
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            const uint8_t Encoding = Buffer.Read8();
            switch (Encoding) {
            case 0: {
                Identifier = Uint32_t(Buffer.Read8());
                return;
            }

            case 1: {
                NamespaceIndex = Uint16_t(Buffer.Read8());
                Identifier = Uint32_t(Buffer.Read16());
                return;
            }

            case 2: {
                NamespaceIndex = Uint16_t(Buffer.Read16());
                Identifier = Uint32_t(Buffer.Read32());
                return;
            }

            case 3: {
                NamespaceIndex = Uint16_t(Buffer.Read16());
                String_t String;
                String.DeserializeFrom(Buffer);
                Identifier = String;
                return;
            }

            case 4: {
                NamespaceIndex = Uint16_t(Buffer.Read16());
                Guid_t Guid;
                Guid.DeserializeFrom(Buffer);
                Identifier = Guid;
                return;
            }

            case 5: {
                NamespaceIndex = Uint16_t(Buffer.Read16());
                ByteString_t ByteString;
                ByteString.DeserializeFrom(Buffer);
                Identifier = ByteString;
                return;
            }
            }
            throw std::runtime_error("cannot decode node id");
        }

        template <typename Type_t> bool Is() const {
            return std::holds_alternative<Type_t>(Identifier);
        }

        template <typename Type_t> const Type_t& As() const {
            return std::get<Type_t>(Identifier);
        }

    private:
        bool FitsInTwoBytes() const {
            if (!Is<Uint32_t>()) {
                return false;
            }

            return NamespaceIndex.Value == 0 && As<Uint32_t>() < 0x1'00;
        }

        bool FitsInFourBytes() const {
            if (!Is<Uint32_t>()) {
                return false;
            }

            const auto& Uint32 = As<Uint32_t>();
            return NamespaceIndex.Value < 0x1'00 && Uint32 < 0x1'00'00;
        }
    };

    struct Serializable_t {
        Serializable_t() = default;

        template <typename Msg_t>
        Serializable_t(Msg_t Msg)
            : p_(std::make_unique<Model_t<Msg_t>>(std::move(Msg))) {}

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            p_->SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) { p_->DeserializeFrom(Buffer); }

        NodeId_t Id() const { return p_->Id(); }

        void Print(const size_t Spaces = 2) const { p_->Print(Spaces); }

    private:
        struct Concept_t {
            virtual ~Concept_t() = default;
            virtual void SerializeInto(std::vector<uint8_t>& Buffer) const = 0;
            virtual void DeserializeFrom(Deserializer_t& Buffer) = 0;
            virtual void Print(const size_t Spaces = 2) const = 0;
            virtual NodeId_t Id() const = 0;
        };

        template <typename Msg_t> struct Model_t : public Concept_t {
            Msg_t msg_;

            Model_t(Msg_t&& m) : msg_(std::move(m)) {}

            void SerializeInto(std::vector<uint8_t>& Buffer) const override {
                msg_.SerializeInto(Buffer);
            }

            void DeserializeFrom(Deserializer_t& Buffer) {
                msg_.DeserializeFrom(Buffer);
            }

            void Print(const size_t Spaces = 2) const override { msg_.Print(Spaces); }

            NodeId_t Id() const override { return Msg_t::Id(); }
        };

        std::unique_ptr<Concept_t> p_;
    };

    struct SerializableBuilder_t {
        template <typename Msg_t>
        SerializableBuilder_t(Msg_t Msg) : p_(std::make_unique<Model_t<Msg_t>>()) {}

        Serializable_t New() const { return p_->New(); }

    private:
        struct Concept_t {
            virtual ~Concept_t() = default;
            virtual Serializable_t New() const = 0;
        };

        template <typename Msg_t> struct Model_t : public Concept_t {
            Serializable_t New() const override { return Serializable_t(Msg_t()); }
        };

        std::unique_ptr<Concept_t> p_;
    };

    struct ExpandedNodeId_t {
        Uint16_t NamespaceIndex;
        std::variant<Uint32_t, String_t, ByteString_t, Guid_t> Identifier =
            Uint32_t();
        std::optional<String_t> NamespaceUri;
        std::optional<Uint32_t> ServerIndex;

        ExpandedNodeId_t() = default;

        ExpandedNodeId_t(const uint32_t Id)
            : NamespaceIndex(0), Identifier(Uint32_t(Id)) {}

        ExpandedNodeId_t(const uint16_t Namespace, const uint32_t Id)
            : NamespaceIndex(Namespace), Identifier(Uint32_t(Id)) {}

        ExpandedNodeId_t(const char* Id)
            : NamespaceIndex(0), Identifier(String_t(Id)) {}

        ExpandedNodeId_t(const uint16_t Namespace, const std::string& Id)
            : NamespaceIndex(Namespace), Identifier(String_t(Id)) {}

        ExpandedNodeId_t(const uint16_t Namespace, const Guid_t& Id)
            : NamespaceIndex(Namespace), Identifier(Guid_t(Id)) {}

        ExpandedNodeId_t(const NodeId_t& NodeId)
            : NamespaceIndex(NodeId.NamespaceIndex), Identifier(NodeId.Identifier) {}

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}ExpandedNodeId_t\n", " ", Spaces);
            fmt::print("{:{}}  Namespace:", " ", Spaces);
            NamespaceIndex.Print(1);
            fmt::print("{:{}}  Identifier:", " ", Spaces);
            std::visit([&](auto&& V) { V.Print(1); }, Identifier);
            if (NamespaceUri) {
                fmt::print("{:{}}  NamespaceUri:", " ", Spaces);
                NamespaceUri->Print(1);
            }

            if (ServerIndex) {
                fmt::print("{:{}}  ServerIndex:", " ", Spaces);
                ServerIndex->Print(1);
            }
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            uint8_t Encoding = 0;
            if (NamespaceUri) {
                Encoding |= 0x80;
            }

            if (ServerIndex) {
                Encoding |= 0x40;
            }

            if (const auto& IntIdentifier = IdentifierAs<Uint32_t>()) {
                if (FitsInTwoBytes()) {
                    Uint8_t(Encoding | 0).SerializeInto(Buffer);
                    Uint8_t(IntIdentifier->Value).SerializeInto(Buffer);
                }
                else if (FitsInFourBytes()) {
                    Uint8_t(Encoding | 1).SerializeInto(Buffer);
                    Uint8_t(NamespaceIndex.Value).SerializeInto(Buffer);
                    Uint16_t(IntIdentifier->Value).SerializeInto(Buffer);
                }
                else {
                    Uint8_t(Encoding | 2).SerializeInto(Buffer);
                    Uint16_t(NamespaceIndex.Value).SerializeInto(Buffer);
                    IntIdentifier->SerializeInto(Buffer);
                }
            }
            else if (const auto& StringIdentifier = IdentifierAs<String_t>()) {
                Uint8_t(Encoding | 3).SerializeInto(Buffer);
                Uint16_t(NamespaceIndex.Value).SerializeInto(Buffer);
                StringIdentifier->SerializeInto(Buffer);
            }
            else if (const auto& GuidIdentifier = IdentifierAs<Guid_t>()) {
                Uint8_t(Encoding | 4).SerializeInto(Buffer);
                Uint16_t(NamespaceIndex.Value).SerializeInto(Buffer);
                GuidIdentifier->SerializeInto(Buffer);
            }
            else if (const auto& ByteStringIdentifier =
                IdentifierAs<ByteString_t>()) {
                Uint8_t(Encoding | 5).SerializeInto(Buffer);
                Uint16_t(NamespaceIndex.Value).SerializeInto(Buffer);
                ByteStringIdentifier->SerializeInto(Buffer);
            }
            else {
                throw std::runtime_error("cannot encode node id");
            }

            if (NamespaceUri) {
                NamespaceUri->SerializeInto(Buffer);
            }

            if (ServerIndex) {
                ServerIndex->SerializeInto(Buffer);
            }
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            const uint8_t Encoding = Buffer.Read8();
            switch (Encoding & 0b1111) {
            case 0: {
                Identifier = Uint32_t(Buffer.Read8());
                return;
            }

            case 1: {
                NamespaceIndex = Uint16_t(Buffer.Read8());
                Identifier = Uint32_t(Buffer.Read16());
                return;
            }

            case 2: {
                NamespaceIndex = Uint16_t(Buffer.Read16());
                Identifier = Uint32_t(Buffer.Read32());
                return;
            }

            case 3: {
                NamespaceIndex = Uint16_t(Buffer.Read16());
                String_t String;
                String.DeserializeFrom(Buffer);
                Identifier = String;
                return;
            }

            case 4: {
                NamespaceIndex = Uint16_t(Buffer.Read16());
                Guid_t Guid;
                Guid.DeserializeFrom(Buffer);
                Identifier = Guid;
                return;
            }

            case 5: {
                NamespaceIndex = Uint16_t(Buffer.Read16());
                ByteString_t ByteString;
                ByteString.DeserializeFrom(Buffer);
                Identifier = ByteString;
                return;
            }

            default: {
                throw std::runtime_error("cannot decode node id");
            }
            }

            if (Encoding & 0b1000'0000) {
                NamespaceUri->DeserializeFrom(Buffer);
            }

            if (Encoding & 0b0100'0000) {
                ServerIndex->DeserializeFrom(Buffer);
            }
        }

        template <typename Type_t> bool IdentifierIs() const {
            return std::holds_alternative<Type_t>(Identifier);
        }

        template <typename Type_t> std::optional<Type_t> IdentifierAs() const {
            if (IdentifierIs<Type_t>()) {
                return std::get<Type_t>(Identifier);
            }

            return {};
        }

    private:
        bool FitsInTwoBytes() const {
            const auto& IdentifierAsInt = IdentifierAs<Uint32_t>();
            return IdentifierAsInt && NamespaceIndex.Value == 0 &&
                IdentifierAsInt->Value < 0x1'00;
        }

        bool FitsInFourBytes() const {
            const auto& IdentifierAsInt = IdentifierAs<Uint32_t>();
            return IdentifierAsInt && NamespaceIndex.Value < 0x1'00 &&
                IdentifierAsInt->Value < 0x1'00'00;
        }
    };

    struct AnonymousIdentityToken_t {
        String_t PolicyId;

        static const NodeId_t Id() { return KnownNodeIds_t::AnonymousIdentityToken; }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}AnonymousIdentityToken({})\n", " ", Spaces, PolicyId);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            PolicyId.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            PolicyId.DeserializeFrom(Buffer);
        }
    };

    struct LocalizedText_t {
        std::optional<String_t> Locale;
        std::optional<String_t> Text;

        LocalizedText_t() = default;

        LocalizedText_t(const char* Text_) : Text(String_t(Text_)) {}

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}LocalizedText_t(", " ", Spaces);
            if (Locale) {
                fmt::print("\"{}\"", " ", Spaces, *Locale);
            }

            if (Text) {
                fmt::print(", \"{}\")\n", " ", Spaces, *Text);
            }
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            uint8_t Encoding = 0;
            if (Locale) {
                Encoding |= 0b1;
            }

            if (Text) {
                Encoding |= 0b10;
            }

            Uint8_t(Encoding).SerializeInto(Buffer);
            if (Locale) {
                Locale->SerializeInto(Buffer);
            }

            if (Text) {
                Text->SerializeInto(Buffer);
            }
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            const uint8_t Encoding = Buffer.Read8();
            if (Encoding & 0b1) {
                String_t String;
                String.DeserializeFrom(Buffer);
                Locale = String;
            }

            if (Encoding & 0b10) {
                String_t String;
                String.DeserializeFrom(Buffer);
                Text = String;
            }
        }
    };

    struct QualifiedName_t {
        Uint16_t Namespace;
        String_t Name;

        QualifiedName_t() = default;
        QualifiedName_t(const char* _Name) : Name(_Name) {}
        QualifiedName_t(const std::string& _Name) : Name(_Name) {}

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}QualifiedName_t\n", " ", Spaces);
            fmt::print("{:{}}  Namespace:", " ", Spaces);
            Namespace.Print(1);
            fmt::print("{:{}}  Name:", " ", Spaces);
            Name.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Namespace.SerializeInto(Buffer);
            Name.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            Namespace.DeserializeFrom(Buffer);
            Name.DeserializeFrom(Buffer);
        }
    };

    template <typename Type_t> struct Array_t : public std::vector<Type_t> {
        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}Array_t\n", " ", Spaces);
            size_t Idx = 0;
            for (const auto& Item : *this) {
                fmt::print("{:{}}  [{}]:\n", " ", Spaces, Idx++);
                Item.Print(Spaces + 4);
            }
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Int32_t(int32_t(this->size())).SerializeInto(Buffer);
            for (const auto& Item : *this) {
                Item.SerializeInto(Buffer);
            }
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            const auto Size = int32_t(Buffer.Read32());
            if (Size < 0) {
                return;
            }

            for (uint32_t i = 0; i < uint32_t(Size); i++) {
                Type_t Type;
                Type.DeserializeFrom(Buffer);
                this->push_back(std::move(Type));
            }
        }
    };

    static Serializable_t BuildEncodeableType(const NodeId_t Id);

    struct ExtensionObject_t {
        std::variant<None_t, XmlElement_t, Serializable_t> Body = None_t();

        ExtensionObject_t() = default;

        ExtensionObject_t(XmlElement_t XmlElement) : Body(std::move(XmlElement)) {}

        ExtensionObject_t(Serializable_t Serializable)
            : Body(std::move(Serializable)) {}

        template <typename Type_t> bool Is() const {
            return std::holds_alternative<Type_t>(Body);
        }

        template <typename Type_t> const Type_t& As() const {
            return std::get<Type_t>(Body);
        }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}ExtensionObject_t\n", " ", Spaces);
            fmt::print("{:{}}  Body:\n", " ", Spaces);
            std::visit([&](auto&& V) { V.Print(Spaces + 4); }, Body);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            if (Is<None_t>()) {
                NodeId_t().SerializeInto(Buffer);
                Uint8_t(0).SerializeInto(Buffer);
            }
            else if (Is<XmlElement_t>()) {
                NodeId_t().SerializeInto(Buffer);
                Uint8_t(2).SerializeInto(Buffer);
                As<XmlElement_t>().SerializeInto(Buffer);
            }
            else if (Is<Serializable_t>()) {
                const auto& Serializable = As<Serializable_t>();
                Serializable.Id().SerializeInto(Buffer);
                Uint8_t(1).SerializeInto(Buffer);
                const size_t SizeBefore = Buffer.size();
                Buffer.resize(SizeBefore + 4);
                Serializable.SerializeInto(Buffer);
                const size_t SizeAfter = Buffer.size();
                const size_t Size = SizeAfter - SizeBefore;
                const auto SizeU32 = uint32_t(Size) - 4;
                std::memcpy(Buffer.data() + SizeBefore, &SizeU32, sizeof(SizeU32));
            }
            else {
                fmt::print("Unknown body in ExtensionObject_t\n");
                std::abort();
            }
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            NodeId_t NodeId;
            NodeId.DeserializeFrom(Buffer);
            const uint8_t Encoding = Buffer.Read8();
            if (Encoding == 0) {
                Body = None_t();
            }
            else if (Encoding == 1) {
                XmlElement_t XmlElement;
                XmlElement.DeserializeFrom(Buffer);
                Body = XmlElement;
            }
            else if (Encoding == 2) {
                const uint32_t ExpectedSize = Buffer.Read32();
                const size_t Before = Buffer.Offset();
                Serializable_t Serializable = BuildEncodeableType(NodeId);
                Serializable.DeserializeFrom(Buffer);
                Body = std::move(Serializable);
                const size_t After = Buffer.Offset();
                const size_t Size = After - Before;
                if (uint32_t(Size) != ExpectedSize) {
                    throw std::runtime_error("Size mismatch");
                }
            }
            else {
                throw std::runtime_error("Unknown encoding");
            }
        }
    };

    struct Variant_t {
        // XXX: DataValue_t, sbyte, byte, etc
        // XXX: Arrays
        std::variant<None_t, Uint8_t, Uint16_t, Uint32_t, Uint64_t, Float_t, Double_t,
            String_t, DateTime_t, Guid_t, ByteString_t, XmlElement_t,
            NodeId_t, ExpandedNodeId_t, QualifiedName_t, LocalizedText_t,
            ExtensionObject_t>
            Value = None_t();

        Variant_t() = default;

        Variant_t(const uint8_t& Uint8) : Value(Uint8_t(Uint8)) {}

        Variant_t(const uint16_t& Uint16) : Value(Uint16_t(Uint16)) {}

        Variant_t(const uint32_t& Uint32) : Value(Uint32_t(Uint32)) {}

        Variant_t(const uint64_t& Uint64) : Value(Uint64_t(Uint64)) {}

        Variant_t(const float& Float) : Value(Float_t(Float)) {}

        Variant_t(const double& Double) : Value(Double_t(Double)) {}

        Variant_t(const DateTime_t& Datetime) : Value(Datetime) {}

        Variant_t(const Guid_t& Guid) : Value(Guid) {}

        Variant_t(const char* String) : Value(String_t(String)) {}

        Variant_t(const String_t& String) : Value(String) {}

        Variant_t(const ByteString_t& Bytestring) : Value(Bytestring) {}

        Variant_t(const XmlElement_t& XmlElement) : Value(XmlElement) {}

        Variant_t(const NodeId_t& NodeId) : Value(NodeId) {}

        Variant_t(const ExpandedNodeId_t& ExpandedNodeId) : Value(ExpandedNodeId) {}

        Variant_t(const QualifiedName_t& QualifiedName) : Value(QualifiedName) {}

        Variant_t(const LocalizedText_t& LocalizedText) : Value(LocalizedText) {}

        Variant_t(ExtensionObject_t ExtensionObject)
            : Value(std::move(ExtensionObject)) {}

        template <typename Type_t> bool Is() const {
            return std::holds_alternative<Type_t>(Value);
        }

        template <typename Type_t> Type_t& As() { return std::get<Type_t>(Value); }

        template <typename Type_t> const Type_t& As() const {
            return std::get<Type_t>(Value);
        }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}Variant_t\n", " ", Spaces);
            std::visit([&](auto&& V) { V.Print(Spaces + 4); }, Value);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            if (Is<None_t>()) {
                Uint8_t(0).SerializeInto(Buffer);
            }
            else if (Is<Uint8_t>()) {
                Uint8_t(3).SerializeInto(Buffer);
                As<Uint8_t>().SerializeInto(Buffer);
            }
            else if (Is<Uint16_t>()) {
                Uint8_t(5).SerializeInto(Buffer);
                As<Uint16_t>().SerializeInto(Buffer);
            }
            else if (Is<Uint32_t>()) {
                Uint8_t(7).SerializeInto(Buffer);
                As<Uint32_t>().SerializeInto(Buffer);
            }
            else if (Is<Uint64_t>()) {
                Uint8_t(9).SerializeInto(Buffer);
                As<Uint64_t>().SerializeInto(Buffer);
            }
            else if (Is<Float_t>()) {
                Uint8_t(0xa).SerializeInto(Buffer);
                As<Float_t>().SerializeInto(Buffer);
            }
            else if (Is<Double_t>()) {
                Uint8_t(0xb).SerializeInto(Buffer);
                As<Double_t>().SerializeInto(Buffer);
            }
            else if (Is<String_t>()) {
                Uint8_t(0xc).SerializeInto(Buffer);
                As<String_t>().SerializeInto(Buffer);
            }
            else if (Is<DateTime_t>()) {
                Uint8_t(0xd).SerializeInto(Buffer);
                As<DateTime_t>().SerializeInto(Buffer);
            }
            else if (Is<Guid_t>()) {
                Uint8_t(0xe).SerializeInto(Buffer);
                As<Guid_t>().SerializeInto(Buffer);
            }
            else if (Is<ByteString_t>()) {
                Uint8_t(0xf).SerializeInto(Buffer);
                As<ByteString_t>().SerializeInto(Buffer);
            }
            else if (Is<XmlElement_t>()) {
                Uint8_t(0x10).SerializeInto(Buffer);
                As<XmlElement_t>();
            }
            else if (Is<NodeId_t>()) {
                Uint8_t(0x11).SerializeInto(Buffer);
                As<NodeId_t>().SerializeInto(Buffer);
            }
            else if (Is<ExpandedNodeId_t>()) {
                Uint8_t(0x12).SerializeInto(Buffer);
                As<ExpandedNodeId_t>().SerializeInto(Buffer);
            }
            else if (Is<QualifiedName_t>()) {
                Uint8_t(0x14).SerializeInto(Buffer);
                As<QualifiedName_t>().SerializeInto(Buffer);
            }
            else if (Is<LocalizedText_t>()) {
                Uint8_t(0x15).SerializeInto(Buffer);
                As<LocalizedText_t>().SerializeInto(Buffer);
            }
            else if (Is<ExtensionObject_t>()) {
                Uint8_t(0x16).SerializeInto(Buffer);
                As<ExtensionObject_t>().SerializeInto(Buffer);
            }
            else {
                throw std::runtime_error("unknown type in variant");
            }
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            const uint8_t Encoding = Buffer.Read8();
            switch (Encoding) {
            case 0: {
                Value = None_t();
                break;
            }

            case 3: {
                Uint8_t Uint8;
                Uint8.DeserializeFrom(Buffer);
                Value = Uint8;
                break;
            }

            case 5: {
                Uint16_t Uint16;
                Uint16.DeserializeFrom(Buffer);
                Value = Uint16;
                break;
            }

            case 7: {
                Uint32_t Uint32;
                Uint32.DeserializeFrom(Buffer);
                Value = Uint32;
                break;
            }

            case 9: {
                Uint64_t Uint64;
                Uint64.DeserializeFrom(Buffer);
                Value = Uint64;
                break;
            }

            case 0xa: {
                Float_t Float;
                Float.DeserializeFrom(Buffer);
                Value = Float;
                break;
            }

            case 0xb: {
                Double_t Double;
                Double.DeserializeFrom(Buffer);
                Value = Double;
                break;
            }

            case 0xc: {
                String_t String;
                String.DeserializeFrom(Buffer);
                Value = String;
                break;
            }

            case 0xd: {
                DateTime_t Datetime;
                Datetime.DeserializeFrom(Buffer);
                Value = Datetime;
                break;
            }

            case 0xe: {
                Guid_t Guid;
                Guid.DeserializeFrom(Buffer);
                Value = Guid;
                break;
            }

            case 0xf: {
                ByteString_t ByteString;
                ByteString.DeserializeFrom(Buffer);
                Value = ByteString;
                break;
            }

            case 0x10: {
                XmlElement_t XmlElement;
                XmlElement.DeserializeFrom(Buffer);
                Value = XmlElement;
                break;
            }

            case 0x11: {
                NodeId_t NodeId;
                NodeId.DeserializeFrom(Buffer);
                Value = NodeId;
                break;
            }

            case 0x12: {
                ExpandedNodeId_t ExpandedNodeId;
                ExpandedNodeId.DeserializeFrom(Buffer);
                Value = ExpandedNodeId;
                break;
            }

            case 0x14: {
                QualifiedName_t QualifiedName;
                QualifiedName.DeserializeFrom(Buffer);
                Value = QualifiedName;
                break;
            }

            case 0x15: {
                LocalizedText_t LocalizedText;
                LocalizedText.DeserializeFrom(Buffer);
                Value = LocalizedText;
                break;
            }

            case 0x16: {
                ExtensionObject_t ExtensionObject;
                ExtensionObject.DeserializeFrom(Buffer);
                Value = std::move(ExtensionObject);
                break;
            }

            default: {
                fmt::print("unknown type in variant deserialization\n");
                throw std::runtime_error("unknown type in variant");
            }
            }
        }
    };

    struct NodeAttributes_t {
        Uint32_t SpecifiedAttributes;
        LocalizedText_t DisplayName;
        LocalizedText_t Description;
        Uint32_t WriteMask;
        Uint32_t UserWriteMask;

        static const NodeId_t Id() { return KnownNodeIds_t::NodeAttributes; }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}NodeAttributes_t\n", " ", Spaces);
            fmt::print("{:{}}  SpecifiedAttributes:", " ", Spaces);
            SpecifiedAttributes.Print(1);
            fmt::print("{:{}}  DisplayName:", " ", Spaces);
            DisplayName.Print(1);
            fmt::print("{:{}}  Description:", " ", Spaces);
            Description.Print(1);
            fmt::print("{:{}}  WriteMask:", " ", Spaces);
            WriteMask.Print(1);
            fmt::print("{:{}}  UserWriteMask:", " ", Spaces);
            UserWriteMask.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            SpecifiedAttributes.SerializeInto(Buffer);
            DisplayName.SerializeInto(Buffer);
            Description.SerializeInto(Buffer);
            WriteMask.SerializeInto(Buffer);
            UserWriteMask.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            SpecifiedAttributes.DeserializeFrom(Buffer);
            DisplayName.DeserializeFrom(Buffer);
            Description.DeserializeFrom(Buffer);
            WriteMask.DeserializeFrom(Buffer);
            UserWriteMask.DeserializeFrom(Buffer);
        }
    };

    struct ObjectAttributes_t {
        Uint32_t SpecifiedAttributes;
        LocalizedText_t DisplayName;
        LocalizedText_t Description;
        Uint32_t WriteMask;
        Uint32_t UserWriteMask;
        Uint8_t EventNotifier;

        static const NodeId_t Id() { return KnownNodeIds_t::ObjectAttributes; }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}ObjectAttributes_t\n", " ", Spaces);
            fmt::print("{:{}}  SpecifiedAttributes:", " ", Spaces);
            SpecifiedAttributes.Print(1);
            fmt::print("{:{}}  DisplayName:", " ", Spaces);
            DisplayName.Print(1);
            fmt::print("{:{}}  Description:", " ", Spaces);
            Description.Print(1);
            fmt::print("{:{}}  WriteMask:", " ", Spaces);
            WriteMask.Print(1);
            fmt::print("{:{}}  UserWriteMask:", " ", Spaces);
            UserWriteMask.Print(1);
            fmt::print("{:{}}  EventNotifier:", " ", Spaces);
            EventNotifier.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            SpecifiedAttributes.SerializeInto(Buffer);
            DisplayName.SerializeInto(Buffer);
            Description.SerializeInto(Buffer);
            WriteMask.SerializeInto(Buffer);
            UserWriteMask.SerializeInto(Buffer);
            EventNotifier.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            SpecifiedAttributes.DeserializeFrom(Buffer);
            DisplayName.DeserializeFrom(Buffer);
            Description.DeserializeFrom(Buffer);
            WriteMask.DeserializeFrom(Buffer);
            UserWriteMask.DeserializeFrom(Buffer);
            EventNotifier.DeserializeFrom(Buffer);
        }
    };

    struct VariableAttributes_t {
        Uint32_t SpecifiedAttributes;
        LocalizedText_t DisplayName;
        LocalizedText_t Description;
        Uint32_t WriteMask;
        Uint32_t UserWriteMask;
        Variant_t Value;
        NodeId_t DataType;
        Uint32_t ValueRank;
        Array_t<Uint32_t> ArrayDimensions;
        Uint8_t AccessLevel;
        Uint8_t UserAccessLevel;
        Double_t MinimumSamplingInterval;
        Uint8_t Historizing;

        static const NodeId_t Id() { return KnownNodeIds_t::VariableAttributes; }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}VariableAttributes_t\n", " ", Spaces);
            fmt::print("{:{}}  SpecifiedAttributes:", " ", Spaces);
            SpecifiedAttributes.Print(1);
            fmt::print("{:{}}  DisplayName:", " ", Spaces);
            DisplayName.Print(1);
            fmt::print("{:{}}  Description:", " ", Spaces);
            Description.Print(1);
            fmt::print("{:{}}  WriteMask:", " ", Spaces);
            WriteMask.Print(1);
            fmt::print("{:{}}  UserWriteMask:", " ", Spaces);
            UserWriteMask.Print(1);
            fmt::print("{:{}}  Value:\n", " ", Spaces);
            Value.Print(Spaces + 4);
            fmt::print("{:{}}  DataType:\n", " ", Spaces);
            DataType.Print(Spaces + 4);
            fmt::print("{:{}}  ValueRank:\n", " ", Spaces);
            ValueRank.Print(Spaces + 4);
            fmt::print("{:{}}  ArrayDimensions:\n", " ", Spaces);
            ArrayDimensions.Print(Spaces + 4);
            fmt::print("{:{}}  AccessLevel:", " ", Spaces);
            AccessLevel.Print(1);
            fmt::print("{:{}}  UserAccessLevel:", " ", Spaces);
            UserAccessLevel.Print(1);
            fmt::print("{:{}}  MinimumSamplingInterval:", " ", Spaces);
            MinimumSamplingInterval.Print(1);
            fmt::print("{:{}}  Historizing:", " ", Spaces);
            Historizing.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            SpecifiedAttributes.SerializeInto(Buffer);
            DisplayName.SerializeInto(Buffer);
            Description.SerializeInto(Buffer);
            WriteMask.SerializeInto(Buffer);
            UserWriteMask.SerializeInto(Buffer);
            Value.SerializeInto(Buffer);
            DataType.SerializeInto(Buffer);
            ValueRank.SerializeInto(Buffer);
            ArrayDimensions.SerializeInto(Buffer);
            AccessLevel.SerializeInto(Buffer);
            UserAccessLevel.SerializeInto(Buffer);
            MinimumSamplingInterval.SerializeInto(Buffer);
            Historizing.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            SpecifiedAttributes.DeserializeFrom(Buffer);
            DisplayName.DeserializeFrom(Buffer);
            Description.DeserializeFrom(Buffer);
            WriteMask.DeserializeFrom(Buffer);
            UserWriteMask.DeserializeFrom(Buffer);
            Value.DeserializeFrom(Buffer);
            DataType.DeserializeFrom(Buffer);
            ValueRank.DeserializeFrom(Buffer);
            ArrayDimensions.DeserializeFrom(Buffer);
            AccessLevel.DeserializeFrom(Buffer);
            UserAccessLevel.DeserializeFrom(Buffer);
            MinimumSamplingInterval.DeserializeFrom(Buffer);
            Historizing.DeserializeFrom(Buffer);
        }
    };

    struct MethodAttributes_t {
        Uint32_t SpecifiedAttributes;
        LocalizedText_t DisplayName;
        LocalizedText_t Description;
        Uint32_t WriteMask;
        Uint32_t UserWriteMask;
        Uint8_t Executable;
        Uint8_t UserExecutable;

        static const NodeId_t Id() { return KnownNodeIds_t::MethodAttributes; }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}MethodAttributes_t\n", " ", Spaces);
            fmt::print("{:{}}  SpecifiedAttributes:", " ", Spaces);
            SpecifiedAttributes.Print(1);
            fmt::print("{:{}}  DisplayName:", " ", Spaces);
            DisplayName.Print(1);
            fmt::print("{:{}}  Description:", " ", Spaces);
            Description.Print(1);
            fmt::print("{:{}}  WriteMask:", " ", Spaces);
            WriteMask.Print(1);
            fmt::print("{:{}}  UserWriteMask:", " ", Spaces);
            UserWriteMask.Print(1);
            fmt::print("{:{}}  Executable:", " ", Spaces);
            Executable.Print(1);
            fmt::print("{:{}}  UserExecutable:", " ", Spaces);
            UserExecutable.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            SpecifiedAttributes.SerializeInto(Buffer);
            DisplayName.SerializeInto(Buffer);
            Description.SerializeInto(Buffer);
            WriteMask.SerializeInto(Buffer);
            UserWriteMask.SerializeInto(Buffer);
            Executable.SerializeInto(Buffer);
            UserExecutable.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            SpecifiedAttributes.DeserializeFrom(Buffer);
            DisplayName.DeserializeFrom(Buffer);
            Description.DeserializeFrom(Buffer);
            WriteMask.DeserializeFrom(Buffer);
            UserWriteMask.DeserializeFrom(Buffer);
            Executable.DeserializeFrom(Buffer);
            UserExecutable.DeserializeFrom(Buffer);
        }
    };

    struct ObjectTypeAttributes_t {
        Uint32_t SpecifiedAttributes;
        LocalizedText_t DisplayName;
        LocalizedText_t Description;
        Uint32_t WriteMask;
        Uint32_t UserWriteMask;
        Uint8_t IsAbstract;

        static const NodeId_t Id() { return KnownNodeIds_t::ObjectTypeAttributes; }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}ObjectTypeAttributes_t\n", " ", Spaces);
            fmt::print("{:{}}  SpecifiedAttributes:", " ", Spaces);
            SpecifiedAttributes.Print(1);
            fmt::print("{:{}}  DisplayName:", " ", Spaces);
            DisplayName.Print(1);
            fmt::print("{:{}}  Description:", " ", Spaces);
            Description.Print(1);
            fmt::print("{:{}}  WriteMask:", " ", Spaces);
            WriteMask.Print(1);
            fmt::print("{:{}}  UserWriteMask:", " ", Spaces);
            UserWriteMask.Print(1);
            fmt::print("{:{}}  IsAbstract:", " ", Spaces);
            IsAbstract.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            SpecifiedAttributes.SerializeInto(Buffer);
            DisplayName.SerializeInto(Buffer);
            Description.SerializeInto(Buffer);
            WriteMask.SerializeInto(Buffer);
            UserWriteMask.SerializeInto(Buffer);
            IsAbstract.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            SpecifiedAttributes.DeserializeFrom(Buffer);
            DisplayName.DeserializeFrom(Buffer);
            Description.DeserializeFrom(Buffer);
            WriteMask.DeserializeFrom(Buffer);
            UserWriteMask.DeserializeFrom(Buffer);
            IsAbstract.DeserializeFrom(Buffer);
        }
    };

    struct VariableTypeAttributes_t {
        Uint32_t SpecifiedAttributes;
        LocalizedText_t DisplayName;
        LocalizedText_t Description;
        Uint32_t WriteMask;
        Uint32_t UserWriteMask;
        Variant_t Value;
        NodeId_t DataType;
        Uint32_t ValueRank;
        Array_t<Uint32_t> ArrayDimensions;
        Uint8_t IsAbstract;

        static const NodeId_t Id() { return KnownNodeIds_t::VariableTypeAttributes; }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}VariableTypeAttributes_t\n", " ", Spaces);
            fmt::print("{:{}}  SpecifiedAttributes:", " ", Spaces);
            SpecifiedAttributes.Print(1);
            fmt::print("{:{}}  DisplayName:", " ", Spaces);
            DisplayName.Print(1);
            fmt::print("{:{}}  Description:", " ", Spaces);
            Description.Print(1);
            fmt::print("{:{}}  WriteMask:", " ", Spaces);
            WriteMask.Print(1);
            fmt::print("{:{}}  UserWriteMask:", " ", Spaces);
            UserWriteMask.Print(1);
            fmt::print("{:{}}  Value:\n", " ", Spaces);
            Value.Print(Spaces + 4);
            fmt::print("{:{}}  DataType:\n", " ", Spaces);
            DataType.Print(Spaces + 4);
            fmt::print("{:{}}  ValueRank:\n", " ", Spaces);
            ValueRank.Print(Spaces + 4);
            fmt::print("{:{}}  ArrayDimensions:\n", " ", Spaces);
            ArrayDimensions.Print(Spaces + 4);
            fmt::print("{:{}}  IsAbstract:", " ", Spaces);
            IsAbstract.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            SpecifiedAttributes.SerializeInto(Buffer);
            DisplayName.SerializeInto(Buffer);
            Description.SerializeInto(Buffer);
            WriteMask.SerializeInto(Buffer);
            UserWriteMask.SerializeInto(Buffer);
            Value.SerializeInto(Buffer);
            DataType.SerializeInto(Buffer);
            ValueRank.SerializeInto(Buffer);
            ArrayDimensions.SerializeInto(Buffer);
            IsAbstract.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            SpecifiedAttributes.DeserializeFrom(Buffer);
            DisplayName.DeserializeFrom(Buffer);
            Description.DeserializeFrom(Buffer);
            WriteMask.DeserializeFrom(Buffer);
            UserWriteMask.DeserializeFrom(Buffer);
            Value.DeserializeFrom(Buffer);
            DataType.DeserializeFrom(Buffer);
            ValueRank.DeserializeFrom(Buffer);
            ArrayDimensions.DeserializeFrom(Buffer);
            IsAbstract.DeserializeFrom(Buffer);
        }
    };

    struct ReferenceTypeAttributes_t {
        Uint32_t SpecifiedAttributes;
        LocalizedText_t DisplayName;
        LocalizedText_t Description;
        Uint32_t WriteMask;
        Uint32_t UserWriteMask;
        Uint8_t IsAbstract;

        static const NodeId_t Id() { return KnownNodeIds_t::ReferenceTypeAttributes; }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}ReferenceTypeAttributes_t\n", " ", Spaces);
            fmt::print("{:{}}  SpecifiedAttributes:", " ", Spaces);
            SpecifiedAttributes.Print(1);
            fmt::print("{:{}}  DisplayName:", " ", Spaces);
            DisplayName.Print(1);
            fmt::print("{:{}}  Description:", " ", Spaces);
            Description.Print(1);
            fmt::print("{:{}}  WriteMask:", " ", Spaces);
            WriteMask.Print(1);
            fmt::print("{:{}}  UserWriteMask:", " ", Spaces);
            UserWriteMask.Print(1);
            fmt::print("{:{}}  IsAbstract:", " ", Spaces);
            IsAbstract.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            SpecifiedAttributes.SerializeInto(Buffer);
            DisplayName.SerializeInto(Buffer);
            Description.SerializeInto(Buffer);
            WriteMask.SerializeInto(Buffer);
            UserWriteMask.SerializeInto(Buffer);
            IsAbstract.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            SpecifiedAttributes.DeserializeFrom(Buffer);
            DisplayName.DeserializeFrom(Buffer);
            Description.DeserializeFrom(Buffer);
            WriteMask.DeserializeFrom(Buffer);
            UserWriteMask.DeserializeFrom(Buffer);
            IsAbstract.DeserializeFrom(Buffer);
        }
    };

    struct DataTypeAttributes_t {
        Uint32_t SpecifiedAttributes;
        LocalizedText_t DisplayName;
        LocalizedText_t Description;
        Uint32_t WriteMask;
        Uint32_t UserWriteMask;
        Uint8_t IsAbstract;
        Uint8_t Symmetric;
        LocalizedText_t InverseName;

        static const NodeId_t Id() { return KnownNodeIds_t::DataTypeAttributes; }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}DataTypeAttributes_t\n", " ", Spaces);
            fmt::print("{:{}}  SpecifiedAttributes:", " ", Spaces);
            SpecifiedAttributes.Print(1);
            fmt::print("{:{}}  DisplayName:", " ", Spaces);
            DisplayName.Print(1);
            fmt::print("{:{}}  Description:", " ", Spaces);
            Description.Print(1);
            fmt::print("{:{}}  WriteMask:", " ", Spaces);
            WriteMask.Print(1);
            fmt::print("{:{}}  UserWriteMask:", " ", Spaces);
            UserWriteMask.Print(1);
            fmt::print("{:{}}  IsAbstract:", " ", Spaces);
            IsAbstract.Print(1);
            fmt::print("{:{}}  Symmetric:", " ", Spaces);
            Symmetric.Print(1);
            fmt::print("{:{}}  InverseName:", " ", Spaces);
            InverseName.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            SpecifiedAttributes.SerializeInto(Buffer);
            DisplayName.SerializeInto(Buffer);
            Description.SerializeInto(Buffer);
            WriteMask.SerializeInto(Buffer);
            UserWriteMask.SerializeInto(Buffer);
            IsAbstract.SerializeInto(Buffer);
            Symmetric.SerializeInto(Buffer);
            InverseName.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            SpecifiedAttributes.DeserializeFrom(Buffer);
            DisplayName.DeserializeFrom(Buffer);
            Description.DeserializeFrom(Buffer);
            WriteMask.DeserializeFrom(Buffer);
            UserWriteMask.DeserializeFrom(Buffer);
            IsAbstract.DeserializeFrom(Buffer);
            Symmetric.DeserializeFrom(Buffer);
            InverseName.DeserializeFrom(Buffer);
        }
    };

    struct ViewAttributes_t {
        Uint32_t SpecifiedAttributes;
        LocalizedText_t DisplayName;
        LocalizedText_t Description;
        Uint32_t WriteMask;
        Uint32_t UserWriteMask;
        Uint8_t ContainsNoLoops;
        Uint8_t EventNotifier;

        static const NodeId_t Id() { return KnownNodeIds_t::ViewAttributes; }

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}ViewAttributes_t\n", " ", Spaces);
            fmt::print("{:{}}  SpecifiedAttributes:", " ", Spaces);
            SpecifiedAttributes.Print(1);
            fmt::print("{:{}}  DisplayName:", " ", Spaces);
            DisplayName.Print(1);
            fmt::print("{:{}}  Description:", " ", Spaces);
            Description.Print(1);
            fmt::print("{:{}}  WriteMask:", " ", Spaces);
            WriteMask.Print(1);
            fmt::print("{:{}}  UserWriteMask:", " ", Spaces);
            UserWriteMask.Print(1);
            fmt::print("{:{}}  ContainsNoLoops:", " ", Spaces);
            ContainsNoLoops.Print(1);
            fmt::print("{:{}}  EventNotifier:", " ", Spaces);
            EventNotifier.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            SpecifiedAttributes.SerializeInto(Buffer);
            DisplayName.SerializeInto(Buffer);
            Description.SerializeInto(Buffer);
            WriteMask.SerializeInto(Buffer);
            UserWriteMask.SerializeInto(Buffer);
            ContainsNoLoops.SerializeInto(Buffer);
            EventNotifier.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            SpecifiedAttributes.DeserializeFrom(Buffer);
            DisplayName.DeserializeFrom(Buffer);
            Description.DeserializeFrom(Buffer);
            WriteMask.DeserializeFrom(Buffer);
            UserWriteMask.DeserializeFrom(Buffer);
            ContainsNoLoops.DeserializeFrom(Buffer);
            EventNotifier.DeserializeFrom(Buffer);
        }
    };

    struct ReadDataValue_t {
        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}ReadDataValue_t\n", " ", Spaces);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {}

        void DeserializeFrom(Deserializer_t& Buffer) {}
    };

    struct SignedSoftwareCertificate_t {
        ByteString_t CertificateData;
        ByteString_t Signature;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}SignedSoftwareCertificate_t\n", " ", Spaces);
            fmt::print("{:{}}  CertificateData:", " ", Spaces);
            CertificateData.Print(1);
            fmt::print("{:{}}  Signature:", " ", Spaces);
            Signature.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            CertificateData.SerializeInto(Buffer);
            Signature.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            CertificateData.DeserializeFrom(Buffer);
            Signature.DeserializeFrom(Buffer);
        }
    };

    struct SignatureData_t {
        String_t Algorithm;
        String_t Signature;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}SignatureData_t\n", " ", Spaces);
            fmt::print("{:{}}  Algorithm:", " ", Spaces);
            Algorithm.Print(1);
            fmt::print("{:{}}  Signature:", " ", Spaces);
            Signature.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Algorithm.SerializeInto(Buffer);
            Signature.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            Algorithm.DeserializeFrom(Buffer);
            Signature.DeserializeFrom(Buffer);
        }
    };

    struct ReadValueId_t {
        NodeId_t NodeId;
        Uint32_t AttributeId;
        String_t IndexRange;
        QualifiedName_t DataEncoding;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}ReadValueId_t\n", " ", Spaces);
            fmt::print("{:{}}  NodeId:\n", " ", Spaces);
            NodeId.Print(Spaces + 4);
            fmt::print("{:{}}  AttributeId:", " ", Spaces);
            AttributeId.Print(1);
            fmt::print("{:{}}  IndexRange:", " ", Spaces);
            IndexRange.Print(1);
            fmt::print("{:{}}  DataEncoding:\n", " ", Spaces);
            DataEncoding.Print(Spaces + 4);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            NodeId.SerializeInto(Buffer);
            AttributeId.SerializeInto(Buffer);
            IndexRange.SerializeInto(Buffer);
            DataEncoding.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            NodeId.DeserializeFrom(Buffer);
            AttributeId.DeserializeFrom(Buffer);
            IndexRange.DeserializeFrom(Buffer);
            DataEncoding.DeserializeFrom(Buffer);
        }

        static ReadValueId_t Make(Rng_t& Rng) {
            ReadValueId_t Req;
            const std::vector<uint32_t> Ids = {
                104,   105,   106,   107,   12169, 111,   112,   113,   114,   116,
                11509, 11511, 3068,  12170, 3067,  3069,  3070,  11433, 11498, 11512,
                11513, 11432, 3071,  3072,  3073,  2005,  2006,  2007,  3074,  3075,
                3076,  3077,  3078,  3079,  3080,  3081,  3082,  3083,  3084,  3085,
                2008,  2742,  3086,  3087,  3088,  3089,  3090,  3091,  3092,  3095,
                3096,  3097,  3098,  3099,  3100,  3101,  3102,  3104,  3105,  3106,
                3107,  3108,  3110,  3112,  3113,  3114,  3115,  11490, 11491, 2014,
                2016,  2017,  2732,  2733,  2734,  3049,  11549, 11550, 11562, 2021,
                3116,  3117,  3118,  3119,  3120,  3121,  3122,  3124,  3125,  3126,
                3127,  3128,  2022,  2023,  3129,  3130,  2025,  2027,  2028,  12098,
                12099, 12100, 12101, 12102, 12103, 12104, 12105, 12106, 12107, 12108,
                12109, 12110, 12111, 12112, 12113, 12114, 12115, 12116, 12117, 12118,
                12119, 12120, 12121, 12122, 12123, 12124, 12125, 12126, 12127, 12128,
                12129, 12130, 12131, 12132, 12133, 12134, 12135, 12136, 12137, 12138,
                12139, 12140, 12141, 12142, 12143, 12144, 12145, 12146, 12147, 12148,
                12149, 12150, 12151, 12152, 2030,  3131,  3132,  3133,  3134,  3135,
                3136,  3137,  3138,  3139,  3140,  3141,  3142,  3143,  8898,  11891,
                3151,  3152,  3153,  3154,  3155,  3156,  3157,  3158,  3159,  3160,
                3161,  3162,  3163,  3164,  3165,  3166,  3167,  3168,  3169,  3170,
                3171,  3172,  3173,  3174,  3175,  3176,  3177,  3178,  2031,  3179,
                3180,  3181,  3182,  3183,  3184,  3185,  3186,  3187,  2032,  2035,
                2037,  2038,  2040,  11948, 11565, 12161, 12162, 11567, 12163, 12164,
                11569, 11570, 11571, 11572, 11573, 11574, 11576, 11577, 11578, 11579,
                11581, 11582, 11584, 11586, 11587, 11589, 11591, 11592, 11594, 11617,
                11618, 11619, 11620, 11621, 11622, 11623, 11625, 11626, 11627, 11628,
                11630, 11631, 11633, 11635, 11636, 11638, 11640, 11641, 11643, 11647,
                11648, 11649, 11650, 11651, 11652, 11653, 11676, 11677, 11678, 11679,
                11681, 11682, 11684, 11686, 11687, 11689, 11691, 11692, 11694, 2042,
                2043,  2044,  2045,  2046,  2047,  3190,  2050,  2051,  2053,  2054,
                2055,  2056,  2057,  2745,  2061,  2746,  2062,  2063,  2065,  2066,
                2070,  2072,  2073,  2747,  2074,  2749,  2076,  2077,  11485, 2079,
                2081,  2083,  2084,  2092,  2094,  2096,  2098,  2750,  2101,  2102,
                2103,  2751,  2128,  2129,  11696, 2134,  2739,  2139,  2140,  2141,
                2142,  3698,  3699,  3700,  3701,  3702,  3703,  2752,  2753,  3052,
                3053,  3054,  3055,  3056,  3057,  2151,  2152,  2153,  2154,  2155,
                2156,  2157,  2159,  2160,  2161,  2162,  2163,  2166,  11697, 11698,
                11699, 2173,  2174,  2175,  2176,  2177,  8888,  2179,  2180,  2181,
                2182,  2183,  2184,  2185,  2186,  2187,  2188,  2189,  2190,  2191,
                2998,  2193,  8889,  8890,  8891,  8892,  8893,  8894,  8895,  8896,
                8897,  8902,  2198,  2199,  2200,  2201,  2202,  2203,  2204,  3050,
                2205,  2206,  2207,  2208,  2209,  8900,  11892, 2217,  2218,  2219,
                2220,  2221,  2222,  2223,  2224,  2225,  2226,  2227,  2228,  2229,
                2230,  2231,  2232,  2233,  2234,  2235,  2236,  2237,  2238,  2239,
                2240,  2241,  2242,  2730,  2731,  2245,  2246,  2247,  2248,  2249,
                2250,  2251,  2252,  3058,  11488, 11701, 2254,  2255,  2256,  2257,
                2258,  2259,  2260,  2262,  2263,  2261,  2264,  2265,  2266,  2992,
                2993,  2267,  2994,  2269,  2271,  2272,  2735,  2736,  2737,  3704,
                11702, 11703, 11705, 12165, 12166, 11707, 12167, 12168, 11709, 11710,
                11711, 11712, 11713, 11714, 2275,  2276,  2277,  2278,  2279,  3705,
                2281,  2282,  2284,  2285,  2286,  2287,  2288,  2289,  2290,  3707,
                3708,  2294,  3709,  11312, 11313, 11314, 11493, 11494, 11193, 11242,
                11273, 11274, 11196, 11197, 11198, 11199, 11200, 11281, 11282, 11283,
                11502, 11275, 2769,  3720,  2770,  3724,  2756,  2757,  2758,  2759,
                2763,  2764,  2765,  2766,  11456, 2772,  3728,  2773,  3732,  2761,
                2768,  2308,  2312,  2774,  3754,  2775,  3746,  2776,  3750,  2777,
                2778,  11940, 2366,  2367,  2370,  2369,  2371,  2374,  2375,  2377,
                11241, 11461, 12024, 12025, 12026, 12027, 12028, 12037, 12046, 12055,
                12056, 12065, 12066, 12067, 12076, 8996,  9000,  9001,  11110, 11111,
                9003,  11112, 11113, 9009,  9010,  3874,  9011,  9012,  9015,  9016,
                9017,  9020,  9021,  9022,  9023,  9024,  9025,  9026,  9030,  3876,
                9035,  9036,  9055,  9056,  9060,  2831,  9064,  9065,  9066,  9067,
                9068,  9070,  9073,  9074,  9093,  9094,  9098,  9102,  9103,  9107,
                9112,  9114,  9118,  9119,  9160,  9161,  9164,  9165,  9166,  11120,
                9169,  9170,  9174,  9179,  9180,  9184,  9185,  9188,  9189,  9214,
                9215,  9216,  9115,  6098,  6100,  6101,  11322, 11323, 11324, 11325,
                11326, 11327, 2991,  11124, 11125, 11126, 11127, 9330,  9332,  9334,
                9336,  11340, 11341, 11342, 11343, 9398,  9399,  9456,  9457,  9461,
                9462,  9465,  9963,  9964,  10020, 10021, 10025, 10029, 10030, 10034,
                10038, 10039, 10043, 10047, 10048, 10052, 10522, 9905,  11158, 4170,
                11851, 11852, 8945,  11853, 8962,  11854, 11855, 3830,  3831,  3833,
                3835,  3836,  3838,  3839,  2392,  2393,  2394,  2395,  2396,  2397,
                2398,  2399,  3840,  3841,  3842,  3843,  3844,  3845,  3846,  3847,
                3848,  3849,  2401,  2403,  2405,  2407,  2409,  2411,  2413,  2415,
                2417,  2419,  2421,  2423,  2425,  2379,  11875, 3825,  3826,  2381,
                2382,  2383,  2384,  2385,  2386,  2387,  2388,  2389,  2390,  11214,
                11168, 11169, 11170, 11171, 2323,  2324,  2325,  2326,  2327,  2328,
                11499, 11500, 11204, 11205, 11206, 11207, 11208, 11215, 2331,  2332,
                11268, 11269, 2334,  2335,  2336,  2337,  2338,  11278, 11279, 11280,
                11501, 11270, 3025,  3028,  3003,  3029,  3030,  3026,  3031,  3032,
                3033,  3027,  3015,  3016,  3017,  3034,  3020,  3021,  3023,  3024,
                11188, 11189, 11190, 11191, 7591,  11878, 7597,  7595,  7596,  7598,
                11881, 11882, 7599,  7605,  11884, 11885, 11886, 7611,  7612,  12078,
                7614,  8252,  8254,  8285,  8291,  8918,  8300,  8297,  8303,  8417,
                8333,  8306,  8309,  8312,  8315,  8318,  8363,  8366,  8369,  8372,
                8321,  8324,  8330,  8564,  8567,  8570,  8573,  8576,  8579,  8582,
                8639,  8702,  8708,  8711,  8807,  8384,  8387,  8390,  8327,  8843,
                11951, 11954, 8846,  8849,  8852,  8855,  8858,  8861,  8294,  8864,
                8867,  8870,  8873,  8876,  12175, 12178, 12083, 12086, 8882,  8879,
                7617,  7619,  7650,  7656,  8914,  7665,  7662,  7668,  7782,  7698,
                7671,  7674,  7677,  7680,  7683,  7728,  7731,  7734,  7737,  7686,
                7689,  7695,  7929,  7932,  7935,  7938,  7941,  7944,  7947,  8004,
                8067,  8073,  8076,  8172,  7749,  7752,  7755,  7692,  8208,  11959,
                11962, 8211,  8214,  8217,  8220,  8223,  8226,  7659,  8229,  8232,
                8235,  8238,  8241,  12183, 12186, 12091, 12094, 8247,  8244 };
            const uint32_t Id = Rng.Pick(Ids);
            if (Rng.Flip()) {
                Req.NodeId = Id;
            }
            else {
                Req.NodeId = fmt::format("i={}", Rng.Int(0xff'ff));
            }

            if (Rng.Lucky(5)) {
                Req.NodeId.NamespaceIndex = 0x63;
            }

            Req.AttributeId = 0xd;
            return Req;
        }
    };

    struct WriteValue_t {
        NodeId_t NodeId;
        Uint32_t AttributeId;
        String_t IndexRange;
        ReadDataValue_t Value;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}WriteValue_t\n", " ", Spaces);
            fmt::print("{:{}}  NodeId:\n", " ", Spaces);
            NodeId.Print(Spaces + 4);
            fmt::print("{:{}}  AttributeId:", " ", Spaces);
            AttributeId.Print(1);
            fmt::print("{:{}}  IndexRange:", " ", Spaces);
            IndexRange.Print(1);
            fmt::print("{:{}}  Value:\n", " ", Spaces);
            Value.Print(Spaces + 4);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            NodeId.SerializeInto(Buffer);
            AttributeId.SerializeInto(Buffer);
            IndexRange.SerializeInto(Buffer);
            Value.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            NodeId.DeserializeFrom(Buffer);
            AttributeId.DeserializeFrom(Buffer);
            IndexRange.DeserializeFrom(Buffer);
            Value.DeserializeFrom(Buffer);
        }
    };

    //
    // Complex objects.
    //

    struct BrowseDescription_t {
        NodeId_t NodeId;
        Uint32_t BrowseDirection;
        NodeId_t ReferenceTypeId;
        Uint8_t IncludeSubtypes;
        Uint32_t NodeClassMask;
        Uint32_t ResultMask;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}BrowseDescription_t\n", " ", Spaces);
            fmt::print("{:{}}  NodeId:\n", " ", Spaces);
            NodeId.Print(Spaces + 4);
            fmt::print("{:{}}  BrowseDirection:", " ", Spaces);
            BrowseDirection.Print(1);
            fmt::print("{:{}}  ReferenceTypeId:", " ", Spaces);
            ReferenceTypeId.Print(1);
            fmt::print("{:{}}  IncludeSubtypes:", " ", Spaces);
            IncludeSubtypes.Print(1);
            fmt::print("{:{}}  NodeClassMask:", " ", Spaces);
            NodeClassMask.Print(1);
            fmt::print("{:{}}  ResultMask:", " ", Spaces);
            ResultMask.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            NodeId.SerializeInto(Buffer);
            BrowseDirection.SerializeInto(Buffer);
            ReferenceTypeId.SerializeInto(Buffer);
            IncludeSubtypes.SerializeInto(Buffer);
            NodeClassMask.SerializeInto(Buffer);
            ResultMask.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            NodeId.DeserializeFrom(Buffer);
            BrowseDirection.DeserializeFrom(Buffer);
            ReferenceTypeId.DeserializeFrom(Buffer);
            IncludeSubtypes.DeserializeFrom(Buffer);
            NodeClassMask.DeserializeFrom(Buffer);
            ResultMask.DeserializeFrom(Buffer);
        }
    };

    struct ViewDescription_t {
        NodeId_t ViewId;
        DateTime_t Timestamp;
        Uint32_t ViewVersion;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}ViewDescription_t\n", " ", Spaces);
            fmt::print("{:{}}  ViewId:\n", " ", Spaces);
            ViewId.Print(Spaces + 4);
            fmt::print("{:{}}  Timestamp:", " ", Spaces);
            Timestamp.Print(1);
            fmt::print("{:{}}  ViewVersion:", " ", Spaces);
            ViewVersion.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            ViewId.SerializeInto(Buffer);
            Timestamp.SerializeInto(Buffer);
            ViewVersion.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            ViewId.DeserializeFrom(Buffer);
            Timestamp.DeserializeFrom(Buffer);
            ViewVersion.DeserializeFrom(Buffer);
        }
    };

    struct DiagnosticInfo_t {
        std::optional<Int32_t> SymbolicId;
        std::optional<Int32_t> NamespaceUri;
        std::optional<Int32_t> Locale;
        std::optional<Int32_t> LocalizedText;
        std::optional<String_t> AdditionalInfo;
        std::optional<StatusCode_t> InnerStatusCode;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}DiagnosticInfo_t\n", " ", Spaces);
            if (SymbolicId) {
                fmt::print("{:{}}  SymbolicId:", " ", Spaces);
                SymbolicId->Print(1);
            }

            if (NamespaceUri) {
                fmt::print("{:{}}  NamespaceUri:", " ", Spaces);
                NamespaceUri->Print(1);
            }

            if (Locale) {
                fmt::print("{:{}}  Locale:", " ", Spaces);
                Locale->Print(1);
            }

            if (LocalizedText) {
                fmt::print("{:{}}  LocalizedText:", " ", Spaces);
                LocalizedText->Print(1);
            }

            if (AdditionalInfo) {
                fmt::print("{:{}}  AdditionalInfo:", " ", Spaces);
                AdditionalInfo->Print(1);
            }

            if (InnerStatusCode) {
                fmt::print("{:{}}  InnerStatusCode:", " ", Spaces);
                InnerStatusCode->Print(1);
            }
        }

        uint8_t EncodingMask() const {
            uint8_t Mask = 0;
            if (SymbolicId) {
                Mask |= 0b1;
            }
            if (NamespaceUri) {
                Mask |= 0b10;
            }
            if (LocalizedText) {
                Mask |= 0b100;
            }
            if (Locale) {
                Mask |= 0b1000;
            }
            if (AdditionalInfo) {
                Mask |= 0b10000;
            }
            if (InnerStatusCode) {
                Mask |= 0b100000;
            }
            return Mask;
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Uint8_t(EncodingMask()).SerializeInto(Buffer);
            if (SymbolicId) {
                SymbolicId->SerializeInto(Buffer);
            }
            if (NamespaceUri) {
                NamespaceUri->SerializeInto(Buffer);
            }
            if (LocalizedText) {
                LocalizedText->SerializeInto(Buffer);
            }
            if (Locale) {
                Locale->SerializeInto(Buffer);
            }
            if (AdditionalInfo) {
                AdditionalInfo->SerializeInto(Buffer);
            }
            if (InnerStatusCode) {
                InnerStatusCode->SerializeInto(Buffer);
            }
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            const uint8_t Encoding = Buffer.Read8();
            if (Encoding & 0b1) {
                Int32_t Int32;
                Int32.DeserializeFrom(Buffer);
                SymbolicId = Int32;
            }

            if (Encoding & 0b10) {
                Int32_t Int32;
                Int32.DeserializeFrom(Buffer);
                NamespaceUri = Int32;
            }

            if (Encoding & 0b100) {
                Int32_t Int32;
                Int32.DeserializeFrom(Buffer);
                LocalizedText = Int32;
            }

            if (Encoding & 0b1000) {
                Int32_t Int32;
                Int32.DeserializeFrom(Buffer);
                Locale = Int32;
            }

            if (Encoding & 0b10000) {
                String_t String;
                String.DeserializeFrom(Buffer);
                AdditionalInfo = String;
            }

            if (Encoding & 0b100000) {
                StatusCode_t StatusCode;
                StatusCode.DeserializeFrom(Buffer);
                InnerStatusCode = StatusCode;
            }
        }
    };

    struct AddNodesItem_t {
        ExpandedNodeId_t ParentNodeId;
        NodeId_t ReferenceTypeId;
        ExpandedNodeId_t RequestedNewNodeId;
        QualifiedName_t BrowseName;
        Uint32_t NodeClass;
        ExtensionObject_t NodeAttributes;
        ExpandedNodeId_t TypeDefinition;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}AddNodesItem_t\n", " ", Spaces);
            fmt::print("{:{}}  ParentNodeId:\n", " ", Spaces);
            ParentNodeId.Print(Spaces + 4);
            fmt::print("{:{}}  ReferenceTypeId:\n", " ", Spaces);
            ReferenceTypeId.Print(Spaces + 4);
            fmt::print("{:{}}  RequestedNewNodeId:\n", " ", Spaces);
            RequestedNewNodeId.Print(Spaces + 4);
            fmt::print("{:{}}  BrowseName:\n", " ", Spaces);
            BrowseName.Print(Spaces + 4);
            fmt::print("{:{}}  NodeClass:\n", " ", Spaces);
            NodeClass.Print(Spaces + 4);
            fmt::print("{:{}}  NodeAttributes:\n", " ", Spaces);
            NodeAttributes.Print(Spaces + 4);
            fmt::print("{:{}}  TypeDefinition:\n", " ", Spaces);
            TypeDefinition.Print(Spaces + 4);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            ParentNodeId.SerializeInto(Buffer);
            ReferenceTypeId.SerializeInto(Buffer);
            RequestedNewNodeId.SerializeInto(Buffer);
            BrowseName.SerializeInto(Buffer);
            NodeClass.SerializeInto(Buffer);
            NodeAttributes.SerializeInto(Buffer);
            TypeDefinition.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            ParentNodeId.DeserializeFrom(Buffer);
            ReferenceTypeId.DeserializeFrom(Buffer);
            RequestedNewNodeId.DeserializeFrom(Buffer);
            BrowseName.DeserializeFrom(Buffer);
            NodeClass.DeserializeFrom(Buffer);
            NodeAttributes.DeserializeFrom(Buffer);
            TypeDefinition.DeserializeFrom(Buffer);
        }
    };

    struct UserTokenPolicy_t {
        String_t PolicyId;
        Uint32_t TokenType;
        String_t IssuedTokenType;
        String_t IssuerEndpointUrl;
        String_t SecurityPolicyUri;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}UserTokenPolicy_t\n", " ", Spaces);
            fmt::print("{:{}}  PolicyId:", " ", Spaces);
            PolicyId.Print(1);
            fmt::print("{:{}}  TokenType:", " ", Spaces);
            TokenType.Print(1);
            fmt::print("{:{}}  IssuedTokenType:", " ", Spaces);
            IssuedTokenType.Print(1);
            fmt::print("{:{}}  IssuerEndpointUrl:", " ", Spaces);
            IssuerEndpointUrl.Print(1);
            fmt::print("{:{}}  SecurityPolicyUri:", " ", Spaces);
            SecurityPolicyUri.Print(1);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            PolicyId.DeserializeFrom(Buffer);
            TokenType.DeserializeFrom(Buffer);
            IssuedTokenType.DeserializeFrom(Buffer);
            IssuerEndpointUrl.DeserializeFrom(Buffer);
            SecurityPolicyUri.DeserializeFrom(Buffer);
        }
    };

    struct ApplicationDescription_t {
        String_t ApplicationUri;
        String_t ProductUri;
        LocalizedText_t ApplicationName;
        Uint32_t ApplicationType;
        String_t GatewayServerUri;
        String_t DiscoveryProfileUri;
        Array_t<String_t> DiscoveryUrls;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}ApplicationDescription_t:\n", " ", Spaces);
            fmt::print("{:{}}  ApplicationUri:", " ", Spaces);
            ApplicationUri.Print(1);
            fmt::print("{:{}}  ProductUri:", " ", Spaces);
            ProductUri.Print(1);
            fmt::print("{:{}}  ApplicationName:", " ", Spaces);
            ApplicationName.Print(1);
            fmt::print("{:{}}  ApplicationType:", " ", Spaces);
            ApplicationType.Print(1);
            fmt::print("{:{}}  GatewayServerUri:", " ", Spaces);
            GatewayServerUri.Print(1);
            fmt::print("{:{}}  DiscoveryProfileUri:", " ", Spaces);
            DiscoveryProfileUri.Print(1);
            fmt::print("{:{}}  DiscoveryUrls:\n", " ", Spaces);
            DiscoveryUrls.Print(Spaces + 4);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            ApplicationUri.SerializeInto(Buffer);
            ProductUri.SerializeInto(Buffer);
            ApplicationName.SerializeInto(Buffer);
            ApplicationType.SerializeInto(Buffer);
            GatewayServerUri.SerializeInto(Buffer);
            DiscoveryProfileUri.SerializeInto(Buffer);
            DiscoveryUrls.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            ApplicationUri.DeserializeFrom(Buffer);
            ProductUri.DeserializeFrom(Buffer);
            ApplicationName.DeserializeFrom(Buffer);
            ApplicationType.DeserializeFrom(Buffer);
            GatewayServerUri.DeserializeFrom(Buffer);
            DiscoveryProfileUri.DeserializeFrom(Buffer);
            DiscoveryUrls.DeserializeFrom(Buffer);
        }
    };

    struct ResponseHeader_t {
        DateTime_t Timestamp;
        Uint32_t RequestHandle;
        StatusCode_t ServiceResult;
        DiagnosticInfo_t ServiceDiagnostics;
        Array_t<String_t> StringTable;
        ExtensionObject_t AdditionalHeader;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}ResponseHeader_t\n", " ", Spaces);
            fmt::print("{:{}}  Timestamp:", " ", Spaces);
            Timestamp.Print(1);
            fmt::print("{:{}}  RequestHandle:", " ", Spaces);
            RequestHandle.Print(1);
            fmt::print("{:{}}  ServiceResult:", " ", Spaces);
            ServiceResult.Print(1);
            fmt::print("{:{}}  ServiceDiagnostics:\n", " ", Spaces);
            ServiceDiagnostics.Print(Spaces + 4);
            fmt::print("{:{}}  StringTable:\n", " ", Spaces);
            StringTable.Print(Spaces + 4);
            fmt::print("{:{}}  AdditionalHeader:\n", " ", Spaces);
            AdditionalHeader.Print(Spaces + 4);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            Timestamp.SerializeInto(Buffer);
            RequestHandle.SerializeInto(Buffer);
            ServiceResult.SerializeInto(Buffer);
            ServiceDiagnostics.SerializeInto(Buffer);
            StringTable.SerializeInto(Buffer);
            AdditionalHeader.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            Timestamp.DeserializeFrom(Buffer);
            RequestHandle.DeserializeFrom(Buffer);
            ServiceResult.DeserializeFrom(Buffer);
            ServiceDiagnostics.DeserializeFrom(Buffer);
            StringTable.DeserializeFrom(Buffer);
            AdditionalHeader.DeserializeFrom(Buffer);
        }
    };

    struct RequestHeader_t {
        // 'Request' header -
        // https://reference.opcfoundation.org/Core/Part4/v104/docs/7.28
        NodeId_t AuthenticationToken;
        DateTime_t Timestamp;
        Uint32_t RequestHandle;
        Uint32_t ReturnDiagnostics;
        String_t AuditEntryId;
        Uint32_t TimeoutHint;
        ExtensionObject_t AdditionalHeader;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}RequestHeader_t\n", " ", Spaces);
            fmt::print("{:{}}  AuthenticationToken:\n", " ", Spaces);
            AuthenticationToken.Print(Spaces + 4);
            fmt::print("{:{}}  Timestamp:", " ", Spaces);
            Timestamp.Print(1);
            fmt::print("{:{}}  ReturnDiagnostics:", " ", Spaces);
            ReturnDiagnostics.Print(1);
            fmt::print("{:{}}  AuditEntryId:", " ", Spaces);
            AuditEntryId.Print(1);
            fmt::print("{:{}}  TimeoutHint:", " ", Spaces);
            TimeoutHint.Print(1);
            fmt::print("{:{}}  AdditionalHeader:\n", " ", Spaces);
            AdditionalHeader.Print(Spaces + 4);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            AuthenticationToken.SerializeInto(Buffer);
            Timestamp.SerializeInto(Buffer);
            RequestHandle.SerializeInto(Buffer);
            ReturnDiagnostics.SerializeInto(Buffer);
            AuditEntryId.SerializeInto(Buffer);
            TimeoutHint.SerializeInto(Buffer);
            AdditionalHeader.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            AuthenticationToken.DeserializeFrom(Buffer);
            Timestamp.DeserializeFrom(Buffer);
            RequestHandle.DeserializeFrom(Buffer);
            ReturnDiagnostics.DeserializeFrom(Buffer);
            AuditEntryId.DeserializeFrom(Buffer);
            TimeoutHint.DeserializeFrom(Buffer);
            AdditionalHeader.DeserializeFrom(Buffer);
        }
    };

    struct SecurityToken_t {
        Uint32_t ChannelId;
        Uint32_t TokenId;
        DateTime_t CreatedAt;
        Uint32_t RevisedLifetime;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}SecurityToken_t:\n", " ", Spaces);
            fmt::print("{:{}}  TokenId:", " ", Spaces);
            TokenId.Print(Spaces + 4);
            fmt::print("{:{}}  CreatedAt:\n", " ", Spaces);
            CreatedAt.Print(Spaces + 4);
            fmt::print("{:{}}  RevisedLifetime:\n", " ", Spaces);
            RevisedLifetime.Print(Spaces + 4);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            ChannelId.SerializeInto(Buffer);
            TokenId.SerializeInto(Buffer);
            CreatedAt.SerializeInto(Buffer);
            RevisedLifetime.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            ChannelId.DeserializeFrom(Buffer);
            TokenId.DeserializeFrom(Buffer);
            CreatedAt.DeserializeFrom(Buffer);
            RevisedLifetime.DeserializeFrom(Buffer);
        }
    };

    struct ChannelHeader_t {
        Uint32_t SecureChannelId;
        String_t SecurityPolicyUri =
            "http://opcfoundation.org/UA/SecurityPolicy#None";
        String_t SenderCertificate;
        String_t ReceiverCertificateThumbprint;
        Uint32_t SequenceNumber;
        Uint32_t RequestId;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}ChannelHeader_t\n", " ", Spaces);
            fmt::print("{:{}}  SecureChannelId:", " ", Spaces);
            SecureChannelId.Print(1);
            fmt::print("{:{}}  SecurityPolicyUri:", " ", Spaces);
            SecurityPolicyUri.Print(1);
            fmt::print("{:{}}  SenderCertificate:", " ", Spaces);
            SenderCertificate.Print(1);
            fmt::print("{:{}}  ReceiverCertificateThumbprint:", " ", Spaces);
            ReceiverCertificateThumbprint.Print(1);
            fmt::print("{:{}}  SequenceNumber:", " ", Spaces);
            SequenceNumber.Print(1);
            fmt::print("{:{}}  RequestId:", " ", Spaces);
            RequestId.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            SecureChannelId.SerializeInto(Buffer);
            SecurityPolicyUri.SerializeInto(Buffer);
            SenderCertificate.SerializeInto(Buffer);
            ReceiverCertificateThumbprint.SerializeInto(Buffer);
            SequenceNumber.SerializeInto(Buffer);
            RequestId.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            SecureChannelId.DeserializeFrom(Buffer);
            SecurityPolicyUri.DeserializeFrom(Buffer);
            SenderCertificate.DeserializeFrom(Buffer);
            ReceiverCertificateThumbprint.DeserializeFrom(Buffer);
            SequenceNumber.DeserializeFrom(Buffer);
            RequestId.DeserializeFrom(Buffer);
        }
    };

    struct SecureChannelHeader_t {
        Uint32_t SecureChannelId;
        Uint32_t SecureTokenId;
        Uint32_t SecureSequenceNumber;
        Uint32_t SecureRequestId;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}SecureChannelHeader_t\n", " ", Spaces);
            fmt::print("{:{}}  SecureChannelId:", " ", Spaces);
            SecureChannelId.Print(1);
            fmt::print("{:{}}  SecureTokenId:", " ", Spaces);
            SecureTokenId.Print(1);
            fmt::print("{:{}}  SecureSequenceNumber:", " ", Spaces);
            SecureSequenceNumber.Print(1);
            fmt::print("{:{}}  SecureRequestId:", " ", Spaces);
            SecureRequestId.Print(1);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            SecureChannelId.SerializeInto(Buffer);
            SecureTokenId.SerializeInto(Buffer);
            SecureSequenceNumber.SerializeInto(Buffer);
            SecureRequestId.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            SecureChannelId.DeserializeFrom(Buffer);
            SecureTokenId.DeserializeFrom(Buffer);
            SecureSequenceNumber.DeserializeFrom(Buffer);
            SecureRequestId.DeserializeFrom(Buffer);
        }
    };

    struct EndpointDescription_t {
        String_t EndpointUrl;
        ApplicationDescription_t Server;
        ByteString_t ServerCertificate;
        Uint32_t SecurityMode;
        String_t SecurityPolicyUri;
        Array_t<UserTokenPolicy_t> UserIdentityTokens;
        String_t TransportProfileUri;
        Uint8_t SecurityLevel;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}EndpointDescription_t\n", " ", Spaces);
            fmt::print("{:{}}  EndpointUrl:", " ", Spaces);
            EndpointUrl.Print(1);
            fmt::print("{:{}}  Server:\n", " ", Spaces);
            Server.Print(Spaces + 4);
            fmt::print("{:{}}  ServerCertificate:", " ", Spaces);
            ServerCertificate.Print(1);
            fmt::print("{:{}}  SecurityMode:", " ", Spaces);
            SecurityMode.Print(1);
            fmt::print("{:{}}  SecurityPolicyUri:", " ", Spaces);
            SecurityPolicyUri.Print(1);
            fmt::print("{:{}}  UserIdentityTokens:\n", " ", Spaces);
            UserIdentityTokens.Print(Spaces + 4);
            fmt::print("{:{}}  TransportProfileUri:", " ", Spaces);
            TransportProfileUri.Print(1);
            fmt::print("{:{}}  SecurityLevel:", " ", Spaces);
            SecurityLevel.Print(1);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            EndpointUrl.DeserializeFrom(Buffer);
            Server.DeserializeFrom(Buffer);
            ServerCertificate.DeserializeFrom(Buffer);
            SecurityMode.DeserializeFrom(Buffer);
            SecurityPolicyUri.DeserializeFrom(Buffer);
            UserIdentityTokens.DeserializeFrom(Buffer);
            TransportProfileUri.DeserializeFrom(Buffer);
            SecurityLevel.DeserializeFrom(Buffer);
        }
    };

    struct CallMethodRequest_t {
        NodeId_t ObjectId;
        NodeId_t MethodId;
        Array_t<Variant_t> InputArguments;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}CallMethodRequest_t\n", " ", Spaces);
            fmt::print("{:{}}  ObjectId:\n", " ", Spaces);
            ObjectId.Print(Spaces + 4);
            fmt::print("{:{}}  MethodId:\n", " ", Spaces);
            MethodId.Print(Spaces + 4);
            fmt::print("{:{}}  InputArguments:\n", " ", Spaces);
            InputArguments.Print(Spaces + 4);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            ObjectId.SerializeInto(Buffer);
            MethodId.SerializeInto(Buffer);
            InputArguments.SerializeInto(Buffer);
        }

        void DeserializeFrom(Deserializer_t& Buffer) {
            ObjectId.DeserializeFrom(Buffer);
            MethodId.DeserializeFrom(Buffer);
            InputArguments.DeserializeFrom(Buffer);
        }
    };

    //
    // Messages.
    //

    struct Hello_t {
        static constexpr uint8_t Magic[4] = { 'H', 'E', 'L', 'F' };
        // https://reference.opcfoundation.org/Core/Part6/v104/docs/7.1.2.3
        Uint32_t ProtocolVersion;
        Uint32_t ReceiveBufferSize;
        Uint32_t SendBufferSize;
        Uint32_t MaxMessageSize;
        Uint32_t MaxChunkCount;
        String_t EndpointUrl;

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            const size_t BeforeSize = Buffer.size();
            Buffer.resize(Buffer.size() + 8);
            ProtocolVersion.SerializeInto(Buffer);
            ReceiveBufferSize.SerializeInto(Buffer);
            SendBufferSize.SerializeInto(Buffer);
            MaxMessageSize.SerializeInto(Buffer);
            MaxChunkCount.SerializeInto(Buffer);
            EndpointUrl.SerializeInto(Buffer);

            const size_t AfterSize = Buffer.size();
            const size_t TotalSize = AfterSize - BeforeSize;
            std::memcpy(Buffer.data() + BeforeSize, Magic, sizeof(Magic));
            const auto TotalSizeU32 = uint32_t(TotalSize);
            std::memcpy(Buffer.data() + BeforeSize + 4, (void*)&TotalSizeU32,
                sizeof(TotalSizeU32));
        }
    };

    struct Acknowledge_t {
        Uint32_t Version;
        Uint32_t ReceiveBufferSize;
        Uint32_t SendBufferSize;
        Uint32_t MaxMessageSize;
        Uint32_t MaxChunkCount;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}Acknowledge_t\n", " ", Spaces);
            fmt::print("{:{}}  Version:", " ", Spaces);
            Version.Print(1);
            fmt::print("{:{}}  ReceiveBufferSize:", " ", Spaces);
            ReceiveBufferSize.Print(1);
            fmt::print("{:{}}  SendBufferSize:", " ", Spaces);
            SendBufferSize.Print(1);
            fmt::print("{:{}}  MaxMessageSize:", " ", Spaces);
            MaxMessageSize.Print(1);
            fmt::print("{:{}}  MaxChunkCount:", " ", Spaces);
            MaxChunkCount.Print(1);
        }

        void DeserializeFrom(const std::vector<uint8_t>& RawBuffer) {
            Deserializer_t Buffer(RawBuffer);
            std::array<uint8_t, 4> Magic;
            Buffer.Read(Magic.data(), sizeof(Magic));
            if (std::memcmp(Magic.data(), "ACKF", Magic.size()) != 0) {
                throw std::runtime_error("expected an acknowledge packet to deserialize");
            }
            Buffer.Advance(4);
            Version.DeserializeFrom(Buffer);
            ReceiveBufferSize.DeserializeFrom(Buffer);
            SendBufferSize.DeserializeFrom(Buffer);
            MaxMessageSize.DeserializeFrom(Buffer);
            MaxChunkCount.DeserializeFrom(Buffer);
        }
    };

    struct OpenSecureChannel_t {
        static constexpr uint8_t Magic[4] = { 'O', 'P', 'N', 'F' };
        ChannelHeader_t ChannelHeader;
        // NodeId_t ServiceNodeId = NodeId_t::Numeric(0x1be); // Some stack don't
        // want it encoded like that
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::OpenSecureChannelRequest);
        RequestHeader_t RequestHeader;
        Uint32_t ClientProtocolVersion;
        Uint32_t RequestType;
        Uint32_t SecurityMode = MessageSecurityMode_t::NONE;
        ByteString_t ClientNonce;
        Uint32_t RequestedLifetime;

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            const size_t BeforeSize = Buffer.size();
            Buffer.resize(Buffer.size() + 8);
            ChannelHeader.SerializeInto(Buffer);
            ServiceNodeId.SerializeInto(Buffer);
            RequestHeader.SerializeInto(Buffer);
            ClientProtocolVersion.SerializeInto(Buffer);
            RequestType.SerializeInto(Buffer);
            SecurityMode.SerializeInto(Buffer);
            ClientNonce.SerializeInto(Buffer);
            RequestedLifetime.SerializeInto(Buffer);

            const size_t AfterSize = Buffer.size();
            const size_t TotalSize = AfterSize - BeforeSize;
            std::memcpy(Buffer.data() + BeforeSize, Magic, sizeof(Magic));
            const auto TotalSizeU32 = uint32_t(TotalSize);
            std::memcpy(Buffer.data() + BeforeSize + 4, (void*)&TotalSizeU32,
                sizeof(TotalSizeU32));
        }
    };

    struct OpenSecureChannelResponse_t {
        ChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::OpenSecureChannelResponse);
        ResponseHeader_t ResponseHeader;
        Uint32_t ServerProtocolVersion;
        SecurityToken_t ChannelSecurityToken;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}OpenSecureChannelResponse_t\n", " ", Spaces);
            fmt::print("{:{}}  ChannelHeader:\n", " ", Spaces);
            ChannelHeader.Print(Spaces + 4);
            fmt::print("{:{}}  ServiceNodeId:\n", " ", Spaces);
            ServiceNodeId.Print(Spaces + 4);
            fmt::print("{:{}}  ResponseHeader:\n", " ", Spaces);
            ResponseHeader.Print(Spaces + 4);
            fmt::print("{:{}}  ServerProtocolVersion:", " ", Spaces);
            ServerProtocolVersion.Print(1);
            fmt::print("{:{}}  ChannelSecurityToken:\n", " ", Spaces);
            ChannelSecurityToken.Print(Spaces + 4);
        }

        void DeserializeFrom(const std::vector<uint8_t>& RawBuffer) {
            Deserializer_t Buffer(RawBuffer);
            std::array<uint8_t, 4> Magic;
            Buffer.Read(Magic.data(), sizeof(Magic));
            if (std::memcmp(Magic.data(), "OPNF", Magic.size()) != 0) {
                throw std::runtime_error("expected an open packet to deserialize");
            }
            Buffer.Advance(4);
            ChannelHeader.DeserializeFrom(Buffer);
            ServiceNodeId.DeserializeFrom(Buffer);
            if (ServiceNodeId.NamespaceIndex.Value != 0) {
                throw std::runtime_error(
                    "open secure channel response expect namespace 0");
            }

            if (std::get<Uint32_t>(ServiceNodeId.Identifier).Value !=
                KnownNodeIds_t::OpenSecureChannelResponse) {
                throw std::runtime_error(
                    "open secure channel response expect correct nodeid");
            }

            ResponseHeader.DeserializeFrom(Buffer);
            ServerProtocolVersion.DeserializeFrom(Buffer);
            ChannelSecurityToken.DeserializeFrom(Buffer);
        }
    };

    struct GetEndpointsRequest_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::GetEndpointsRequest);
        RequestHeader_t RequestHeader;
        String_t EndpointUrl;
        Array_t<String_t> LocaleIds;
        Array_t<String_t> ProfileUris;

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            const size_t BeforeSize = Buffer.size();
            Buffer.resize(Buffer.size() + 8);
            ChannelHeader.SerializeInto(Buffer);
            ServiceNodeId.SerializeInto(Buffer);
            RequestHeader.SerializeInto(Buffer);
            EndpointUrl.SerializeInto(Buffer);
            LocaleIds.SerializeInto(Buffer);
            ProfileUris.SerializeInto(Buffer);

            const size_t AfterSize = Buffer.size();
            const size_t TotalSize = AfterSize - BeforeSize;
            std::memcpy(Buffer.data() + BeforeSize, Magic, sizeof(Magic));
            const auto TotalSizeU32 = uint32_t(TotalSize);
            std::memcpy(Buffer.data() + BeforeSize + 4, (void*)&TotalSizeU32,
                sizeof(TotalSizeU32));
        }
    };

    struct GetEndpointsResponse_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::GetEndpointsResponse);
        ResponseHeader_t ResponseHeader;
        Array_t<EndpointDescription_t> Endpoints;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}GetEndpointsResponse_t\n", " ", Spaces);
            fmt::print("{:{}}  ChannelHeader:\n", " ", Spaces);
            ChannelHeader.Print(Spaces + 4);
            fmt::print("{:{}}  ServiceNodeId:\n", " ", Spaces);
            ServiceNodeId.Print(Spaces + 4);
            fmt::print("{:{}}  ResponseHeader:\n", " ", Spaces);
            ResponseHeader.Print(Spaces + 4);
            fmt::print("{:{}}  Endpoints:\n", " ", Spaces);
            Endpoints.Print(Spaces + 4);
        }

        void DeserializeFrom(const std::vector<uint8_t>& RawBuffer) {
            Deserializer_t Buffer(RawBuffer);
            std::array<uint8_t, 4> Magic;
            Buffer.Read(Magic.data(), sizeof(Magic));
            if (std::memcmp(Magic.data(), "MSGF", Magic.size()) != 0) {
                throw std::runtime_error("expected an open packet to deserialize");
            }
            Buffer.Advance(4);

            ChannelHeader.DeserializeFrom(Buffer);
            ServiceNodeId.DeserializeFrom(Buffer);
            if (ServiceNodeId.NamespaceIndex.Value != 0) {
                throw std::runtime_error("get endpoints response expect namespace 0");
            }

            if (std::get<Uint32_t>(ServiceNodeId.Identifier).Value !=
                KnownNodeIds_t::GetEndpointsResponse) {
                throw std::runtime_error("get endpoints response expect correct nodeid");
            }

            ResponseHeader.DeserializeFrom(Buffer);
            Endpoints.DeserializeFrom(Buffer);
        }
    };

    struct CreateSessionRequest_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::CreateSessionRequest);
        RequestHeader_t RequestHeader;
        ApplicationDescription_t ClientDescription;
        String_t ServerUri;
        String_t EndpointUrl;
        String_t SessionName;
        ByteString_t ClientNonce;
        ByteString_t ClientCertificate;
        Double_t RequestedSessionTimeout;
        Uint32_t MaxResponseMessageSize;

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            const size_t BeforeSize = Buffer.size();
            Buffer.resize(Buffer.size() + 8);
            ChannelHeader.SerializeInto(Buffer);
            ServiceNodeId.SerializeInto(Buffer);
            RequestHeader.SerializeInto(Buffer);
            ClientDescription.SerializeInto(Buffer);
            ServerUri.SerializeInto(Buffer);
            EndpointUrl.SerializeInto(Buffer);
            SessionName.SerializeInto(Buffer);
            ClientNonce.SerializeInto(Buffer);
            ClientCertificate.SerializeInto(Buffer);
            RequestedSessionTimeout.SerializeInto(Buffer);
            MaxResponseMessageSize.SerializeInto(Buffer);

            const size_t AfterSize = Buffer.size();
            const size_t TotalSize = AfterSize - BeforeSize;
            std::memcpy(Buffer.data() + BeforeSize, Magic, sizeof(Magic));
            const auto TotalSizeU32 = uint32_t(TotalSize);
            std::memcpy(Buffer.data() + BeforeSize + 4, (void*)&TotalSizeU32,
                sizeof(TotalSizeU32));
        }
    };

    struct CreateSessionResponse_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::CreateSessionResponse);
        ResponseHeader_t ResponseHeader;
        NodeId_t SessionId;
        NodeId_t AuthenticationToken;
        Double_t RevisedSessionTimeout;
        ByteString_t ServerNonce;
        ByteString_t ServerCertificate;
        Array_t<EndpointDescription_t> ServerEndpoints;
        Array_t<SignedSoftwareCertificate_t> ServerSoftwareCertificates;
        SignatureData_t ServerSignature;
        Uint32_t MaxRequestMessageSize;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}CreateSessionResponse_t\n", " ", Spaces);
            fmt::print("{:{}}  ChannelHeader:\n", " ", Spaces);
            ChannelHeader.Print(Spaces + 4);
            fmt::print("{:{}}  ServiceNodeId:\n", " ", Spaces);
            ServiceNodeId.Print(Spaces + 4);
            fmt::print("{:{}}  ResponseHeader:\n", " ", Spaces);
            ResponseHeader.Print(Spaces + 4);
            fmt::print("{:{}}  SessionId:\n", " ", Spaces);
            SessionId.Print(Spaces + 4);
            fmt::print("{:{}}  AuthenticationToken:\n", " ", Spaces);
            AuthenticationToken.Print(Spaces + 4);
            fmt::print("{:{}}  RevisedSessionTimeout:\n", " ", Spaces);
            RevisedSessionTimeout.Print(Spaces + 4);
            fmt::print("{:{}}  ServerNonce:\n", " ", Spaces);
            ServerNonce.Print(Spaces + 4);
            fmt::print("{:{}}  ServerCertificate:\n", " ", Spaces);
            ServerCertificate.Print(Spaces + 4);
            fmt::print("{:{}}  ServerEndpoints:\n", " ", Spaces);
            ServerEndpoints.Print(Spaces + 4);
            fmt::print("{:{}}  ServerSoftwareCertificates:\n", " ", Spaces);
            ServerSoftwareCertificates.Print(Spaces + 4);
            fmt::print("{:{}}  ServerSignature:\n", " ", Spaces);
            ServerSignature.Print(Spaces + 4);
            fmt::print("{:{}}  MaxRequestMessageSize:\n", " ", Spaces);
            MaxRequestMessageSize.Print(Spaces + 4);
        }

        void DeserializeFrom(const std::vector<uint8_t>& RawBuffer) {
            Deserializer_t Buffer(RawBuffer);
            std::array<uint8_t, 4> Magic;
            Buffer.Read(Magic.data(), sizeof(Magic));
            if (std::memcmp(Magic.data(), "MSGF", Magic.size()) != 0) {
                throw std::runtime_error("expected an open packet to deserialize");
            }
            Buffer.Advance(4);

            ChannelHeader.DeserializeFrom(Buffer);
            ServiceNodeId.DeserializeFrom(Buffer);
            if (ServiceNodeId.NamespaceIndex.Value != 0) {
                throw std::runtime_error("create session response expect namespace 0");
            }

            if (std::get<Uint32_t>(ServiceNodeId.Identifier).Value !=
                KnownNodeIds_t::CreateSessionResponse) {
                throw std::runtime_error("create session response expect correct nodeid");
            }

            ResponseHeader.DeserializeFrom(Buffer);
            SessionId.DeserializeFrom(Buffer);
            AuthenticationToken.DeserializeFrom(Buffer);
            RevisedSessionTimeout.DeserializeFrom(Buffer);
            ServerNonce.DeserializeFrom(Buffer);
            ServerCertificate.DeserializeFrom(Buffer);
            ServerEndpoints.DeserializeFrom(Buffer);
            ServerSoftwareCertificates.DeserializeFrom(Buffer);
            ServerSignature.DeserializeFrom(Buffer);
            MaxRequestMessageSize.DeserializeFrom(Buffer);
        }
    };

    struct ActivateSessionRequest_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::ActivateSessionRequest);
        RequestHeader_t RequestHeader;
        SignatureData_t ClientSignature;
        Array_t<SignedSoftwareCertificate_t> ClientSoftwareCertificates;
        Array_t<String_t> LocaleIds;
        ExtensionObject_t UserIdentityToken =
            oua::Serializable_t(AnonymousIdentityToken_t("Anonymous"));
        SignatureData_t UserTokenSignature;

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            const size_t BeforeSize = Buffer.size();
            Buffer.resize(Buffer.size() + 8);
            ChannelHeader.SerializeInto(Buffer);
            ServiceNodeId.SerializeInto(Buffer);
            RequestHeader.SerializeInto(Buffer);
            ClientSignature.SerializeInto(Buffer);
            ClientSoftwareCertificates.SerializeInto(Buffer);
            LocaleIds.SerializeInto(Buffer);
            UserIdentityToken.SerializeInto(Buffer);
            UserTokenSignature.SerializeInto(Buffer);

            const size_t AfterSize = Buffer.size();
            const size_t TotalSize = AfterSize - BeforeSize;
            std::memcpy(Buffer.data() + BeforeSize, Magic, sizeof(Magic));
            const auto TotalSizeU32 = uint32_t(TotalSize);
            std::memcpy(Buffer.data() + BeforeSize + 4, (void*)&TotalSizeU32,
                sizeof(TotalSizeU32));
        }
    };

    struct ActivateSessionResponse_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::ActivateSessionResponse);
        ResponseHeader_t ResponseHeader;
        ByteString_t ServerNonce;
        Array_t<StatusCode_t> Results;
        Array_t<DiagnosticInfo_t> DiagnosticInfos;

        void Print(const size_t Spaces = 2) {
            fmt::print("{:{}}ActivateSessionResponse_t\n", " ", Spaces);
            fmt::print("{:{}}  ChannelHeader:\n", " ", Spaces);
            ChannelHeader.Print(Spaces + 4);
            fmt::print("{:{}}  ServiceNodeId:\n", " ", Spaces);
            ServiceNodeId.Print(Spaces + 4);
            fmt::print("{:{}}  ResponseHeader:\n", " ", Spaces);
            ResponseHeader.Print(Spaces + 4);
            fmt::print("{:{}}  ServerNonce:", " ", Spaces);
            ServerNonce.Print(1);
            fmt::print("{:{}}  Results:\n", " ", Spaces);
            Results.Print(Spaces + 4);
            fmt::print("{:{}}  DiagnosticInfos:\n", " ", Spaces);
            DiagnosticInfos.Print(Spaces + 4);
        }

        void DeserializeFrom(const std::vector<uint8_t>& RawBuffer) {
            Deserializer_t Buffer(RawBuffer);
            std::array<uint8_t, 4> Magic;
            Buffer.Read(Magic.data(), sizeof(Magic));
            if (std::memcmp(Magic.data(), "MSGF", Magic.size()) != 0) {
                throw std::runtime_error("expected an open packet to deserialize");
            }
            Buffer.Advance(4);
            ChannelHeader.DeserializeFrom(Buffer);
            ServiceNodeId.DeserializeFrom(Buffer);
            if (ServiceNodeId.NamespaceIndex.Value != 0) {
                throw std::runtime_error("activate session response expect namespace 0");
            }

            if (std::get<Uint32_t>(ServiceNodeId.Identifier).Value !=
                KnownNodeIds_t::ActivateSessionResponse) {
                throw std::runtime_error(
                    "activate session response expect correct nodeid");
            }

            ResponseHeader.DeserializeFrom(Buffer);
            ServerNonce.DeserializeFrom(Buffer);
            Results.DeserializeFrom(Buffer);
            DiagnosticInfos.DeserializeFrom(Buffer);
        }
    };

    struct ReadRequest_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::ReadRequest);
        RequestHeader_t RequestHeader;
        Double_t MaxAge;
        Uint32_t TimestampsToReturn;
        Array_t<ReadValueId_t> NodesToRead;

        void Print(const size_t Spaces = 2) {
            fmt::print("{:{}}ReadRequest_t\n", " ", Spaces);
            fmt::print("{:{}}  ChannelHeader:\n", " ", Spaces);
            ChannelHeader.Print(Spaces + 4);
            fmt::print("{:{}}  ServiceNodeId:\n", " ", Spaces);
            ServiceNodeId.Print(Spaces + 4);
            fmt::print("{:{}}  RequestHeader:\n", " ", Spaces);
            RequestHeader.Print(Spaces + 4);
            fmt::print("{:{}}  MaxAge:", " ", Spaces);
            MaxAge.Print(1);
            fmt::print("{:{}}  TimestampsToReturn:", " ", Spaces);
            TimestampsToReturn.Print(1);
            fmt::print("{:{}}  NodesToRead:\n", " ", Spaces);
            NodesToRead.Print(Spaces + 4);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            const size_t BeforeSize = Buffer.size();
            Buffer.resize(Buffer.size() + 8);
            ChannelHeader.SerializeInto(Buffer);
            ServiceNodeId.SerializeInto(Buffer);
            RequestHeader.SerializeInto(Buffer);
            MaxAge.SerializeInto(Buffer);
            TimestampsToReturn.SerializeInto(Buffer);
            NodesToRead.SerializeInto(Buffer);

            const size_t AfterSize = Buffer.size();
            const size_t TotalSize = AfterSize - BeforeSize;
            std::memcpy(Buffer.data() + BeforeSize, Magic, sizeof(Magic));
            const auto TotalSizeU32 = uint32_t(TotalSize);
            std::memcpy(Buffer.data() + BeforeSize + 4, (void*)&TotalSizeU32,
                sizeof(TotalSizeU32));
        }

        void DeserializeFrom(const std::vector<uint8_t>& RawBuffer) {
            Deserializer_t Buffer(RawBuffer);
            std::array<uint8_t, 4> Magic;
            Buffer.Read(Magic.data(), sizeof(Magic));
            if (std::memcmp(Magic.data(), "MSGF", Magic.size()) != 0) {
                throw std::runtime_error("expected an open packet to deserialize");
            }
            Buffer.Advance(4);
            ChannelHeader.DeserializeFrom(Buffer);
            ServiceNodeId.DeserializeFrom(Buffer);
            if (ServiceNodeId.NamespaceIndex.Value != 0) {
                throw std::runtime_error("read request expect namespace 0");
            }

            if (std::get<Uint32_t>(ServiceNodeId.Identifier).Value !=
                KnownNodeIds_t::ReadRequest) {
                throw std::runtime_error("read request expect correct nodeid");
            }

            RequestHeader.DeserializeFrom(Buffer);
            MaxAge.DeserializeFrom(Buffer);
            TimestampsToReturn.DeserializeFrom(Buffer);
            NodesToRead.DeserializeFrom(Buffer);
        }
    };

    struct ReadResponse_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::ReadResponse);
        ResponseHeader_t ResponseHeader;
        Array_t<ReadDataValue_t> Results;
        Array_t<DiagnosticInfo_t> DiagnosticInfos;

        void Print(const size_t Spaces = 2) {
            fmt::print("{:{}}ReadResponse_t\n", " ", Spaces);
            fmt::print("{:{}}  ChannelHeader:\n", " ", Spaces);
            ChannelHeader.Print(Spaces + 4);
            fmt::print("{:{}}  ServiceNodeId:\n", " ", Spaces);
            ServiceNodeId.Print(Spaces + 4);
            fmt::print("{:{}}  ResponseHeader:\n", " ", Spaces);
            ResponseHeader.Print(Spaces + 4);
            fmt::print("{:{}}  Results:", " ", Spaces);
            Results.Print(1);
            fmt::print("{:{}}  DiagnosticInfos:\n", " ", Spaces);
            DiagnosticInfos.Print(Spaces + 4);
        }

        void DeserializeFrom(const std::vector<uint8_t>& RawBuffer) {
            Deserializer_t Buffer(RawBuffer);
            std::array<uint8_t, 4> Magic;
            Buffer.Read(Magic.data(), sizeof(Magic));
            if (std::memcmp(Magic.data(), "MSGF", Magic.size()) != 0) {
                throw std::runtime_error("expected an open packet to deserialize");
            }
            Buffer.Advance(4);
            ChannelHeader.DeserializeFrom(Buffer);
            ServiceNodeId.DeserializeFrom(Buffer);
            if (ServiceNodeId.NamespaceIndex.Value != 0) {
                throw std::runtime_error("activate session response expect namespace 0");
            }

            if (std::get<Uint32_t>(ServiceNodeId.Identifier).Value !=
                KnownNodeIds_t::ReadResponse) {
                throw std::runtime_error(
                    "activate session response expect correct nodeid");
            }

            ResponseHeader.DeserializeFrom(Buffer);
            Results.DeserializeFrom(Buffer);
            DiagnosticInfos.DeserializeFrom(Buffer);
        }
    };

    struct WriteRequest_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::WriteRequest);
        RequestHeader_t RequestHeader;
        Array_t<WriteValue_t> NodesToWrite;

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            const size_t BeforeSize = Buffer.size();
            Buffer.resize(Buffer.size() + 8);
            ChannelHeader.SerializeInto(Buffer);
            ServiceNodeId.SerializeInto(Buffer);
            RequestHeader.SerializeInto(Buffer);
            NodesToWrite.SerializeInto(Buffer);

            const size_t AfterSize = Buffer.size();
            const size_t TotalSize = AfterSize - BeforeSize;
            std::memcpy(Buffer.data() + BeforeSize, Magic, sizeof(Magic));
            const auto TotalSizeU32 = uint32_t(TotalSize);
            std::memcpy(Buffer.data() + BeforeSize + 4, (void*)&TotalSizeU32,
                sizeof(TotalSizeU32));
        }
    };

    struct FindServersRequest_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::FindServersRequest);
        RequestHeader_t RequestHeader;
        String_t EndpointUrl;
        Array_t<String_t> LocaleIds;
        Array_t<String_t> ServerUris;

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            const size_t BeforeSize = Buffer.size();
            Buffer.resize(Buffer.size() + 8);
            ChannelHeader.SerializeInto(Buffer);
            ServiceNodeId.SerializeInto(Buffer);
            RequestHeader.SerializeInto(Buffer);
            EndpointUrl.SerializeInto(Buffer);
            LocaleIds.SerializeInto(Buffer);
            ServerUris.SerializeInto(Buffer);

            const size_t AfterSize = Buffer.size();
            const size_t TotalSize = AfterSize - BeforeSize;
            std::memcpy(Buffer.data() + BeforeSize, Magic, sizeof(Magic));
            const auto TotalSizeU32 = uint32_t(TotalSize);
            std::memcpy(Buffer.data() + BeforeSize + 4, (void*)&TotalSizeU32,
                sizeof(TotalSizeU32));
        }
    };

    struct CloseSessionRequest_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::CloseSessionRequest);
        RequestHeader_t RequestHeader;
        Uint8_t DeleteSubscriptions;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}CloseSessionRequest_t\n", " ", Spaces);
            fmt::print("{:{}}  ChannelHeader:\n", " ", Spaces);
            ChannelHeader.Print(Spaces + 4);
            fmt::print("{:{}}  ServiceNodeId:\n", " ", Spaces);
            ServiceNodeId.Print(Spaces + 4);
            fmt::print("{:{}}  RequestHeader:\n", " ", Spaces);
            RequestHeader.Print(Spaces + 4);
            fmt::print("{:{}}  DeleteSubscriptions:", " ", Spaces);
            DeleteSubscriptions.Print(1);
        }

        void DeserializeFrom(const std::vector<uint8_t>& RawBuffer) {
            Deserializer_t Buffer(RawBuffer);
            std::array<uint8_t, 4> Magic;
            Buffer.Read(Magic.data(), sizeof(Magic));
            if (std::memcmp(Magic.data(), "MSGF", Magic.size()) != 0) {
                throw std::runtime_error("expected an open packet to deserialize");
            }
            Buffer.Advance(4);

            ChannelHeader.DeserializeFrom(Buffer);
            ServiceNodeId.DeserializeFrom(Buffer);
            if (ServiceNodeId.NamespaceIndex.Value != 0) {
                throw std::runtime_error("close session request expect namespace 0");
            }

            if (std::get<Uint32_t>(ServiceNodeId.Identifier).Value !=
                KnownNodeIds_t::CloseSessionRequest) {
                throw std::runtime_error("close session request expect correct nodeid");
            }

            RequestHeader.DeserializeFrom(Buffer);
            DeleteSubscriptions.DeserializeFrom(Buffer);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            const size_t BeforeSize = Buffer.size();
            Buffer.resize(Buffer.size() + 8);
            ChannelHeader.SerializeInto(Buffer);
            ServiceNodeId.SerializeInto(Buffer);
            RequestHeader.SerializeInto(Buffer);
            DeleteSubscriptions.SerializeInto(Buffer);

            const size_t AfterSize = Buffer.size();
            const size_t TotalSize = AfterSize - BeforeSize;
            std::memcpy(Buffer.data() + BeforeSize, Magic, sizeof(Magic));
            const auto TotalSizeU32 = uint32_t(TotalSize);
            std::memcpy(Buffer.data() + BeforeSize + 4, (void*)&TotalSizeU32,
                sizeof(TotalSizeU32));
        }
    };

    struct CancelRequest_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::CancelRequest);
        RequestHeader_t RequestHeader;
        Uint32_t RequestHandle;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}CancelRequest_t\n", " ", Spaces);
            fmt::print("{:{}}  ChannelHeader:\n", " ", Spaces);
            ChannelHeader.Print(Spaces + 4);
            fmt::print("{:{}}  ServiceNodeId:\n", " ", Spaces);
            ServiceNodeId.Print(Spaces + 4);
            fmt::print("{:{}}  RequestHeader:\n", " ", Spaces);
            RequestHeader.Print(Spaces + 4);
            fmt::print("{:{}}  RequestHandle:", " ", Spaces);
            RequestHandle.Print(1);
        }

        void DeserializeFrom(const std::vector<uint8_t>& RawBuffer) {
            Deserializer_t Buffer(RawBuffer);
            std::array<uint8_t, 4> Magic;
            Buffer.Read(Magic.data(), sizeof(Magic));
            if (std::memcmp(Magic.data(), "MSGF", Magic.size()) != 0) {
                throw std::runtime_error("expected an open packet to deserialize");
            }
            Buffer.Advance(4);

            ChannelHeader.DeserializeFrom(Buffer);
            ServiceNodeId.DeserializeFrom(Buffer);
            if (ServiceNodeId.NamespaceIndex.Value != 0) {
                throw std::runtime_error("cancel request expect namespace 0");
            }

            if (std::get<Uint32_t>(ServiceNodeId.Identifier).Value !=
                KnownNodeIds_t::CancelRequest) {
                throw std::runtime_error("cancel request expect correct nodeid");
            }

            RequestHeader.DeserializeFrom(Buffer);
            RequestHandle.DeserializeFrom(Buffer);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            const size_t BeforeSize = Buffer.size();
            Buffer.resize(Buffer.size() + 8);
            ChannelHeader.SerializeInto(Buffer);
            ServiceNodeId.SerializeInto(Buffer);
            RequestHeader.SerializeInto(Buffer);
            RequestHandle.SerializeInto(Buffer);

            const size_t AfterSize = Buffer.size();
            const size_t TotalSize = AfterSize - BeforeSize;
            std::memcpy(Buffer.data() + BeforeSize, Magic, sizeof(Magic));
            const auto TotalSizeU32 = uint32_t(TotalSize);
            std::memcpy(Buffer.data() + BeforeSize + 4, (void*)&TotalSizeU32,
                sizeof(TotalSizeU32));
        }
    };

    struct AddNodesRequest_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::AddNodesRequest);
        RequestHeader_t RequestHeader;
        Array_t<AddNodesItem_t> NodesToAdd;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}AddNodesRequest_t\n", " ", Spaces);
            fmt::print("{:{}}  ChannelHeader:\n", " ", Spaces);
            ChannelHeader.Print(Spaces + 4);
            fmt::print("{:{}}  ServiceNodeId:\n", " ", Spaces);
            ServiceNodeId.Print(Spaces + 4);
            fmt::print("{:{}}  RequestHeader:\n", " ", Spaces);
            RequestHeader.Print(Spaces + 4);
            fmt::print("{:{}}  NodesToAdd:\n", " ", Spaces);
            NodesToAdd.Print(Spaces + 4);
        }

        void DeserializeFrom(const std::vector<uint8_t>& RawBuffer) {
            Deserializer_t Buffer(RawBuffer);
            std::array<uint8_t, 4> Magic;
            Buffer.Read(Magic.data(), sizeof(Magic));
            if (std::memcmp(Magic.data(), "MSGF", Magic.size()) != 0) {
                throw std::runtime_error("expected an open packet to deserialize");
            }
            Buffer.Advance(4);

            ChannelHeader.DeserializeFrom(Buffer);
            ServiceNodeId.DeserializeFrom(Buffer);
            if (ServiceNodeId.NamespaceIndex.Value != 0) {
                throw std::runtime_error("add nodes request expect namespace 0");
            }

            if (std::get<Uint32_t>(ServiceNodeId.Identifier).Value !=
                KnownNodeIds_t::AddNodesRequest) {
                throw std::runtime_error("add nodes request expect correct nodeid");
            }

            RequestHeader.DeserializeFrom(Buffer);
            NodesToAdd.DeserializeFrom(Buffer);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            const size_t BeforeSize = Buffer.size();
            Buffer.resize(Buffer.size() + 8);
            ChannelHeader.SerializeInto(Buffer);
            ServiceNodeId.SerializeInto(Buffer);
            RequestHeader.SerializeInto(Buffer);
            NodesToAdd.SerializeInto(Buffer);

            const size_t AfterSize = Buffer.size();
            const size_t TotalSize = AfterSize - BeforeSize;
            std::memcpy(Buffer.data() + BeforeSize, Magic, sizeof(Magic));
            const auto TotalSizeU32 = uint32_t(TotalSize);
            std::memcpy(Buffer.data() + BeforeSize + 4, (void*)&TotalSizeU32,
                sizeof(TotalSizeU32));
        }
    };

    struct BrowseRequest_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::BrowseRequest);
        RequestHeader_t RequestHeader;
        ViewDescription_t View;
        Uint32_t RequestedMaxReferencesPerNode;
        Array_t<BrowseDescription_t> NodesToBrowse;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}BrowseRequest_t\n", " ", Spaces);
            fmt::print("{:{}}  ChannelHeader:\n", " ", Spaces);
            ChannelHeader.Print(Spaces + 4);
            fmt::print("{:{}}  ServiceNodeId:\n", " ", Spaces);
            ServiceNodeId.Print(Spaces + 4);
            fmt::print("{:{}}  RequestHeader:\n", " ", Spaces);
            RequestHeader.Print(Spaces + 4);
            fmt::print("{:{}}  View:\n", " ", Spaces);
            View.Print(Spaces + 4);
            fmt::print("{:{}}  RequestedMaxReferencesPerNode:", " ", Spaces);
            RequestedMaxReferencesPerNode.Print(1);
            fmt::print("{:{}}  NodesToBrowse:\n", " ", Spaces);
            NodesToBrowse.Print(Spaces + 4);
        }

        void DeserializeFrom(const std::vector<uint8_t>& RawBuffer) {
            Deserializer_t Buffer(RawBuffer);
            std::array<uint8_t, 4> Magic;
            Buffer.Read(Magic.data(), sizeof(Magic));
            if (std::memcmp(Magic.data(), "MSGF", Magic.size()) != 0) {
                throw std::runtime_error("expected an open packet to deserialize");
            }
            Buffer.Advance(4);

            ChannelHeader.DeserializeFrom(Buffer);
            ServiceNodeId.DeserializeFrom(Buffer);
            if (ServiceNodeId.NamespaceIndex.Value != 0) {
                throw std::runtime_error("browse request expect namespace 0");
            }

            if (std::get<Uint32_t>(ServiceNodeId.Identifier).Value !=
                KnownNodeIds_t::BrowseRequest) {
                throw std::runtime_error("browse request expect correct nodeid");
            }

            RequestHeader.DeserializeFrom(Buffer);
            View.DeserializeFrom(Buffer);
            RequestedMaxReferencesPerNode.DeserializeFrom(Buffer);
            NodesToBrowse.DeserializeFrom(Buffer);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            const size_t BeforeSize = Buffer.size();
            Buffer.resize(Buffer.size() + 8);
            ChannelHeader.SerializeInto(Buffer);
            ServiceNodeId.SerializeInto(Buffer);
            RequestHeader.SerializeInto(Buffer);
            View.SerializeInto(Buffer);
            RequestedMaxReferencesPerNode.SerializeInto(Buffer);
            NodesToBrowse.SerializeInto(Buffer);

            const size_t AfterSize = Buffer.size();
            const size_t TotalSize = AfterSize - BeforeSize;
            std::memcpy(Buffer.data() + BeforeSize, Magic, sizeof(Magic));
            const auto TotalSizeU32 = uint32_t(TotalSize);
            std::memcpy(Buffer.data() + BeforeSize + 4, (void*)&TotalSizeU32,
                sizeof(TotalSizeU32));
        }
    };

    struct BrowseNextRequest_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::BrowseNextRequest);
        RequestHeader_t RequestHeader;
        Uint8_t ReleaseContinuationPoints;
        Array_t<ByteString_t> ContinuationPoints;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}BrowseRequest_t\n", " ", Spaces);
            fmt::print("{:{}}  ChannelHeader:\n", " ", Spaces);
            ChannelHeader.Print(Spaces + 4);
            fmt::print("{:{}}  ServiceNodeId:\n", " ", Spaces);
            ServiceNodeId.Print(Spaces + 4);
            fmt::print("{:{}}  RequestHeader:\n", " ", Spaces);
            RequestHeader.Print(Spaces + 4);
            fmt::print("{:{}}  ReleaseContinuationPoints:", " ", Spaces);
            ReleaseContinuationPoints.Print(1);
            fmt::print("{:{}}  ContinuationPoints:\n", " ", Spaces);
            ContinuationPoints.Print(Spaces + 4);
        }

        void DeserializeFrom(const std::vector<uint8_t>& RawBuffer) {
            Deserializer_t Buffer(RawBuffer);
            std::array<uint8_t, 4> Magic;
            Buffer.Read(Magic.data(), sizeof(Magic));
            if (std::memcmp(Magic.data(), "MSGF", Magic.size()) != 0) {
                throw std::runtime_error("expected an open packet to deserialize");
            }
            Buffer.Advance(4);

            ChannelHeader.DeserializeFrom(Buffer);
            ServiceNodeId.DeserializeFrom(Buffer);
            if (ServiceNodeId.NamespaceIndex.Value != 0) {
                throw std::runtime_error("browse request expect namespace 0");
            }

            if (std::get<Uint32_t>(ServiceNodeId.Identifier).Value !=
                KnownNodeIds_t::BrowseNextRequest) {
                throw std::runtime_error("browse next request expect correct nodeid");
            }

            RequestHeader.DeserializeFrom(Buffer);
            ReleaseContinuationPoints.DeserializeFrom(Buffer);
            ContinuationPoints.DeserializeFrom(Buffer);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            const size_t BeforeSize = Buffer.size();
            Buffer.resize(Buffer.size() + 8);
            ChannelHeader.SerializeInto(Buffer);
            ServiceNodeId.SerializeInto(Buffer);
            RequestHeader.SerializeInto(Buffer);
            ReleaseContinuationPoints.SerializeInto(Buffer);
            ContinuationPoints.SerializeInto(Buffer);

            const size_t AfterSize = Buffer.size();
            const size_t TotalSize = AfterSize - BeforeSize;
            std::memcpy(Buffer.data() + BeforeSize, Magic, sizeof(Magic));
            const auto TotalSizeU32 = uint32_t(TotalSize);
            std::memcpy(Buffer.data() + BeforeSize + 4, (void*)&TotalSizeU32,
                sizeof(TotalSizeU32));
        }
    };

    struct CallRequest_t {
        static constexpr uint8_t Magic[4] = { 'M', 'S', 'G', 'F' };
        SecureChannelHeader_t ChannelHeader;
        NodeId_t ServiceNodeId = NodeId_t(KnownNodeIds_t::CallRequest);
        RequestHeader_t RequestHeader;
        Array_t<CallMethodRequest_t> MethodsToCall;

        void Print(const size_t Spaces = 2) const {
            fmt::print("{:{}}CallRequest_t\n", " ", Spaces);
            fmt::print("{:{}}  ChannelHeader:\n", " ", Spaces);
            ChannelHeader.Print(Spaces + 4);
            fmt::print("{:{}}  ServiceNodeId:\n", " ", Spaces);
            ServiceNodeId.Print(Spaces + 4);
            fmt::print("{:{}}  RequestHeader:\n", " ", Spaces);
            RequestHeader.Print(Spaces + 4);
            fmt::print("{:{}}  MethodsToCall:\n", " ", Spaces);
            MethodsToCall.Print(Spaces + 4);
        }

        void DeserializeFrom(const std::vector<uint8_t>& RawBuffer) {
            Deserializer_t Buffer(RawBuffer);
            std::array<uint8_t, 4> Magic;
            Buffer.Read(Magic.data(), sizeof(Magic));
            if (std::memcmp(Magic.data(), "MSGF", Magic.size()) != 0) {
                throw std::runtime_error("expected an open packet to deserialize");
            }
            Buffer.Advance(4);

            ChannelHeader.DeserializeFrom(Buffer);
            ServiceNodeId.DeserializeFrom(Buffer);
            if (ServiceNodeId.NamespaceIndex.Value != 0) {
                throw std::runtime_error("browse request expect namespace 0");
            }

            if (std::get<Uint32_t>(ServiceNodeId.Identifier).Value !=
                KnownNodeIds_t::CallRequest) {
                throw std::runtime_error("call request expect correct nodeid");
            }

            RequestHeader.DeserializeFrom(Buffer);
            MethodsToCall.DeserializeFrom(Buffer);
        }

        void SerializeInto(std::vector<uint8_t>& Buffer) const {
            const size_t BeforeSize = Buffer.size();
            Buffer.resize(Buffer.size() + 8);
            ChannelHeader.SerializeInto(Buffer);
            ServiceNodeId.SerializeInto(Buffer);
            RequestHeader.SerializeInto(Buffer);
            MethodsToCall.SerializeInto(Buffer);

            const size_t AfterSize = Buffer.size();
            const size_t TotalSize = AfterSize - BeforeSize;
            std::memcpy(Buffer.data() + BeforeSize, Magic, sizeof(Magic));
            const auto TotalSizeU32 = uint32_t(TotalSize);
            std::memcpy(Buffer.data() + BeforeSize + 4, (void*)&TotalSizeU32,
                sizeof(TotalSizeU32));
        }
    };

    static Serializable_t BuildEncodeableType(const NodeId_t Id) {
        static const auto Types = []() {
            std::map<NodeId_t, SerializableBuilder_t> Types;
#define ADD_TYPE(Type)                                                         \
  { Types.emplace(Type::Id(), Type()); }

            ADD_TYPE(NodeAttributes_t);
            ADD_TYPE(ObjectAttributes_t);
            ADD_TYPE(VariableAttributes_t);
            ADD_TYPE(MethodAttributes_t);
            ADD_TYPE(ObjectTypeAttributes_t);
            ADD_TYPE(VariableTypeAttributes_t);
            ADD_TYPE(ReferenceTypeAttributes_t);
            ADD_TYPE(DataTypeAttributes_t);
            ADD_TYPE(ViewAttributes_t);
            ADD_TYPE(AnonymousIdentityToken_t);
            return Types;
        }();

        return Types.at(Id).New();
    }

    namespace helpers {

        struct Header_t {
            std::array<uint8_t, 4> Magic;
            uint32_t MessageSize = 0;
            SecureChannelHeader_t ChannelHeader;
        };

        static_assert(sizeof(Header_t) == 24,
            "The size of the secure header isn't right");

        std::vector<uint8_t> Defragment(const uint8_t* Packet,
            const size_t PacketSize) {
            std::vector<uint8_t> Defragmented;
            size_t Remaining = PacketSize;
            const uint8_t* ReadPtr = Packet;
            Header_t Header;
            bool First = true;
            size_t TotalMessageSize = 0;
            while (Remaining != 0) {
                if (Remaining < sizeof(Header)) {
                    throw std::runtime_error("issue somewhere");
                }

                //
                // Grab a header.
                //

                std::memcpy(&Header, ReadPtr, sizeof(Header));

                //
                // Keep track of the total size of the packet.
                //

                TotalMessageSize += Header.MessageSize;

                //
                // If the remaining amount is equal to the current message, then it
                // means that this is the last chunk (or that the packet isn't
                // fragmented)
                //

                if (Header.MessageSize == Remaining) {

                    if (First) {

                        //
                        // The packet is actually not fragmented..which means we need to
                        // append the header.
                        //

                        Defragmented.insert(Defragmented.end(), ReadPtr,
                            ReadPtr + sizeof(Header_t));
                    }

                    //
                    // Append the packet content.
                    //

                    ReadPtr += sizeof(Header);
                    Remaining -= sizeof(Header);

                    Defragmented.insert(Defragmented.end(), ReadPtr, ReadPtr + Remaining);

                    //
                    // Fix up the size.
                    //

                    auto* NewHeader = (Header_t*)Defragmented.data();
                    NewHeader->MessageSize = uint32_t(TotalMessageSize);

                    //
                    // Fix up the magic.
                    //

                    NewHeader->Magic.back() = 'F';

                    //
                    // We're done son!
                    //

                    break;
                }

                Remaining -= Header.MessageSize;
                ReadPtr += Header.MessageSize;
                First = false;
            }

            return Defragmented;
        }

        std::vector<uint8_t> Defragment(const std::vector<uint8_t> Packet) {
            return Defragment(Packet.data(), Packet.size());
        }

        std::vector<std::vector<uint8_t>>
            Fragment(const std::vector<uint8_t>& Packet,
                const size_t MaxFragmentSize = 0x2'000) {
            Header_t Header;
            const size_t HeaderSize = sizeof(Header);
            const size_t MaxUseableFragmentSize = MaxFragmentSize - HeaderSize;

            std::vector<std::vector<uint8_t>> Fragments;

            //
            // This tracks the amount that remains to be fragmented, and the offset we
            // read from in the original packet.
            //

            size_t Remaining = Packet.size();
            size_t Offset = 0;

            //
            // For the first fragment, we simply copy the header that is in the
            // original packet.
            //

            std::memcpy(&Header, Packet.data(), sizeof(Header));

            //
            // Reflect that into the Remaining / Offset variables.
            //

            Remaining -= sizeof(Header);
            Offset += sizeof(Header);

            //
            // Loop until there's data to put in fragments.
            //

            while (Remaining > 0) {

                //
                // How much data can we put in this fragment?
                //

                const size_t FragmentSize = std::min(MaxUseableFragmentSize, Remaining);
                Remaining -= FragmentSize;

                //
                // We need to let the server know if this is a regular chunk or the
                // final one.
                //

                const bool IsFinal = Remaining == 0;
                Header.Magic.back() = IsFinal ? 'F' : 'C';

                //
                // Update the size of the fragment.
                //

                const size_t TotalFragmentSize = FragmentSize + HeaderSize;
                Header.MessageSize = uint32_t(TotalFragmentSize);

                //
                // Calculate the total size of the fragment.
                //

                std::vector<uint8_t> Fragment(TotalFragmentSize);

                //
                // Write the header followed by the fragment's content.
                //

                std::memcpy(Fragment.data(), &Header, sizeof(Header));
                std::memcpy(Fragment.data() + sizeof(Header), Packet.data() + Offset,
                    FragmentSize);
                Offset += FragmentSize;

                //
                // Increment the sequence number.
                //

                Header.ChannelHeader.SecureSequenceNumber++;

                //
                // Woot, move the fragment into the final vector.
                //

                Fragments.push_back(std::move(Fragment));
            }

            if (Fragments.size() > 256) {
                throw std::runtime_error("Too many chunks!");
            }

            return Fragments;
        }

        template <typename Message_t>
        std::vector<std::vector<uint8_t>>
            Fragment(const Message_t& Msg, const size_t MaxFragmentSize = 0x2'000) {
            std::vector<uint8_t> Buffer;
            Msg.SerializeInto(Buffer);
            return Fragment(Buffer, MaxFragmentSize);
        }
    } // namespace helpers
} // namespace oua