/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2017-Present Couchbase, Inc.
 *
 *   Use of this software is governed by the Business Source License included
 *   in the file licenses/BSL-Couchbase.txt.  As of the Change Date specified
 *   in that file, in accordance with the Business Source License, use of this
 *   software will be governed by the Apache License, Version 2.0, included in
 *   the file licenses/APL2.txt.
 */
#pragma once

#include <gsl/gsl-lite.hpp>
#include <mcbp/protocol/dcp_stream_end_status.h>
#include <mcbp/protocol/status.h>
#include <memcached/dcp_stream_id.h>
#include <memcached/engine_error.h>
#include <memcached/types.h>
#include <memcached/vbucket.h>
#include <memcached/visibility.h>

class CookieIface;
struct DocKey;

namespace cb::durability {
class Requirements;
enum class Level : uint8_t;
} // namespace cb::durability

namespace cb::mcbp {
class Response;
}

namespace mcbp::systemevent {
enum class id : uint32_t;
enum class version : uint8_t;
} // namespace mcbp::systemevent

class DcpConnHandlerIface {
public:
    virtual ~DcpConnHandlerIface() = default;
};

/**
 * The message producers are used by the engine's DCP producer
 * to add messages into the DCP stream.  Please look at the full
 * DCP documentation to figure out the real meaning for all of the
 * messages.
 */
struct DcpMessageProducersIface {
    virtual ~DcpMessageProducersIface() = default;

    virtual cb::engine_errc get_failover_log(uint32_t opaque, Vbid vbucket) = 0;

    virtual cb::engine_errc stream_req(uint32_t opaque,
                                       Vbid vbucket,
                                       uint32_t flags,
                                       uint64_t start_seqno,
                                       uint64_t end_seqno,
                                       uint64_t vbucket_uuid,
                                       uint64_t snap_start_seqno,
                                       uint64_t snap_end_seqno,
                                       const std::string& request_value) = 0;

    virtual cb::engine_errc add_stream_rsp(uint32_t opaque,
                                           uint32_t stream_opaque,
                                           cb::mcbp::Status status) = 0;

    virtual cb::engine_errc marker_rsp(uint32_t opaque,
                                       cb::mcbp::Status status) = 0;

    virtual cb::engine_errc set_vbucket_state_rsp(uint32_t opaque,
                                                  cb::mcbp::Status status) = 0;

    /**
     * Send a Stream End message
     *
     * @param opaque this is the opaque requested by the consumer
     *               in the Stream Request message
     * @param vbucket the vbucket id the message belong to
     * @param status the reason for the stream end.
     *              0 = success
     *              1+ = Something happened on the vbucket causing
     *                   us to abort it.
     * @param sid The stream-ID the end applies to (can be 0 for none)
     *
     * @return cb::engine_errc::success upon success
     *         cb::engine_errc::would_block if no data is available
     *         ENGINE_* for errors
     */
    virtual cb::engine_errc stream_end(uint32_t opaque,
                                       Vbid vbucket,
                                       cb::mcbp::DcpStreamEndStatus status,
                                       cb::mcbp::DcpStreamId sid) = 0;

    /**
     * Send a marker
     *
     * @param opaque this is the opaque requested by the consumer
     *               in the Stream Request message
     * @param vbucket the vbucket id the message belong to
     * @param start_seqno start of the snapshot range
     * @param end_seqno end of the snapshot range
     * @param flags snapshot marker flags (DISK/MEMORY/CHK/ACK).
     * @param highCompletedSeqno the SyncRepl high completed seqno
     * @param maxVisibleSeqno highest committed seqno (ignores prepare/abort)
     * @param timestamp for the data in the snapshot marker (only valid for
     *                  disk type and represents the disk commit time)
     * @param sid The stream-ID the marker applies to (can be 0 for none)
     *
     * @return cb::engine_errc::success upon success
     */
    virtual cb::engine_errc marker(uint32_t opaque,
                                   Vbid vbucket,
                                   uint64_t start_seqno,
                                   uint64_t end_seqno,
                                   uint32_t flags,
                                   std::optional<uint64_t> highCompletedSeqno,
                                   std::optional<uint64_t> maxVisibleSeqno,
                                   std::optional<uint64_t> timestamp,
                                   cb::mcbp::DcpStreamId sid) = 0;

    /**
     * Send a Mutation
     *
     * @param opaque this is the opaque requested by the consumer
     *               in the Stream Request message
     * @param itm the item to send.
     * @param vbucket the vbucket id the message belong to
     * @param by_seqno
     * @param rev_seqno
     * @param lock_time
     * @param nru the nru field used by ep-engine (may safely be ignored)
     * @param sid The stream-ID the mutation applies to (can be 0 for none)
     *
     * @return cb::engine_errc::success upon success
     */
    virtual cb::engine_errc mutation(uint32_t opaque,
                                     cb::unique_item_ptr itm,
                                     Vbid vbucket,
                                     uint64_t by_seqno,
                                     uint64_t rev_seqno,
                                     uint32_t lock_time,
                                     uint8_t nru,
                                     cb::mcbp::DcpStreamId sid) = 0;

    /**
     * Send a deletion
     *
     * @param opaque this is the opaque requested by the consumer
     *               in the Stream Request message
     * @param itm the item to send.
     * @param by_seqno
     * @param rev_seqno
     * @param sid The stream-ID the deletion applies to (can be 0 for none)
     *
     * @return cb::engine_errc::success upon success
     */
    virtual cb::engine_errc deletion(uint32_t opaque,
                                     cb::unique_item_ptr itm,
                                     Vbid vbucket,
                                     uint64_t by_seqno,
                                     uint64_t rev_seqno,
                                     cb::mcbp::DcpStreamId sid) = 0;

    /**
     * Send a deletion with delete_time or collections (or both)
     *
     * @param opaque this is the opaque requested by the consumer
     *               in the Stream Request message
     * @param itm the item to send.
     * @param vbucket the vbucket id the message belong to
     * @param by_seqno
     * @param rev_seqno
     * @param delete_time the time of the deletion (tombstone creation time)
     * @param sid The stream-ID the deletion applies to (can be 0 for none)
     *
     * @return cb::engine_errc::success upon success
     */
    virtual cb::engine_errc deletion_v2(uint32_t opaque,
                                        cb::unique_item_ptr itm,
                                        Vbid vbucket,
                                        uint64_t by_seqno,
                                        uint64_t rev_seqno,
                                        uint32_t delete_time,
                                        cb::mcbp::DcpStreamId sid) = 0;

    /**
     * Send an expiration
     *
     * @param opaque this is the opaque requested by the consumer
     *               in the Stream Request message
     * @param itm the item to send.
     * @param by_seqno
     * @param rev_seqno
     * @param delete_time the time of the deletion (tombstone creation time)
     * @param sid The stream-ID the expiration applies to (can be 0 for none)
     *
     * @return cb::engine_errc::success upon success
     */
    virtual cb::engine_errc expiration(uint32_t opaque,
                                       cb::unique_item_ptr itm,
                                       Vbid vbucket,
                                       uint64_t by_seqno,
                                       uint64_t rev_seqno,
                                       uint32_t delete_time,
                                       cb::mcbp::DcpStreamId sid) = 0;

    /**
     * Send a state transition for a vbucket
     *
     * @param opaque this is the opaque requested by the consumer
     *               in the Stream Request message
     * @param vbucket the vbucket id the message belong to
     * @param state the new state
     *
     * @return cb::engine_errc::success upon success
     */
    virtual cb::engine_errc set_vbucket_state(uint32_t opaque,
                                              Vbid vbucket,
                                              vbucket_state_t state) = 0;

    /**
     * Send a noop
     *
     * @param opaque what to use as the opaque in the buffer
     *
     * @return cb::engine_errc::success upon success
     */
    virtual cb::engine_errc noop(uint32_t opaque) = 0;

    /**
     * Send a buffer acknowledgment
     *
     * @param opaque this is the opaque requested by the consumer
     *               in the Stream Request message
     * @param vbucket the vbucket id the message belong to
     * @param buffer_bytes the amount of bytes processed
     *
     * @return cb::engine_errc::success upon success
     */
    virtual cb::engine_errc buffer_acknowledgement(uint32_t opaque,
                                                   Vbid vbucket,
                                                   uint32_t buffer_bytes) = 0;

    /**
     * Send a control message to the other end
     *
     * @param opaque what to use as the opaque in the buffer
     * @param key the identifier for the property to set
     * @param value The value for the property (the layout of the
     *              value is defined for the key)
     *
     * @return cb::engine_errc::success upon success
     */
    virtual cb::engine_errc control(uint32_t opaque,
                                    std::string_view key,
                                    std::string_view value) = 0;

    /**
     * Send a system event message to the other end
     *
     * @param cookie passed on the cookie provided by step
     * @param opaque what to use as the opaque in the buffer
     * @param vbucket the vbucket the event applies to
     * @param bySeqno the sequence number of the event
     * @param version A version value defining the eventData format
     * @param key the system event's key data
     * @param eventData the system event's specific data
     * @param sid The stream-ID the event applies to (can be 0 for none)
     *
     * @return cb::engine_errc::success upon success
     */
    virtual cb::engine_errc system_event(uint32_t opaque,
                                         Vbid vbucket,
                                         mcbp::systemevent::id event,
                                         uint64_t bySeqno,
                                         mcbp::systemevent::version version,
                                         cb::const_byte_buffer key,
                                         cb::const_byte_buffer eventData,
                                         cb::mcbp::DcpStreamId sid) = 0;

    /*
     * Send a GetErrorMap message to the other end
     *
     * @param opaque The opaque to send over
     * @param version The version of the error map
     *
     * @return cb::engine_errc::success upon success
     */
    virtual cb::engine_errc get_error_map(uint32_t opaque,
                                          uint16_t version) = 0;

    /**
     * See mutation for a description of the parameters except for:
     *
     * @param deleted Are we storing a deletion operation?
     * @param durability the durability specification for this item
     */
    virtual cb::engine_errc prepare(uint32_t opaque,
                                    cb::unique_item_ptr itm,
                                    Vbid vbucket,
                                    uint64_t by_seqno,
                                    uint64_t rev_seqno,
                                    uint32_t lock_time,
                                    uint8_t nru,
                                    DocumentState document_state,
                                    cb::durability::Level level) = 0;

    /**
     * Send a seqno ack message
     *
     * It serves to inform KV-Engine active nodes that the replica has
     * successfully received and prepared to memory/disk all DCP_PREPARE
     * messages up to the specified seqno.
     *
     * @param opaque identifying stream
     * @param vbucket the vbucket the seqno ack is for
     * @param prepared_seqno The seqno the replica has prepared up to.
     */
    virtual cb::engine_errc seqno_acknowledged(uint32_t opaque,
                                               Vbid vbucket,
                                               uint64_t prepared_seqno) = 0;

    /**
     * Send a commit message:
     *
     * This is sent from the DCP Producer to the DCP Consumer.
     * It is only sent to DCP replicas, not GSI, FTS etc. It serves to inform
     * KV-Engine replicas of a committed Sync Write
     *
     * @param opaque
     * @param vbucket the vbucket the event applies to
     * @param key The key of the committed mutation.
     * @param prepare_seqno The seqno of the prepare that we are committing.
     * @param commit_seqno The sequence number to commit this mutation at.
     * @return
     */
    virtual cb::engine_errc commit(uint32_t opaque,
                                   Vbid vbucket,
                                   const DocKey& key,
                                   uint64_t prepare_seqno,
                                   uint64_t commit_seqno) = 0;
    /**
     * Send an abort message:
     *
     * This is sent from the DCP Producer to the DCP Consumer.
     */
    virtual cb::engine_errc abort(uint32_t opaque,
                                  Vbid vbucket,
                                  const DocKey& key,
                                  uint64_t prepared_seqno,
                                  uint64_t abort_seqno) = 0;
    /**
     * Send an OSO snapshot marker to from server to client
     */
    virtual cb::engine_errc oso_snapshot(uint32_t opaque,
                                         Vbid vbucket,
                                         uint32_t flags,
                                         cb::mcbp::DcpStreamId sid) = 0;

    virtual cb::engine_errc seqno_advanced(uint32_t opaque,
                                           Vbid vbucket,
                                           uint64_t seqno,
                                           cb::mcbp::DcpStreamId sid) = 0;
};

using dcp_add_failover_log =
        std::function<cb::engine_errc(const std::vector<vbucket_failover_t>&)>;

struct MEMCACHED_PUBLIC_CLASS DcpIface {
    /**
     * Called from the memcached core for a DCP connection to allow it to
     * inject new messages on the stream.
     *
     * @param cookie a unique handle the engine should pass on to the
     *               message producers
     * @param producers functions the client may use to add messages to
     *                  the DCP stream
     *
     * @return The appropriate error code returned from the message
     *         producerif it failed, or:
     *         cb::engine_errc::success if the engine don't have more messages
     *                        to send at this moment
     */
    virtual cb::engine_errc step(const CookieIface& cookie,
                                 DcpMessageProducersIface& producers) = 0;

    /**
     * Called from the memcached core to open a new DCP connection.
     *
     * @param cookie a unique handle the engine should pass on to the
     *               message producers (typically representing the memcached
     *               connection).
     * @param opaque what to use as the opaque for this DCP connection.
     * @param seqno Unused
     * @param flags bitfield of flags to specify what to open. See DCP_OPEN_XXX
     * @param name Identifier for this connection. Note that the name must be
     *             unique; attempting to (re)connect with a name already in use
     *             will disconnect the existing connection.
     * @param value An optional JSON value specifying extra information about
     *              the connection to be opened.
     * @return cb::engine_errc::success if the DCP connection was successfully
     * opened, otherwise error code indicating reason for the failure.
     */
    virtual cb::engine_errc open(const CookieIface& cookie,
                                 uint32_t opaque,
                                 uint32_t seqno,
                                 uint32_t flags,
                                 std::string_view name,
                                 std::string_view value = {}) = 0;

    /**
     * Called from the memcached core to add a vBucket stream to the set of
     * connected streams.
     *
     * @param cookie a unique handle the engine should pass on to the
     *               message producers (typically representing the memcached
     *               connection).
     * @param opaque what to use as the opaque for this DCP connection.
     * @param vbucket The vBucket to stream.
     * @param flags bitfield of flags to specify what to open. See
     *              DCP_ADD_STREAM_FLAG_XXX
     * @return cb::engine_errc::success if the DCP stream was successfully
     * opened, otherwise error code indicating reason for the failure.
     */
    virtual cb::engine_errc add_stream(const CookieIface& cookie,
                                       uint32_t opaque,
                                       Vbid vbucket,
                                       uint32_t flags) = 0;

    /**
     * Called from the memcached core to close a vBucket stream to the set of
     * connected streams.
     *
     * @param cookie a unique handle the engine should pass on to the
     *               message producers (typically representing the memcached
     *               connection).
     * @param opaque what to use as the opaque for this DCP connection.
     * @param vbucket The vBucket to close.
     * @param sid The id of the stream to close (can be 0/none)
     * @return
     */
    virtual cb::engine_errc close_stream(const CookieIface& cookie,
                                         uint32_t opaque,
                                         Vbid vbucket,
                                         cb::mcbp::DcpStreamId sid) = 0;

    /**
     * Callback to the engine that a Stream Request message was received
     */
    virtual cb::engine_errc stream_req(
            const CookieIface& cookie,
            uint32_t flags,
            uint32_t opaque,
            Vbid vbucket,
            uint64_t start_seqno,
            uint64_t end_seqno,
            uint64_t vbucket_uuid,
            uint64_t snap_start_seqno,
            uint64_t snap_end_seqno,
            uint64_t* rollback_seqno,
            dcp_add_failover_log callback,
            std::optional<std::string_view> json) = 0;

    /**
     * Callback to the engine that a get failover log message was received
     */
    virtual cb::engine_errc get_failover_log(const CookieIface& cookie,
                                             uint32_t opaque,
                                             Vbid vbucket,
                                             dcp_add_failover_log callback) = 0;

    /**
     * Callback to the engine that a stream end message was received
     */
    virtual cb::engine_errc stream_end(const CookieIface& cookie,
                                       uint32_t opaque,
                                       Vbid vbucket,
                                       cb::mcbp::DcpStreamEndStatus status) = 0;

    /**
     * Callback to the engine that a snapshot marker message was received
     */
    virtual cb::engine_errc snapshot_marker(
            const CookieIface& cookie,
            uint32_t opaque,
            Vbid vbucket,
            uint64_t start_seqno,
            uint64_t end_seqno,
            uint32_t flags,
            std::optional<uint64_t> high_completed_seqno,
            std::optional<uint64_t> max_visible_seqno) = 0;

    /**
     * Callback to the engine that a mutation message was received
     *
     * @param cookie The cookie representing the connection
     * @param opaque The opaque field in the message (identifying the stream)
     * @param key The documents key
     * @param value The value to store
     * @param priv_bytes The number of bytes in the value which should be
     *                   allocated from the privileged pool
     * @param datatype The datatype for the incomming item
     * @param cas The documents CAS value
     * @param vbucket The vbucket identifier for the document
     * @param flags The user specified flags
     * @param by_seqno The sequence number in the vbucket
     * @param rev_seqno The revision number for the item
     * @param expiration When the document expire
     * @param lock_time The lock time for the document
     * @param meta The documents meta
     * @param nru The engine's NRU value
     * @return Standard engine error code.
     */
    virtual cb::engine_errc mutation(const CookieIface& cookie,
                                     uint32_t opaque,
                                     const DocKey& key,
                                     cb::const_byte_buffer value,
                                     size_t priv_bytes,
                                     uint8_t datatype,
                                     uint64_t cas,
                                     Vbid vbucket,
                                     uint32_t flags,
                                     uint64_t by_seqno,
                                     uint64_t rev_seqno,
                                     uint32_t expiration,
                                     uint32_t lock_time,
                                     cb::const_byte_buffer meta,
                                     uint8_t nru) = 0;

    /**
     * Callback to the engine that a deletion message was received
     *
     * @param cookie The cookie representing the connection
     * @param opaque The opaque field in the message (identifying the stream)
     * @param key The documents key
     * @param value The value to store
     * @param priv_bytes The number of bytes in the value which should be
     *                   allocated from the privileged pool
     * @param datatype The datatype for the incomming item
     * @param cas The documents CAS value
     * @param vbucket The vbucket identifier for the document
     * @param by_seqno The sequence number in the vbucket
     * @param rev_seqno The revision number for the item
     * @param meta The documents meta
     * @return Standard engine error code.
     */
    virtual cb::engine_errc deletion(const CookieIface& cookie,
                                     uint32_t opaque,
                                     const DocKey& key,
                                     cb::const_byte_buffer value,
                                     size_t priv_bytes,
                                     uint8_t datatype,
                                     uint64_t cas,
                                     Vbid vbucket,
                                     uint64_t by_seqno,
                                     uint64_t rev_seqno,
                                     cb::const_byte_buffer meta) = 0;

    /**
     * Callback to the engine that a deletion_v2 message was received
     *
     * @param cookie The cookie representing the connection
     * @param opaque The opaque field in the message (identifying the stream)
     * @param key The documents key
     * @param value The value to store
     * @param priv_bytes The number of bytes in the value which should be
     *                   allocated from the privileged pool
     * @param datatype The datatype for the incomming item
     * @param cas The documents CAS value
     * @param vbucket The vbucket identifier for the document
     * @param by_seqno The sequence number in the vbucket
     * @param rev_seqno The revision number for the item
     * @param delete_time The time of the delete
     * @return Standard engine error code.
     */
    virtual cb::engine_errc deletion_v2(const CookieIface& cookie,
                                        uint32_t opaque,
                                        const DocKey& key,
                                        cb::const_byte_buffer value,
                                        size_t priv_bytes,
                                        uint8_t datatype,
                                        uint64_t cas,
                                        Vbid vbucket,
                                        uint64_t by_seqno,
                                        uint64_t rev_seqno,
                                        uint32_t delete_time) {
        return cb::engine_errc::not_supported;
    }

    /**
     * Callback to the engine that an expiration message was received
     *
     * @param cookie The cookie representing the connection
     * @param opaque The opaque field in the message (identifying the stream)
     * @param key The documents key
     * @param value The value to store
     * @param priv_bytes The number of bytes in the value which should be
     *                   allocated from the privileged pool
     * @param datatype The datatype for the incomming item
     * @param cas The documents CAS value
     * @param vbucket The vbucket identifier for the document
     * @param by_seqno The sequence number in the vbucket
     * @param rev_seqno The revision number for the item
     * @param meta The documents meta
     * @return Standard engine error code.
     */
    virtual cb::engine_errc expiration(const CookieIface& cookie,
                                       uint32_t opaque,
                                       const DocKey& key,
                                       cb::const_byte_buffer value,
                                       size_t priv_bytes,
                                       uint8_t datatype,
                                       uint64_t cas,
                                       Vbid vbucket,
                                       uint64_t by_seqno,
                                       uint64_t rev_seqno,
                                       uint32_t deleteTime) = 0;

    /**
     * Callback to the engine that a set vbucket state message was received
     */
    virtual cb::engine_errc set_vbucket_state(const CookieIface& cookie,
                                              uint32_t opaque,
                                              Vbid vbucket,
                                              vbucket_state_t state) = 0;

    /**
     * Callback to the engine that a NOOP message was received
     */
    virtual cb::engine_errc noop(const CookieIface& cookie,
                                 uint32_t opaque) = 0;

    /**
     * Callback to the engine that a buffer_ack message was received
     */
    virtual cb::engine_errc buffer_acknowledgement(const CookieIface& cookie,
                                                   uint32_t opaque,
                                                   Vbid vbucket,
                                                   uint32_t buffer_bytes) = 0;

    /**
     * Callback to the engine that a Control message was received.
     *
     * @param cookie The cookie representing the connection
     * @param opaque The opaque field in the message (identifying the stream)
     * @param key The control message name
     * @param value The control message value
     * @return Standard engine error code.
     */
    virtual cb::engine_errc control(const CookieIface& cookie,
                                    uint32_t opaque,
                                    std::string_view key,
                                    std::string_view value) = 0;

    /**
     * Callback to the engine that a response message has been received.
     * @param cookie The cookie representing the connection
     * @param response The response which the server received.
     * @return Standard engine error code.
     */
    virtual cb::engine_errc response_handler(
            const CookieIface& cookie, const cb::mcbp::Response& response) = 0;

    /**
     * Callback to the engine that a system event message was received.
     *
     * @param cookie The cookie representing the connection
     * @param opaque The opaque field in the message (identifying the stream)
     * @param vbucket The vbucket identifier for this event.
     * @param event The type of system event.
     * @param bySeqno Sequence number of event.
     * @param version The version of system event (defines the eventData format)
     * @param key The event name .
     * @param eventData The event value.
     * @return Standard engine error code.
     */
    virtual cb::engine_errc system_event(const CookieIface& cookie,
                                         uint32_t opaque,
                                         Vbid vbucket,
                                         mcbp::systemevent::id event,
                                         uint64_t bySeqno,
                                         mcbp::systemevent::version version,
                                         cb::const_byte_buffer key,
                                         cb::const_byte_buffer eventData) = 0;

    /**
     * Called by the core when it receives a DCP PREPARE message over the
     * wire.
     *
     * See mutation for a description of the parameters except for:
     *
     * @param deleted Are we storing a deletion operation?
     * @param durability the durability specification for this item
     */
    virtual cb::engine_errc prepare(const CookieIface& cookie,
                                    uint32_t opaque,
                                    const DocKey& key,
                                    cb::const_byte_buffer value,
                                    size_t priv_bytes,
                                    uint8_t datatype,
                                    uint64_t cas,
                                    Vbid vbucket,
                                    uint32_t flags,
                                    uint64_t by_seqno,
                                    uint64_t rev_seqno,
                                    uint32_t expiration,
                                    uint32_t lock_time,
                                    uint8_t nru,
                                    DocumentState document_state,
                                    cb::durability::Level level) = 0;

    /**
     * Called by the core when it receives a DCP SEQNO ACK message over the
     * wire.
     *
     * It serves to inform KV-Engine active nodes that the replica has
     * successfully received and prepared all DCP_PREPARE messages up to the
     * specified seqno.
     *
     * @param cookie connection to send it over
     * @param opaque identifying stream
     * @param vbucket The vbucket which is being acknowledged.
     * @param prepared_seqno The seqno the replica has prepared up to.
     */
    virtual cb::engine_errc seqno_acknowledged(const CookieIface& cookie,
                                               uint32_t opaque,
                                               Vbid vbucket,
                                               uint64_t prepared_seqno) = 0;

    /**
     * Called by the core when it receives a DCP COMMIT message over the
     * wire.
     *
     * This is sent from the DCP Producer to the DCP Consumer.
     * It is only sent to DCP replicas, not GSI, FTS etc. It serves to inform
     * KV-Engine replicas of a committed Sync Write
     */
    virtual cb::engine_errc commit(const CookieIface& cookie,
                                   uint32_t opaque,
                                   Vbid vbucket,
                                   const DocKey& key,
                                   uint64_t prepared_seqno,
                                   uint64_t commit_seqno) = 0;
    /**
     * Called by the core when it receives a DCP ABORT message over the
     * wire.
     *
     * This is sent from the DCP Producer to the DCP Consumer.
     */
    virtual cb::engine_errc abort(const CookieIface& cookie,
                                  uint32_t opaque,
                                  Vbid vbucket,
                                  const DocKey& key,
                                  uint64_t prepared_seqno,
                                  uint64_t abort_seqno) = 0;
};


/* -----------------------------------------------------------------------------
 * Copyright 2021 Jonathan Haigh
 * SPDX-License-Identifier: MIT
 * ---------------------------------------------------------------------------*/

#ifndef SQ_INCLUDE_GUARD_parser_TokenView_h_
#define SQ_INCLUDE_GUARD_parser_TokenView_h_

#include "core/Token.h"
#include "core/typeutil.h"

#include <gsl/gsl>
#include <optional>
#include <range/v3/view/facade.hpp>
#include <string_view>

namespace sq::parser {

/**
 * A ranges::view of the input query split into tokens.
 */
class TokenView : public ranges::view_facade<TokenView> {
public:
  explicit TokenView(std::string_view str) noexcept;

  TokenView() noexcept = default;
  TokenView(const TokenView &) noexcept = default;
  TokenView(TokenView &&) noexcept = default;
  TokenView &operator=(const TokenView &) noexcept = default;
  TokenView &operator=(TokenView &&) noexcept = default;
  ~TokenView() noexcept = default;

  // required for ranges::view_facade
  friend ranges::range_access;

  SQ_ND const Token &read() const;
  SQ_ND bool equal(ranges::default_sentinel_t other) const noexcept;
  void next();

private:
  gsl::index whitespace_length() const;

  std::string_view str_;
  gsl::index pos_ = 0;
  mutable std::optional<Token> cache_ = std::nullopt;
};

} // namespace sq::parser
#endif // SQ_INCLUDE_GUARD_parser_TokenView_h_


#pragma once

#include <cassert>
#include <functional>
#include <map>
#include <type_traits>
#include <vector>

#include <gsl/assert>

namespace xmol::utils {

class DeadObserverAccessError : public std::runtime_error {
public:
  using std::runtime_error::runtime_error;
};

template <typename Observer>
class DeadObserverAccessErrorT : public DeadObserverAccessError { /// todo: parametrize on error message instead
public:
  using DeadObserverAccessError::DeadObserverAccessError;
};

enum class ObserverState { ANY, ACTIVE, INVALID };

/// @brief Implements base primitives for observable entity
template <typename Observer> class Observable {
  static_assert(!std::is_reference<Observer>::value);
  static_assert(!std::is_pointer<Observer>::value);

public:
  Observable() = default;
  Observable(Observable&& rhs) noexcept = default;
  Observable(const Observable& rhs) = default;
  Observable& operator=(Observable&& rhs) noexcept = default;
  Observable& operator=(const Observable& rhs) = default;

protected:
  template <ObserverState apply_to = ObserverState::ANY, typename... Args, typename Func = void (Observer::*)(Args...)>
  void notify(Func func, Args&&... args) const {
    static_assert(apply_to != ObserverState::INVALID);
    for (auto& [observer, state] : observers) {
      if (state == ObserverState::ACTIVE) {
        std::invoke(func, observer, std::forward<Args>(args)...);
      } else {
        if (GSL_UNLIKELY(apply_to == ObserverState::ANY)) {
          throw DeadObserverAccessErrorT<Observer>("");
        }
      }
    }
  }

  void add_observer(Observer& ptr) const { observers.emplace(&ptr, ObserverState::ACTIVE); }

  void remove_observer(Observer& ptr) const {
    auto count = observers.erase(&ptr);
    static_cast<void>(count);
    assert(count == 1);
  }

  void clear_observers() const { observers.clear(); }

  /// @brief marks observer as invalid
  /// next broadcast notify would fire an exception
  void invalidate_observer(Observer& ptr) const {
    auto it = observers.find(&ptr);
    assert(it != observers.end());
    it->second = ObserverState::INVALID;
  }

  void move_observer(Observer& from, Observer& to) const {
    remove_observer(from);
    add_observer(to);
  }

public:

  void on_move(Observer& from, Observer& to) {
    remove_observer(from);
    add_observer(to);
  }

  void on_delete(Observer& o) { remove_observer(o); }
  void on_copy(Observer& o) { add_observer(o); }

protected:
  mutable std::map<Observer*, ObserverState> observers;
};

} // namespace xmol


/*
  Copyright [2017-2021] [IBM Corporation]
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
  http://www.apache.org/licenses/LICENSE-2.0
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#ifndef _MCAS_POOL_SESSION_H_
#define _MCAS_POOL_SESSION_H_

#include <gsl/pointers>
#include <memory>

class Pool_instance;

struct pool_session {
  pool_session(gsl::not_null<std::shared_ptr<Pool_instance>> ph) : pool(ph), canary(0x45450101) {
  }

  ~pool_session() {
  }

  bool check() const { return canary == 0x45450101; }
  gsl::not_null<std::shared_ptr<Pool_instance>> pool;
  const unsigned canary;
};

#endif


#pragma once
#include <gsl/gsl>
#include <d3d11.h>
#undef min
#undef max

namespace Halley {
	class DX11Video;

	class DX11Buffer {
    public:
		enum class Type
		{
			Vertex,
			Index,
			Constant
		};

        DX11Buffer(DX11Video& video, Type type, size_t initialSize = 0);
		DX11Buffer(DX11Buffer&& other) noexcept;
		~DX11Buffer();

		DX11Buffer(const DX11Buffer& other) = delete;

		DX11Buffer& operator=(const DX11Buffer& other) = delete;
		DX11Buffer& operator=(DX11Buffer&& other) = delete;

		void setData(gsl::span<const gsl::byte> data);
		ID3D11Buffer*& getBuffer();
		UINT getOffset() const;
		UINT getLastSize() const;
		bool canFit(size_t size) const;

		void reset();
		void clear();

	private:
		DX11Video& video;
		Type type;
		ID3D11Buffer* buffer = nullptr;
		size_t curSize = 0;
		size_t curPos = 0;
		size_t lastSize = 0;
		size_t lastPos = 0;
		bool waitingReset = false;

		void resize(size_t size);
	};
}


/****************************************************************************
 *                                                                          *
 *  Author : lukasz.iwaszkiewicz@gmail.com                                  *
 *  ~~~~~~~~                                                                *
 *  License : see COPYING file for details.                                 *
 *  ~~~~~~~~~                                                               *
 ****************************************************************************/

#pragma once
#include <gsl/gsl>

namespace sd {

void init ();
int lsdir (gsl::czstring path);

} // namespace sd


/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef dir_710838b0_50ee_47d4_bb24_95e5ded5be0b_h
#define dir_710838b0_50ee_47d4_bb24_95e5ded5be0b_h

#include <direct.h>
#include <io.h>
#include <gslib/type.h>

#ifdef _UNICODE

#define _gs_getcwd _wgetcwd
#define _gs_chdir _wchdir
#define _gs_mkdir _wmkdir
#define _gs_rmdir _wrmdir
#define _gs_finddata_t _wfinddata_t
#define _gs_findfirst _wfindfirst
#define _gs_findnext _wfindnext
#define _gs_findclose _findclose

#else

#define _gs_getcwd _getcwd
#define _gs_chdir _chdir
#define _gs_mkdir _mkdir
#define _gs_rmdir _rmdir
#define _gs_finddata_t _finddata_t
#define _gs_findfirst _findfirst
#define _gs_findnext _findnext
#define _gs_findclose _findclose

#endif

#endif


#pragma once

#include <gsl/gsl>

#include <vector>
#include <memory>

struct AVCodecContext;
struct AVPacket;
struct AVFrame;

namespace video_streamer
{

class FrameEncoder
{
public:
    explicit FrameEncoder(gsl::not_null<AVCodecContext*> codecContext);

    std::vector<unsigned char> encode(gsl::not_null<const AVFrame*> frame);
private:
    gsl::not_null<AVCodecContext*> m_codecContext;
    gsl::not_null<AVPacket*> m_packet;
};

std::shared_ptr<FrameEncoder> createFrameEncoder(
    const std::string& encoderName, gsl::not_null<const AVCodecContext*> codecContext);

} // video_streamer


#pragma once
/*
 * (C) Copyright 2020-2021 UCAR
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 */
/*! \defgroup ioda_cxx_attribute Attributes and Has_Attributes
 * \brief Ancillary data attached to variables and groups.
 * \ingroup ioda_cxx_api
 *
 * @{
 * \file Attribute.h
 * \brief @link ioda_cxx_attribute Interfaces @endlink for ioda::Attribute and related classes.
 */
#include <functional>
#include <gsl/gsl-lite.hpp>
#include <iostream>
#include <memory>
#include <string>
#include <valarray>
#include <vector>

#include "ioda/Exception.h"
#include "ioda/Misc/Dimensions.h"
#include "ioda/Misc/Eigen_Compat.h"
#include "ioda/Python/Att_ext.h"
#include "ioda/Types/Marshalling.h"
#include "ioda/Types/Type.h"
#include "ioda/Types/Type_Provider.h"
#include "ioda/defs.h"

namespace ioda {
class Attribute;

namespace detail {
class Attribute_Backend;
class Variable_Backend;

/// \brief Base class for Attributes
/// \ingroup ioda_cxx_attribute
///
/// \details You might wonder why we have this class
/// as a template. This is because we are using
/// a bit of compile-time template polymorphism to return
/// Attribute objects from base classes before Attribute is fully declared.
/// This is a variation of the (Curiously Recurring Template
/// Pattern)[https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern].
template <class Attribute_Implementation = Attribute>
class Attribute_Base {
protected:
  /// Using an opaque object to implement the backend.
  std::shared_ptr<Attribute_Backend> backend_;

  // Variable_Backend's derived classes do occasionally need access to the Attribute_Backend object.
  friend class Variable_Backend;

  /// @name General Functions
  /// @{

  Attribute_Base(std::shared_ptr<Attribute_Backend>);

public:
  virtual ~Attribute_Base();

  /// @}

  /// @name Writing Data
  /// \note Writing metadata is an all-or-nothing-process, unlike writing
  /// segments of data to a variable.
  /// \note Dimensions are fixed. Attribute are not resizable.
  /// @{
  ///

  /// \brief The fundamental write function. Backends overload this function to implement all write
  /// operations.
  ///
  /// \details This function writes a span of bytes (characters) to the backend attribute storage.
  /// No type conversions take place here (see the templated conversion function, below).
  ///
  /// \param data is a span of data.
  /// \param in_memory_datatype is an opaque (backend-level) object that describes the placement of
  ///   the data in memory. Usually ignorable - needed for complex data structures.
  /// \throws ioda::Exception if data has the wrong size.
  /// \returns The attribute (for chaining).
  virtual Attribute_Implementation write(gsl::span<const char> data, const Type& type);

  /// \brief Write data.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \tparam Marshaller is the class that serializes the data type into something that the backend
  /// library can use.
  /// \tparam TypeWrapper is a helper class that creates Type objects for the backend.
  /// \param data is a gsl::span (a pointer-length pair) that contains the data to be written.
  /// \param in_memory_dataType is the memory layout needed to parse data's type.
  /// \returns Another instance of this Attribute. Used for operation chaining.
  /// \throws ioda::Exception if data.size() does not match getDimensions().numElements.
  /// \see gsl::span for details of how to make a span.
  /// \see gsl::make_span
  template <class DataType, class Marshaller = ioda::Object_Accessor<DataType>,
            class TypeWrapper = Types::GetType_Wrapper<DataType>>
  Attribute_Implementation write(gsl::span<const DataType> data) {
    try {
      Marshaller m;
      auto d   = m.serialize(data);
      auto spn = gsl::make_span<const char>(reinterpret_cast<const char*>(d->DataPointers.data()),
                                            d->DataPointers.size() * Marshaller::bytesPerElement_);
      write(spn, TypeWrapper::GetType(getTypeProvider()));
      return Attribute_Implementation{backend_};
    } catch (...) {
      std::throw_with_nested(Exception(ioda_Here()));
    }
  }

  /// \brief Write data
  /// \note Normally the gsl::span write is fine. This one exists for easy Python binding.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \tparam Marshaller is the class that serializes the data type into something that the backend
  /// library can use.
  /// \tparam TypeWrapper is a helper class that creates Type objects for the backend.
  /// \param data is a std::vector that contains the data to be written.
  /// \param in_memory_dataType is the memory layout needed to parse data's type.
  /// \returns Another instance of this Attribute. Used for operation chaining.
  /// \throws ioda::Exception if data.size() does not match getDimensions().numElements.
  /// \see gsl::span for details of how to make a span.
  /// \see gsl::make_span for details on how to make a span.
  template <class DataType, class Marshaller = ioda::Object_Accessor<DataType>,
            class TypeWrapper = Types::GetType_Wrapper<DataType>>
  Attribute_Implementation write(const std::vector<DataType>& data) {
    std::vector<DataType> vd = data;
    return this->write<DataType, Marshaller, TypeWrapper>(gsl::make_span(vd));
  }

  /// \brief Write data.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \param data is an initializer list that contains the data to be written.
  /// \param in_memory_dataType is the memory layout needed to parse data's type.
  /// \returns Another instance of this Attribute. Used for operation chaining.
  /// \throws ioda::Exception if data.size() does not match getDimensions().numElements.
  /// \see gsl::span for details of how to make a span.
  template <class DataType>
  Attribute_Implementation write(std::initializer_list<DataType> data) {
    std::vector<DataType> v(data);
    return this->write<DataType>(gsl::make_span(v));
  }

  /// \brief Write a datum.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \param data is the data to be written.
  /// \returns Another instance of this Attribute. Used for operation chaining.
  /// \throws ioda::Exception if the Attribute dimensions are larger than a single point.
  template <class DataType>  //, class Marshaller = HH::Types::Object_Accessor<DataType> >
  Attribute_Implementation write(DataType data) {
    try {
      if (getDimensions().numElements != 1)
        throw Exception("Wrong number of elements. Use a different write() method.", ioda_Here());
      return write<DataType>(gsl::make_span<DataType>(&data, 1));
    } catch (...) {
      std::throw_with_nested(Exception(ioda_Here()));
    }
  }

  /// \brief Write an Eigen object (a Matrix, an Array, a Block, a Map).
  /// \tparam EigenClass is the Eigen object to write.
  /// \param d is the data to be written.
  /// \throws ioda::Exception on a dimension mismatch.
  /// \returns the attribute
  template <class EigenClass>
  Attribute_Implementation writeWithEigenRegular(const EigenClass& d) {
#if 1  //__has_include("Eigen/Dense")
    try {
      typedef typename EigenClass::Scalar ScalarType;
      // If d is already in Row Major form, then this is optimized out.
      Eigen::Array<ScalarType, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> dout;
      dout.resize(d.rows(), d.cols());
      dout               = d;
      const auto& dconst = dout;  // To make some compilers happy.
      auto sp            = gsl::make_span(dconst.data(), static_cast<int>(d.rows() * d.cols()));

      return write<ScalarType>(sp);
    } catch (...) {
      std::throw_with_nested(Exception(ioda_Here()));
    }
#else
    static_assert(false, "The Eigen headers cannot be found, so this function cannot be used.");
#endif
  }

  /// \brief Write an Eigen Tensor-like object
  /// \tparam EigenClass is the Eigen tensor to write.
  /// \param d is the data to be written.
  /// \throws ioda::Exception on a dimension mismatch.
  /// \returns the attribute
  template <class EigenClass>
  Attribute_Implementation writeWithEigenTensor(const EigenClass& d) {
#if 1  //__has_include("unsupported/Eigen/CXX11/Tensor")
    try {
      ioda::Dimensions dims = detail::EigenCompat::getTensorDimensions(d);

      auto sp  = (gsl::make_span(d.data(), dims.numElements));
      auto res = write(sp);
      return res;
    } catch (...) {
      std::throw_with_nested(Exception(ioda_Here()));
    }
#else
    static_assert(
      false, "The Eigen unsupported/ headers cannot be found, so this function cannot be used.");
#endif
  }

  /// @}
  /// @name Reading Data
  /// @{
  ///

  /// \brief The fundamental read function. Backends overload this function to implement all read
  /// operations.
  ///
  /// \details This function reads in a span of characters from the backend attribute storage.
  /// No type conversions take place here (see the templated conversion function, below).
  ///
  /// \param data is a span of data that has length of getStorageSize().
  /// \param in_memory_datatype is an opaque (backend-level) object that describes the placement of
  ///   the data in memory. Usually ignorable - needed for complex data structures.
  /// \throws ioda::Exception if data has the wrong size.
  /// \returns The attribute (for chaining).
  virtual Attribute_Implementation read(gsl::span<char> data, const Type& in_memory_dataType) const;

  /// \brief Read data.
  ///
  /// \details This is a fundamental function that reads a span of characters from backend storage,
  /// and then performs the appropriate type conversion / deserialization into objects in data.
  ///
  /// \tparam DataType is the type if the data to be read. I.e. float, int, int32_t, uint16_t,
  /// std::string, etc.
  /// \tparam Marshaller is the class that performs the deserialization operation.
  /// \tparam TypeWrapper is a helper class that passes Type information to the backend.
  /// \param data is a pointer-size pair to the data buffer that is filled with the metadata's
  /// contents. It should be
  ///   pre-sized to accomodate all of the matadata. See getDimensions().numElements. data will be
  ///   filled in row-major order.
  /// \param in_memory_datatype is an opaque (backend-level) object that describes the placement of
  ///   the data in memory. Usually this does not need to be set to anything other than its default
  ///   value. Kept as a parameter for debugging purposes.
  /// \returns Another instance of this Attribute. Used for operation chaining.
  /// \throws ioda::Exception if data.size() != getDimensions().numElements.
  /// \see getDimensions for buffer size information.
  template <class DataType, class Marshaller = ioda::Object_Accessor<DataType>,
            class TypeWrapper = Types::GetType_Wrapper<DataType>>
  Attribute_Implementation read(gsl::span<DataType> data) const {
    try {
      const size_t numObjects = data.size();
      if (getDimensions().numElements != gsl::narrow<ioda::Dimensions_t>(numObjects))
        throw Exception("Size mismatch between underlying object and user-provided data range.",
                        ioda_Here());

      detail::PointerOwner pointerOwner = getTypeProvider()->getReturnedPointerOwner();
      Marshaller m(pointerOwner);
      auto p = m.prep_deserialize(numObjects);
      read(gsl::make_span<char>(reinterpret_cast<char*>(p->DataPointers.data()),
                                p->DataPointers.size() * Marshaller::bytesPerElement_),
           TypeWrapper::GetType(getTypeProvider()));
      m.deserialize(p, data);

      return Attribute_Implementation{backend_};
    } catch (...) {
      std::throw_with_nested(Exception(ioda_Here()));
    }
  }

  /// \brief Vector read convenience function.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \param data is a vector acting as a data buffer that is filled with the metadata's contents.
  ///   It gets resized as needed.
  /// \returns Another instance of this Attribute. Used for operation chaining.
  /// \note data will be stored in row-major order.
  template <class DataType>
  Attribute_Implementation read(std::vector<DataType>& data) const {
    data.resize(getDimensions().numElements);
    return read<DataType>(gsl::make_span<DataType>(data.data(), data.size()));
  }

  /// \brief Valarray read convenience function.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \param data is a valarray acting as a data buffer that is filled with the metadata's contents.
  ///   It gets resized as needed.
  /// \returns Another instance of this Attribute. Used for operation chaining.
  /// \note data will be stored in row-major order.
  template <class DataType>
  Attribute_Implementation read(std::valarray<DataType>& data) const {
    data.resize(getDimensions().numElements);
    return read<DataType>(gsl::make_span<DataType>(std::begin(data), std::end(data)));
  }

  /// \brief Read into a single value (convenience function).
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \param data is where the datum is read to.
  /// \returns Another instance of this Attribute. Used for operation chaining.
  /// \throws ioda::Exception if the underlying data have multiple elements.
  template <class DataType>
  Attribute_Implementation read(DataType& data) const {
    try {
      if (getDimensions().numElements != 1)
        throw Exception("Wrong number of elements. Use a different read() method.", ioda_Here());
      return read<DataType>(gsl::make_span<DataType>(&data, 1));
    } catch (...) {
      std::throw_with_nested(Exception(ioda_Here()));
    }
  }

  /// \brief Read a single value (convenience function).
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \returns A datum of type DataType.
  /// \throws ioda::Exception if the underlying data have size greater than 1.
  /// \note The Python function is read_datum_*
  template <class DataType>
  DataType read() const {
    DataType ret;
    read<DataType>(ret);
    return ret;
  }

  /// \brief Read into a new vector. Python convenience function.
  /// \tparam DataType is the type of the data.
  /// \note The Python function is read_list_*
  template <class DataType>
  std::vector<DataType> readAsVector() const {
    std::vector<DataType> data(getDimensions().numElements);
    read<DataType>(gsl::make_span<DataType>(data.data(), data.size()));
    return data;
  }

  /// \brief Read data into an Eigen::Array, Eigen::Matrix, Eigen::Map, etc.
  /// \tparam EigenClass is a template pointing to the Eigen object.
  ///   This template must provide the EigenClass::Scalar typedef.
  /// \tparam Resize indicates whether the Eigen object should be resized
  ///   if there is a dimension mismatch. Not all Eigen objects can be resized.
  /// \param res is the Eigen object.
  /// \returns Another instance of this Attribute. Used for operation chaining.
  /// \throws ioda::Exception if the attribute's dimensionality is
  ///   too high.
  /// \throws ioda::Exception if resize = false and there is a dimension mismatch.
  /// \note When reading in a 1-D object, the data are read as a column vector.
  template <class EigenClass, bool Resize = detail::EigenCompat::CanResize<EigenClass>::value>
  Attribute_Implementation readWithEigenRegular(EigenClass& res) const {
#if 1  //__has_include("Eigen/Dense")
    try {
      typedef typename EigenClass::Scalar ScalarType;

      static_assert(
        !(Resize && !detail::EigenCompat::CanResize<EigenClass>::value),
        "This object cannot be resized, but you have specified that a resize is required.");

      // Check that the dimensionality is 1 or 2.
      const auto dims = getDimensions();
      if (dims.dimensionality > 2)
        throw Exception(
          "Dimensionality too high for a regular Eigen read. Use "
          "Eigen::Tensor reads instead.",
          ioda_Here());

      int nDims[2] = {1, 1};
      if (dims.dimsCur.size() >= 1) nDims[0] = gsl::narrow<int>(dims.dimsCur[0]);
      if (dims.dimsCur.size() >= 2) nDims[1] = gsl::narrow<int>(dims.dimsCur[1]);

      // Resize if needed.
      if (Resize)
        detail::EigenCompat::DoEigenResize(res, nDims[0],
                                           nDims[1]);  // nullop if the size is already correct.
      else if (dims.numElements != (size_t)(res.rows() * res.cols()))
        throw Exception("Size mismatch", ioda_Here());

      // Array copy to preserve row vs column major format.
      // Should be optimized away by the compiler if unneeded.
      // Note to the reader: We are reading in the data to a temporary object.
      // We can size _this_ temporary object however we want.
      // The temporary is used to swap row / column indices if needed.
      // It should be optimized away if not needed... making sure this happens is a todo.
      Eigen::Array<ScalarType, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> data_in(res.rows(),
                                                                                res.cols());

      auto ret = read<ScalarType>(gsl::span<ScalarType>(data_in.data(), dims.numElements));
      res      = data_in;
      return ret;
    } catch (...) {
      std::throw_with_nested(Exception(ioda_Here()));
    }
#else
    static_assert(false, "The Eigen headers cannot be found, so this function cannot be used.");
#endif
  }

  /// \brief Read data into an Eigen::Array, Eigen::Matrix, Eigen::Map, etc.
  /// \tparam EigenClass is a template pointing to the Eigen object.
  ///   This template must provide the EigenClass::Scalar typedef.
  /// \param res is the Eigen object.
  /// \returns Another instance of this Attribute. Used for operation chaining.
  /// \throws ioda::Exception if there is a size mismatch.
  /// \note When reading in a 1-D object, the data are read as a column vector.
  template <class EigenClass>
  Attribute_Implementation readWithEigenTensor(EigenClass& res) const {
#if 1  //__has_include("unsupported/Eigen/CXX11/Tensor")
    try {
      // Check dimensionality of source and destination
      const auto ioda_dims  = getDimensions();
      const auto eigen_dims = ioda::detail::EigenCompat::getTensorDimensions(res);
      if (ioda_dims.numElements != eigen_dims.numElements)
        throw Exception("Size mismatch for Eigen Tensor-like read.", ioda_Here());

      auto sp = (gsl::make_span(res.data(), eigen_dims.numElements));
      return read(sp);
    } catch (...) {
      std::throw_with_nested(Exception(ioda_Here()));
    }
#else
    static_assert(
      false, "The Eigen unsupported/ headers cannot be found, so this function cannot be used.");
#endif
  }

  /// \internal Python binding function
  template <class EigenClass>
  EigenClass _readWithEigenRegular_python() const {
    EigenClass data;
    readWithEigenRegular(data);
    return data;
  }

  /// @}
  /// @name Type-querying Functions
  /// @{

  /// \brief Get Attribute type.
  virtual Type getType() const;
  /// \brief Get Attribute type.
  inline Type type() const { return getType(); }

  /// Query the backend and get the type provider.
  virtual detail::Type_Provider* getTypeProvider() const;

  /// \brief Convenience function to check an Attribute's storage type.
  /// \tparam DataType is the type of the data. I.e. float, int, int32_t, uint16_t, std::string, etc.
  /// \returns True if the type matches
  /// \returns False (0) if the type does not match
  /// \throws ioda::Exception if an error occurred.
  template <class DataType>
  bool isA() const {
    Type templateType = Types::GetType_Wrapper<DataType>::GetType(getTypeProvider());

    return isA(templateType);
  }
  /// Hand-off to the backend to check equivalence
  virtual bool isA(Type lhs) const;

  /// Python compatability function
  inline bool isA(BasicTypes dataType) { return isA(Type(dataType, getTypeProvider())); }
  /// \internal pybind11
  inline bool _py_isA2(BasicTypes dataType) { return isA(dataType); }

  /// @}
  /// @name Data Space-Querying Functions
  /// @{

  // Get an Attribute's dataspace
  // virtual Encapsulated_Handle getSpace() const;

  /// \brief Get Attribute's dimensions.
  virtual Dimensions getDimensions() const;

  /// @}
};

// extern template class Attribute_Base<>;
}  // namespace detail

/** \brief This class represents attributes, which may be attached to both Variables and Groups.
 *  \ingroup ioda_cxx_attribute
 *
 * Attributes are used to store small objects that get tagged to a Variable or a
 * Group to provide context to users and other programs. Attributes include
 * descriptions, units, alternate names, dimensions, and similar constructs.
 * Attributes may have different types (ints, floats, datetimes, strings, etc.),
 * and may be 0- or 1-dimensional.
 *
 * We can open an Attribute from a Has_Attribute object, which is a member of Groups and
 * Variables.
 *
 * \note Multidimensional attributes are supported by some of the underlying backends,
 * like HDF5, but are incompatible with the NetCDF file format.
 * \see Has_Attribute for the class that can create and open new Attribute objects.
 * \throws ioda::Exception on all exceptions.
 **/
class IODA_DL Attribute : public detail::Attribute_Base<> {
public:
  Attribute();
  Attribute(std::shared_ptr<detail::Attribute_Backend> b);
  Attribute(const Attribute&);
  Attribute& operator=(const Attribute&);
  virtual ~Attribute();

  /// @name Python compatability objects
  /// @{

  detail::python_bindings::AttributeIsA<Attribute> _py_isA;

  detail::python_bindings::AttributeReadSingle<Attribute> _py_readSingle;
  detail::python_bindings::AttributeReadVector<Attribute> _py_readVector;
  detail::python_bindings::AttributeReadNPArray<Attribute> _py_readNPArray;

  detail::python_bindings::AttributeWriteSingle<Attribute> _py_writeSingle;
  detail::python_bindings::AttributeWriteVector<Attribute> _py_writeVector;
  detail::python_bindings::AttributeWriteNPArray<Attribute> _py_writeNPArray;

  /// @}
};

namespace detail {
/// \brief Attribute backends inherit from this.
class IODA_DL Attribute_Backend : public Attribute_Base<> {
public:
  virtual ~Attribute_Backend();

protected:
  Attribute_Backend();
};
}  // namespace detail
}  // namespace ioda

/// @} // End Doxygen block


#pragma once

#include <gsl/gsl>

namespace Halley {
    // Note: it's important that this class has the same layout and binary structure as a plain pointer
    template <typename T>
    class MaybeRef {
    public:
        MaybeRef() : pointer(nullptr) {}
        MaybeRef(T* pointer) : pointer(pointer) {}
        MaybeRef(T& ref) : pointer(&ref) {}

        bool hasValue() const
        {
            return pointer != nullptr;
        }

        T& get()
        {
            Expects(pointer != nullptr);
            return *pointer;
        }

        const T& get() const
        {
            Expects(pointer != nullptr);
            return *pointer;
        }

    	T* operator->()
        {
        	Expects(pointer != nullptr);
			return pointer;
        }

    	const T* operator->() const
        {
        	Expects(pointer != nullptr);
			return pointer;
        }

    	operator bool() const
        {
            return pointer != nullptr;
        }

    	T* tryGet()
        {
	        return pointer;
        }

    	const T* tryGet() const
        {
	        return pointer;
        }

    private:
        T* pointer;
    };
}


#ifndef SEARCHPATTERNBUILDER_HHHHH
#define SEARCHPATTERNBUILDER_HHHHH

#include "detail/SearchPatternBase.h"
#include "SearchPattern.h"

#include <vector>
#include <string>
#include <memory>

#include <gsl/string_span>

namespace MPSig {

    class SearchPatternBuilder {
        std::vector<std::unique_ptr<detail::SearchPatternBase>> m_patternSteps;
    public:
        SearchPatternBuilder();

        SearchPatternBuilder& HasHexPattern(gsl::cstring_span<-1> pattern);
        SearchPatternBuilder& ReferencesBSTR(gsl::cwstring_span<-1> bstrText);

        SearchPattern Compile();
    };
}

#endif


#pragma once
#include "PreConfig.h"

#ifdef CRU_PLATFORM_WINDOWS
#ifdef CRU_BASE_EXPORT_API
#define CRU_BASE_API __declspec(dllexport)
#else
#define CRU_BASE_API __declspec(dllimport)
#endif
#else
#define CRU_BASE_API
#endif

#include <gsl/gsl>

#define CRU_UNUSED(entity) static_cast<void>(entity);

#define CRU__CONCAT(a, b) a##b
#define CRU_MAKE_UNICODE_LITERAL(str) CRU__CONCAT(u, #str)

#define CRU_DEFAULT_COPY(classname)      \
  classname(const classname&) = default; \
  classname& operator=(const classname&) = default;

#define CRU_DEFAULT_MOVE(classname) \
  classname(classname&&) = default; \
  classname& operator=(classname&&) = default;

#define CRU_DELETE_COPY(classname)      \
  classname(const classname&) = delete; \
  classname& operator=(const classname&) = delete;

#define CRU_DELETE_MOVE(classname) \
  classname(classname&&) = delete; \
  classname& operator=(classname&&) = delete;

#define CRU_DEFAULT_DESTRUCTOR(classname) ~classname() = default;

#define CRU_DEFAULT_CONSTRUCTOR_DESTRUCTOR(classname) \
  classname() = default;                              \
  ~classname() = default;

#define CRU_DEFINE_COMPARE_OPERATORS(classname)                           \
  inline bool operator==(const classname& left, const classname& right) { \
    return left.Compare(right) == 0;                                      \
  }                                                                       \
                                                                          \
  inline bool operator!=(const classname& left, const classname& right) { \
    return left.Compare(right) != 0;                                      \
  }                                                                       \
                                                                          \
  inline bool operator<(const classname& left, const classname& right) {  \
    return left.Compare(right) < 0;                                       \
  }                                                                       \
                                                                          \
  inline bool operator<=(const classname& left, const classname& right) { \
    return left.Compare(right) <= 0;                                      \
  }                                                                       \
                                                                          \
  inline bool operator>(const classname& left, const classname& right) {  \
    return left.Compare(right) > 0;                                       \
  }                                                                       \
                                                                          \
  inline bool operator>=(const classname& left, const classname& right) { \
    return left.Compare(right) >= 0;                                      \
  }

namespace cru {
class CRU_BASE_API Object {
 public:
  Object() = default;
  CRU_DEFAULT_COPY(Object)
  CRU_DEFAULT_MOVE(Object)
  virtual ~Object() = default;
};

struct CRU_BASE_API Interface {
  Interface() = default;
  CRU_DELETE_COPY(Interface)
  CRU_DELETE_MOVE(Interface)
  virtual ~Interface() = default;
};

[[noreturn]] void CRU_BASE_API UnreachableCode();

using Index = gsl::index;

// https://www.boost.org/doc/libs/1_54_0/doc/html/hash/reference.html#boost.hash_combine
template <class T>
inline void hash_combine(std::size_t& s, const T& v) {
  std::hash<T> h;
  s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2);
}

#define CRU_DEFINE_CLASS_LOG_TAG(tag) \
 private:                             \
  constexpr static const char16_t* kLogTag = tag;
}  // namespace cru


#ifndef DART_CPP14_SHIM_H
#define DART_CPP14_SHIM_H

// Figure out what compiler we have.
#if defined(__clang__)
#define DART_USING_CLANG 1
#elif defined(__GNUC__) || defined(__GNUG__)
#define DART_USING_GCC 1
#elif defined(_MSC_VER)
#define DART_USING_MSVC 1
#endif

#ifdef DART_USING_MSVC
#define _CRT_SECURE_NO_WARNINGS 1
#endif

#include <ctime>
#include <cstring>

#if DART_USING_CLANG && __clang_major__ >= 5 && __clang_major__ <= 7
// Side-step a disagreement between clang (5/6) and GNU std::variant.
#define DART_USE_MPARK_VARIANT 1
#elif defined(__APPLE__)
// Side-step AppleClang misreporting compiler capabilities on macos 10.13 and below.
#include <availability.h>
#ifndef __MAC_10_14
#define DART_USE_MPARK_VARIANT 1
#endif
#endif

// MSVC doesn't have a signed size type, obviously
#if DART_USING_MSVC
#include <BaseTsd.h>
using ssize_t = SSIZE_T;
#endif

// Make sure we have a fallback for compilers that don't support attributes at all.
#ifndef __has_cpp_attribute
#define __has_cpp_attribute(name) 0
#endif

// Figure out how to declare things [[nodiscard]]
#if __has_cpp_attribute(gnu::warn_unused_result)
#define DART_NODISCARD [[gnu::warn_unused_result]]
#elif __has_cpp_attribute(nodiscard)
#define DART_NODISCARD [[nodiscard]]
#else
#define DART_NODISCARD
#endif

#define DART_STRINGIFY_IMPL(x) #x
#define DART_STRINGIFY(x) DART_STRINGIFY_IMPL(x)

#if DART_USING_MSVC
#define DART_UNLIKELY(x) !!(x)
#else
#define DART_UNLIKELY(x) __builtin_expect(!!(x), 0)
#endif

#ifndef NDEBUG

#if DART_USING_MSVC
#include <io.h>
#define DART_WRITE(fd, ptr, bytes) _write(fd, ptr, static_cast<unsigned int>(bytes))
#define DART_STDERR_FILENO _fileno(stderr)
#else
#include <unistd.h>
#define DART_WRITE(fd, ptr, bytes) write(fd, ptr, bytes)
#define DART_STDERR_FILENO STDERR_FILENO
#endif

/**
 *  @brief
 *  Macro customizes functionality usually provided by assert().
 *
 *  @details
 *  Not strictly necessary, but tries to provide a bit more context and
 *  information as to why I just murdered the user's program (in production, no doubt).
 *
 *  @remarks
 *  Don't actually know if Doxygen lets you document macros, guess we'll see.
 */
#define DART_ASSERT(cond)                                                                                \
  if (DART_UNLIKELY(!(cond))) {                                                                               \
    auto& msg = "dart::packet has detected fatal memory corruption and cannot continue execution.\n"          \
      "\"" DART_STRINGIFY(cond) "\" violated.\nSee " __FILE__ ":" DART_STRINGIFY(__LINE__) "\n";              \
    int errfd = DART_STDERR_FILENO;                                                                           \
    ssize_t spins {0}, written {0}, total {sizeof(msg)};                                                      \
    do {                                                                                \
      ssize_t ret = DART_WRITE(errfd, msg + written, total - written);                                        \
      if (ret >= 0) written += ret;                                                                           \
    } while (written != total && spins++ < 16);                                                               \
    std::abort();                                                                                \
  }
#else
#define DART_ASSERT(cond)
#endif

// Conditionally include different implementations of different data structures
// depending on what standard we have access to.
#if __cplusplus >= 201703L && !DART_USE_MPARK_VARIANT
#define DART_HAS_CPP17 1
#include <variant>
#include <optional>
#include <string_view>
#else
#define DART_HAS_CPP14 1
#include "support/variant.h"
#include "support/optional.h"
#include "support/string_view.h"
#endif

// We support two versions of GSL, but unfortunately they don't agree about
// the template signature of gsl::span (why would we expect two production-ready
// implementations of the same exact specification to agree about something like
// that), so we have to declare some of our partial specializations differently
// depending on what we included.
#include <gsl/gsl>
#ifndef gsl_lite_VERSION
#define DART_USING_GSL
#else
#define DART_USING_GSL_LITE
#endif

// Conditionally pull each of those types into our namespace.
namespace dart {
  namespace shim {

#if DART_USING_MSVC
    inline int aligned_alloc(void** memptr, size_t alignment, size_t size) {
      void* ret = _aligned_malloc(size, alignment);
      if (ret) {
        *memptr = ret;
        return 0;
      } else {
        return -1;
      }
    }

    inline void aligned_free(void* ptr) {
      _aligned_free(ptr);
    }

    inline void gmtime(time_t const* src, std::tm* out) {
      gmtime_s(out, src);
    }

    inline time_t timegm(std::tm* src) {
      return _mkgmtime(src);
    }
#else
    inline int aligned_alloc(void** memptr, size_t alignment, size_t size) {
      return posix_memalign(memptr, alignment, size);
    }

    inline void aligned_free(void* ptr) {
      free(ptr);
    }

    inline void gmtime(time_t const* src, std::tm* out) {
      gmtime_r(src, out);
    }

    inline time_t timegm(std::tm* src) {
      return ::timegm(src);
    }
#endif

#ifdef DART_HAS_CPP17

    // Pull in names of types.
    using std::optional;
    using std::nullopt_t;
    using std::variant;
    using std::monostate;
    using std::string_view;
    using std::basic_string_view;

    // Pull in constants.
    static inline constexpr auto nullopt = std::nullopt;

    // Pull in non-member helpers.
    using std::get;
    using std::visit;
    using std::get_if;
    using std::launder;
    using std::holds_alternative;
    using std::variant_alternative;
    using std::variant_alternative_t;

    // Define a way to compose lambdas.
    template <class... Ls>
    struct compose : Ls... {
      using Ls::operator ()...;
    };
    template <class... Ls>
    compose(Ls...) -> compose<Ls...>;

    template <class... Ls>
    auto compose_together(Ls&&... lambdas) {
      return compose {std::forward<Ls>(lambdas)...};
    }
#else

    // Pull in names of types.
    using dart::optional;
    using dart::nullopt_t;
    using dart::string_view;
    using dart::basic_string_view;
    using mpark::variant;
    using mpark::monostate;

    // Pull in non-member helpers.
    using mpark::get;
    using mpark::visit;
    using mpark::get_if;
    using mpark::holds_alternative;
    using mpark::variant_alternative;
    using mpark::variant_alternative_t;

    // Pull in constants.
    static constexpr auto nullopt = dart::nullopt;

    // Define a way to compose lambdas.
    template <class... Ls>
    struct compose;
    template <class L, class... Ls>
    struct compose<L, Ls...> : L, compose<Ls...> {
      compose(L l, Ls... the_rest) : L(std::move(l)), compose<Ls...>(std::move(the_rest)...) {}
      using L::operator ();
      using compose<Ls...>::operator ();
    };
    template <class L>
    struct compose<L> : L {
      compose(L l) : L(std::move(l)) {}
      using L::operator ();
    };

    template <class... Ls>
    auto compose_together(Ls&&... lambdas) {
      return compose<std::decay_t<Ls>...> {std::forward<Ls>(lambdas)...};
    }
#endif
  }
}

#endif


#pragma once
#include "halley/plugin/iasset_importer.h"
#include <gsl/gsl>

namespace Halley
{
	class Animation;

	class AnimationImporter : public IAssetImporter
	{
	public:
		ImportAssetType getType() const override { return ImportAssetType::Animation; }

		void import(const ImportingAsset& asset, IAssetCollector& collector) override;

		static void parseAnimation(Animation& animation, gsl::span<const gsl::byte> data);
	};
}


/***
 * Copyright 2019 The Katla Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef KATLA_SOCKET_H
#define KATLA_SOCKET_H

#include "posix-errors.h"

#include "katla/core/error.h"
#include "katla/core/core.h"

#include <gsl/span>

#include <net/ethernet.h>

#include <memory>
#include <optional>
#include <system_error>

#include <chrono>
#include <string>

namespace outcome = OUTCOME_V2_NAMESPACE;

namespace katla {

class PosixSocket {
public:
    enum class ProtocolDomain { Unix, IPv4, IPv6, Packet, Can, Bluetooth, VSock};
    enum class Type { Stream, Datagram, SequencedPacket, Raw};
    enum class FrameType : uint16_t { All = ETH_P_ALL, EtherCat = 0x88a4 };

    struct SocketOptions {
        ProtocolDomain domain {ProtocolDomain::IPv4};
        Type type {Type::Stream};
        FrameType frameType {FrameType::All};
        bool nonBlocking {false};
        bool reuseAddress {false};
    };

    static constexpr SocketOptions IPv4SocketOptions {ProtocolDomain::IPv4, Type::Stream, FrameType::All, false, false};

    struct WaitResult {
        bool dataToRead {0};
        bool urgentDataToRead {0};
        bool writingWillNotBlock {0};
        bool readHangup {0};
        bool writeHangup {0};
        bool error {0};
        bool invalid {0};
        bool wakeup {0};
    };

    PosixSocket();
    PosixSocket(ProtocolDomain protocolDomain, Type type, FrameType frameType, bool nonBlocking);
    PosixSocket(const PosixSocket&) = delete;
    ~PosixSocket();

    static outcome::result<std::array<std::shared_ptr<PosixSocket>, 2>, Error> createUnnamedPair(ProtocolDomain protocolDomain, Type type, FrameType frameType, bool nonBlocking);

    outcome::result<void, Error> bind(std::string url);
    outcome::result<void, Error> bindIPv4(std::string ip, int port, SocketOptions options = IPv4SocketOptions);

    outcome::result<void, Error> listen();
    outcome::result<std::unique_ptr<PosixSocket>, Error> accept();
    std::optional<Error> error();

    outcome::result<void, Error> connect(std::string url, SocketOptions options);
    outcome::result<void, Error> connectIPv4(std::string ip, int port, SocketOptions options = IPv4SocketOptions);

    // TODO add wakeup to interrupt wait -> multithreading??
    outcome::result<WaitResult, Error> poll(std::chrono::milliseconds timeout, bool writePending = false);

    outcome::result<ssize_t, Error> read(const gsl::span<std::byte>& buffer);
    outcome::result<ssize_t, Error> write(const gsl::span<std::byte>& buffer);

    outcome::result<ssize_t, Error> receiveFrom(const gsl::span<std::byte>& buffer);
    outcome::result<ssize_t, Error> sendTo(std::string url, const gsl::span<std::byte>& buffer);

    outcome::result<void, Error> wakeup();

    outcome::result<void, Error> close();
private:
    outcome::result<void, Error> create();

    PosixSocket & operator=(const PosixSocket&) = delete;

    PosixSocket(ProtocolDomain protocolDomain, Type type, FrameType frameType, bool nonBlocking, int fd, int wakeupFd);

    static int mapProtocolDomain (ProtocolDomain protocolDomain);
    static int mapType (Type type);

    int _fd {-1};
    int _wakeupFd {-1};

    ProtocolDomain _protocolDomain {ProtocolDomain::IPv4};
    Type _type {Type::Stream};
    FrameType _frameType {FrameType::All};

    std::string _url;

    bool _nonBlocking {false};
};

}

#endif // KATLA_SOCKET_H


/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef xsd_284c8467_1f5e_4ae4_b645_077382bf6ab4_h
#define xsd_284c8467_1f5e_4ae4_b645_077382bf6ab4_h

#include <gslib/config.h>
#include <gslib/xml.h>
#include <gslib/std.h>
#include <gslib/xsdtypes.h>

__gslib_begin__

//#define xsd_def_

class xsd_schema;
struct xsd_extern_schema;
class xsd_node;
class xsd_element;
class xsd_attribute;
class xsd_group;
class xsd_attribute_group;
class xsd_simple_type;
class xsd_complex_type;

typedef xml_wrapper xsd_wrapper;
typedef vector<xsd_wrapper*> xsd_wrappers;
typedef xmltree::iterator xsd_iterator;
typedef xmltree::const_iterator xsd_const_iterator;

template<class _ty>
struct xsd_schema_ns_hash:
    public std::unary_function<_ty, size_t>
{
    size_t operator()(_ty t) const
    {
        assert(t);
        return string_hash(t->get_namespace());
    }
};

template<class _ty>
struct xsd_schema_ns_equalto:
    public std::binary_function<_ty, _ty, bool>
{
    bool operator()(_ty t1, _ty t2) const
    {
        assert(t1 && t2);
        return t1->get_namespace() == t2->get_namespace();
    }
};

template<class _ty>
struct xsd_schema_loc_hash:
    public std::unary_function<_ty, size_t>
{
    size_t operator()(_ty t) const
    {
        assert(t);
        return string_hash(t->get_location());
    }
};

template<class _ty>
struct xsd_schema_loc_equalto:
    public std::binary_function<_ty, _ty, bool>
{
    bool operator()(_ty t1, _ty t2) const
    {
        assert(t1 && t2);
        return t1->get_location() == t2->get_location();
    }
};

template<class _ty>
struct xsd_name_hash:
    public std::unary_function<_ty, size_t>
{
    size_t operator()(_ty t) const
    {
        assert(t);
        return string_hash(t->get_name());
    }
};

template<class _ty>
struct xsd_name_equalto:
    public std::binary_function<_ty, _ty, bool>
{
    bool operator()(_ty t1, _ty t2) const
    {
        assert(t1 && t2);
        return t1->get_name() == t2->get_name();
    }
};

typedef vector<xsd_schema*> xsd_schemas;
typedef unordered_multiset<xsd_schema*, xsd_schema_ns_hash<xsd_schema*>, xsd_schema_ns_equalto<xsd_schema*> > xsd_schema_ns_map;
typedef unordered_set<xsd_schema*, xsd_schema_loc_hash<xsd_schema*>, xsd_schema_loc_equalto<xsd_schema*> > xsd_schema_loc_map;
typedef list<xsd_extern_schema> xsd_extern_schemas;
typedef unordered_set<xsd_node*, xsd_name_hash<xsd_node*>, xsd_name_equalto<xsd_node*> > xsd_node_map;
typedef unordered_multiset<xsd_node*, xsd_name_hash<xsd_node*>, xsd_name_equalto<xsd_node*> > xsd_node_multimap;

struct xsd_global
{
    xsd_schemas             packs;
    xsd_schema_ns_map       ns_map;
    xsd_schema_loc_map      loc_map;

public:
    ~xsd_global();

};

struct xsd_cg_config {};

extern xsd_schema* xsd_parse_schema(xsd_global& global, xmltree& dom, const gchar* location);
extern xsd_schema* xsd_parse_schema(xsd_global& global, const gchar* location);
extern void xsd_make_file_name(string& name, const string& str);
extern bool xsd_prepare_name_mangling(xsd_global& global);
extern bool xsd_prepare_translation(xsd_schema* schema, xsd_global& global);
extern bool xsd_translate_declarations(string& output, xsd_schema* schema, xsd_global& global);
extern bool xsd_translate_implementations(string& output, xsd_schema* schema, xsd_global& global);

enum xsd_extern_schema_type
{
    xest_include,
    xest_import,
};

enum xsd_node_type
{
    xnt_key,
    xnt_element,
    xnt_attribute,
    xnt_attribute_group,
    xnt_group,
    xnt_simple_type,
    xnt_complex_type,
};

struct xsd_extern_schema
{
    xsd_extern_schema_type  type;
    string                  ns;
    xsd_schema*             schema;

public:
    xsd_schema* operator->() { return schema; }
    const xsd_schema* operator->() const { return schema; }
};

class xsd_schema
{
protected:
    xsd_wrapper*            _source;
    string                  _namespace;
    string                  _location;
    xsd_extern_schemas      _external;      // include & import
    string                  _xsd_namespace;
    xsd_node_map            _element_map;
    xsd_node_map            _attribute_map;
    xsd_node_map            _attribute_group_map;
    xsd_node_map            _group_map;
    xsd_node_map            _simple_type_map;
    xsd_node_map            _complex_type_map;

public:
    xsd_schema();
    ~xsd_schema();
    const string& get_namespace() const { return _namespace; }
    const string& get_location() const { return _location; }
    const string& get_xsd_namespace() const { return _xsd_namespace; }
    void set_namespace(const string& str) { _namespace = str; }
    void set_location(const string& str) { _location = str; }
    void set_xsd_namespace(const string& str) { _xsd_namespace = str; }
    void set_source(xsd_wrapper* s) { _source = s; }
    xsd_extern_schemas& get_extern_schemas() { return _external; }
    xsd_node_map& get_element_map() { return _element_map; }
    xsd_node_map& get_attribute_map() { return _attribute_map; }
    xsd_node_map& get_attribute_group_map() { return _attribute_group_map; }
    xsd_node_map& get_group_map() { return _group_map; }
    xsd_node_map& get_simple_type_map() { return _simple_type_map; }
    xsd_node_map& get_complex_type_map() { return _complex_type_map; }

protected:
    static xsd_node* find_node(const xsd_node_map& m, const string& name);
    template<class _node>
    static _node* find_and_verify(const xsd_node_map& m, const string& n, xsd_node_type t)
    {
        xsd_node* r = find_node(m, n);
        if(!r) return 0;
        assert(r->get_type() == t);
        return static_cast<_node*>(r);
    }

public:
    xsd_element* find_element_node(const string& name) const { return find_and_verify<xsd_element>(_element_map, name, xnt_element); }
    xsd_attribute* find_attribute_node(const string& name) const { return find_and_verify<xsd_attribute>(_attribute_map, name, xnt_attribute); }
    xsd_group* find_group_node(const string& name) const { return find_and_verify<xsd_group>(_group_map, name, xnt_group); }
    xsd_attribute_group* find_attribute_group_node(const string& name) const { return find_and_verify<xsd_attribute_group>(_attribute_group_map, name, xnt_attribute_group); }
    xsd_simple_type* find_simple_type_node(const string& name) const { return find_and_verify<xsd_simple_type>(_simple_type_map, name, xnt_simple_type); }
    xsd_complex_type* find_complex_type_node(const string& name) const { return find_and_verify<xsd_complex_type>(_complex_type_map, name, xnt_complex_type); }
};

struct xsd_ns_key:
    public xsd_schema
{
    xsd_ns_key(const string& str) { _namespace.assign(str); }
    xsd_ns_key(const gchar* str) { _namespace.assign(str); }
    xsd_ns_key(const gchar* str, int len) { _namespace.assign(str, len); }
};

struct xsd_loc_key:
    public xsd_schema
{
    xsd_loc_key(const string& str) { _location.assign(str); }
    xsd_loc_key(const gchar* str) { _location.assign(str); }
    xsd_loc_key(const gchar* str, int len) { _location.assign(str, len); }
};

class __gs_novtable xsd_node abstract
{
public:
    xsd_node();
    virtual ~xsd_node() {}
    virtual xsd_node_type get_type() const = 0;

public:
    void set_name(const string& str) { _name = str; }
    void set_mangling(const string& str) { _mangling = str; }
    const string& get_name() const { return _name; }
    const string& get_mangling() const { return _mangling; }
    xsd_wrapper* get_source() const { return _source; }
    void set_source(xsd_wrapper* s) { _source = s; }
    void set_translated(bool b) { _translated = b; }
    bool is_translated() const { return _translated; }

protected:
    string                  _name;
    string                  _mangling;
    xsd_wrapper*            _source;
    bool                    _translated;
};

struct xsd_node_key:
    public xsd_node
{
    xsd_node_key(const string& str) { _name.assign(str); }
    xsd_node_key(const gchar* str) { _name.assign(str); }
    xsd_node_key(const gchar* str, int len) { _name.assign(str, len); }
    xsd_node_type get_type() const override { return xnt_key; }
};

class xsd_element:
    public xsd_node
{
public:
    xsd_element();
    ~xsd_element();
    xsd_node_type get_type() const override { return xnt_element; }
    void set_ref(xsd_element* r) { _ref = r; }
    xsd_element* get_ref() const { return _ref; }
    void set_local_type(xsd_node* n) { _local_type = n; }
    xsd_node* get_local_type() const { return _local_type; }
    string& get_type_name() { return _elem_type_name; }
    string& get_entity_name() { return _elem_entity_name; }
    string& get_verificator() { return _elem_verificator; }
    string& get_assigner() { return _elem_assigner; }
    const string& const_type_name() const { return _elem_type_name; }
    const string& const_entity_name() const { return _elem_entity_name; }
    const string& const_verificator() const { return _elem_verificator; }
    const string& const_assigner() const { return _elem_assigner; }

protected:
    xsd_element*            _ref;
    xsd_node*               _local_type;
    string                  _elem_type_name;
    string                  _elem_entity_name;
    string                  _elem_verificator;
    string                  _elem_assigner;
};

class xsd_attribute:
    public xsd_node
{
public:
    xsd_attribute();
    ~xsd_attribute();
    xsd_node_type get_type() const override { return xnt_attribute; }
    void set_ref(xsd_attribute* r) { _ref = 0; }
    xsd_attribute* get_ref() const { return _ref; }
    void set_local_type(xsd_node* n) { _local_type = 0; }
    xsd_node* get_local_type() const { return _local_type; }
    string& get_type_name() { return _attr_type_name; }
    string& get_entity_name() { return _attr_entity_name; }
    string& get_verificator() { return _attr_verificator; }
    string& get_assigner() { return _attr_assigner; }
    const string& const_type_name() const { return _attr_type_name; }
    const string& const_entity_name() const { return _attr_entity_name; }
    const string& const_verificator() const { return _attr_verificator; }
    const string& const_assigner() const { return _attr_assigner; }

protected:
    xsd_attribute*          _ref;
    xsd_node*               _local_type;
    string                  _attr_type_name;
    string                  _attr_entity_name;
    string                  _attr_verificator;
    string                  _attr_assigner;
};

class xsd_group:
    public xsd_node
{
public:
    xsd_node_type get_type() const override { return xnt_group; }

protected:
};

class xsd_attribute_group:
    public xsd_node
{
public:
    xsd_node_type get_type() const override { return xnt_attribute_group; }
};

enum xsd_sts_type
{
    xst_restriction,
    xst_union,
    xst_list,
};

class __gs_novtable xsd_st_substitution abstract
{
public:
    virtual ~xsd_st_substitution() {}
    virtual xsd_sts_type get_type() const = 0;
};

class xsd_st_restriction:
    public xsd_st_substitution
{
public:
    xsd_sts_type get_type() const override { return xst_restriction; }

protected:

};

class xsd_st_union:
    public xsd_st_substitution
{
public:
    xsd_sts_type get_type() const override { return xst_union; }
};

class xsd_st_list:
    public xsd_st_substitution
{
public:
    xsd_sts_type get_type() const override { return xst_list; }
};

class xsd_simple_type:
    public xsd_node
{
public:
    xsd_simple_type();
    ~xsd_simple_type();
    xsd_node_type get_type() const override { return xnt_simple_type; }
    void set_ref(xsd_simple_type* r) { _ref = r; }
    xsd_simple_type* get_ref() const { return _ref; }
    void set_substitution(xsd_st_substitution* sub);
    bool build_assigner(string& code, const string& arg, const string& indent);

protected:
    xsd_simple_type*        _ref;
    xsd_st_substitution*    _substitution;
};

class xsd_complex_type:
    public xsd_node
{
public:
    xsd_node_type get_type() const override { return xnt_complex_type; }
    bool build_assigner(string& code, const string& arg, const string& indent) { return false; }
};

__gslib_end__

#endif