#pragma once

#include "Cesium3DTiles/BoundingVolume.h"
#include "Cesium3DTiles/Gltf.h"
#include "Cesium3DTiles/Library.h"
#include "Cesium3DTiles/RasterMappedTo3DTile.h"
#include "Cesium3DTiles/RasterOverlayTile.h"
#include "Cesium3DTiles/TileContext.h"
#include "Cesium3DTiles/TileID.h"
#include "Cesium3DTiles/TileRefine.h"
#include "Cesium3DTiles/TileSelectionState.h"
#include "CesiumAsync/IAssetRequest.h"
#include "CesiumGeospatial/Projection.h"
#include "CesiumUtility/DoublyLinkedList.h"
#include <atomic>
#include <glm/mat4x4.hpp>
#include <gsl/span>
#include <memory>
#include <optional>
#include <string>
#include <vector>

namespace Cesium3DTiles {
class Tileset;
class TileContent;
struct TileContentLoadResult;

/**
 * @brief A tile in a {@link Tileset}.
 *
 * The tiles of a tileset form a hierarchy, where each tile may contain
 * renderable content, and each tile has an associated bounding volume.
 *
 * The actual hierarchy is represented with the {@link Tile::getParent}
 * and {@link Tile::getChildren} functions.
 *
 * The renderable content is provided as a {@link TileContentLoadResult}
 * from the {@link Tile::getContent} function.
 * The {@link Tile::getGeometricError} function returns the geometric
 * error of the representation of the renderable content of a tile.
 *
 * The {@link BoundingVolume} is given by the {@link Tile::getBoundingVolume}
 * function. This bounding volume encloses the renderable content of the
 * tile itself, as well as the renderable content of all children, yielding
 * a spatially coherent hierarchy of bounding volumes.
 *
 * The bounding volume of the content of an individual tile is given
 * by the {@link Tile::getContentBoundingVolume} function.
 *
 */
class CESIUM3DTILES_API Tile final {
public:
  /**
   * The current state of this tile in the loading process.
   */
  enum class LoadState {
    /**
     * @brief This tile is in the process of being destroyed.
     *
     * Any pointers to it will soon be invalid.
     */
    Destroying = -3,

    /**
     * @brief Something went wrong while loading this tile and it will not be
     * retried.
     */
    Failed = -2,

    /**
     * @brief Something went wrong while loading this tile, but it may be a
     * temporary problem.
     */
    FailedTemporarily = -1,

    /**
     * @brief The tile is not yet loaded at all, beyond the metadata in
     * tileset.json.
     */
    Unloaded = 0,

    /**
     * @brief The tile content is currently being loaded.
     *
     * Note that while a tile is in this state, its {@link Tile::getContent},
     * and {@link Tile::getState}, methods may be called from the load thread,
     * and the state may change due to the internal loading process.
     */
    ContentLoading = 1,

    /**
     * @brief The tile content has finished loading.
     */
    ContentLoaded = 2,

    /**
     * @brief The tile is completely done loading.
     */
    Done = 3
  };

  /**
   * @brief Default constructor for an empty, uninitialized tile.
   */
  Tile() noexcept;

  /**
   * @brief Default destructor, which clears all resources associated with this
   * tile.
   */
  ~Tile();

  /**
   * @brief Copy constructor.
   *
   * @param rhs The other instance.
   */
  Tile(Tile& rhs) noexcept = delete;

  /**
   * @brief Move constructor.
   *
   * @param rhs The other instance.
   */
  Tile(Tile&& rhs) noexcept;

  /**
   * @brief Move assignment operator.
   *
   * @param rhs The other instance.
   */
  Tile& operator=(Tile&& rhs) noexcept;

  /**
   * @brief Returns the {@link Tileset} to which this tile belongs.
   */
  Tileset* getTileset() noexcept { return this->_pContext->pTileset; }

  /** @copydoc Tile::getTileset() */
  const Tileset* getTileset() const noexcept {
    return this->_pContext->pTileset;
  }

  /**
   * @brief Returns the {@link TileContext} of this tile.
   *
   * This function is not supposed to be called by clients.
   *
   * @return The tile context.
   */
  TileContext* getContext() noexcept { return this->_pContext; }

  /** @copydoc Tile::getContext() */
  const TileContext* getContext() const noexcept { return this->_pContext; }

  /**
   * @brief Set the {@link TileContext} of this tile.
   *
   * This function is not supposed to be called by clients.
   *
   * @param pContext The tile context.
   */
  void setContext(TileContext* pContext) noexcept {
    this->_pContext = pContext;
  }

  /**
   * @brief Returns the parent of this tile in the tile hierarchy.
   *
   * This will be the `nullptr` if this is the root tile.
   *
   * @return The parent.
   */
  Tile* getParent() noexcept { return this->_pParent; }

  /** @copydoc Tile::getParent() */
  const Tile* getParent() const noexcept { return this->_pParent; }

  /**
   * @brief Set the parent of this tile.
   *
   * This function is not supposed to be called by clients.
   *
   * @param pParent The parent tile .
   */
  void setParent(Tile* pParent) noexcept { this->_pParent = pParent; }

  /**
   * @brief Returns a *view* on the children of this tile.
   *
   * The returned span will become invalid when this tile is destroyed.
   *
   * @return The children of this tile.
   */
  gsl::span<Tile> getChildren() noexcept {
    return gsl::span<Tile>(this->_children);
  }

  /** @copydoc Tile::getChildren() */
  gsl::span<const Tile> getChildren() const noexcept {
    return gsl::span<const Tile>(this->_children);
  }

  /**
   * @brief Allocates space for the given number of child tiles.
   *
   * This function is not supposed to be called by clients.
   *
   * @param count The number of child tiles.
   * @throws `std::runtime_error` if this tile already has children.
   */
  void createChildTiles(size_t count);

  /**
   * @brief Assigns the given child tiles to this tile.
   *
   * This function is not supposed to be called by clients.
   *
   * @param children The child tiles.
   * @throws `std::runtime_error` if this tile already has children.
   */
  void createChildTiles(std::vector<Tile>&& children);

  /**
   * @brief Returns the {@link BoundingVolume} of this tile.
   *
   * This is a bounding volume that encloses the content of this tile,
   * as well as the content of all child tiles.
   *
   * @see Tile::getContentBoundingVolume
   *
   * @return The bounding volume.
   */
  const BoundingVolume& getBoundingVolume() const noexcept {
    return this->_boundingVolume;
  }

  /**
   * @brief Set the {@link BoundingVolume} of this tile.
   *
   * This function is not supposed to be called by clients.
   *
   * @param value The bounding volume.
   */
  void setBoundingVolume(const BoundingVolume& value) noexcept {
    this->_boundingVolume = value;
  }

  /**
   * @brief Returns the viewer request volume of this tile.
   *
   * The viewer request volume is an optional {@link BoundingVolume} that
   * may be associated with a tile. It allows controlling the rendering
   * process of the tile content: If the viewer request volume is present,
   * then the content of the tile will only be rendered when the viewer
   * (i.e. the camera position) is inside the viewer request volume.
   *
   * @return The viewer request volume, or an empty optional.
   */
  const std::optional<BoundingVolume>& getViewerRequestVolume() const noexcept {
    return this->_viewerRequestVolume;
  }

  /**
   * @brief Set the viewer request volume of this tile.
   *
   * This function is not supposed to be called by clients.
   *
   * @param value The viewer request volume.
   */
  void
  setViewerRequestVolume(const std::optional<BoundingVolume>& value) noexcept {
    this->_viewerRequestVolume = value;
  }

  /**
   * @brief Returns the geometric error of this tile.
   *
   * This is the error, in meters, introduced if this tile is rendered and its
   * children are not. This is used to compute screen space error, i.e., the
   * error measured in pixels.
   *
   * @return The geometric error of this tile, in meters.
   */
  double getGeometricError() const noexcept { return this->_geometricError; }

  /**
   * @brief Set the geometric error of the contents of this tile.
   *
   * This function is not supposed to be called by clients.
   *
   * @param value The geometric error, in meters.
   */
  void setGeometricError(double value) noexcept {
    this->_geometricError = value;
  }

  /**
   * @brief The refinement strategy of this tile.
   *
   * Returns the {@link TileRefine} value that indicates the refinement strategy
   * for this tile. This is `Add` when the content of the
   * child tiles is *added* to the content of this tile during refinement, and
   * `Replace` when the content of the child tiles *replaces*
   * the content of this tile during refinement.
   *
   * @return The refinement strategy.
   */
  TileRefine getRefine() const noexcept { return this->_refine; }

  /**
   * @brief Set the refinement strategy of this tile.
   *
   * This function is not supposed to be called by clients.
   *
   * @param value The refinement strategy.
   */
  void setRefine(TileRefine value) noexcept { this->_refine = value; }

  /**
   * @brief Gets the transformation matrix for this tile.
   *
   * This matrix does _not_ need to be multiplied with the tile's parent's
   * transform as this has already been done.
   *
   * @return The transform matrix.
   */
  const glm::dmat4x4& getTransform() const noexcept { return this->_transform; }

  /**
   * @brief Set the transformation matrix for this tile.
   *
   * This function is not supposed to be called by clients.
   *
   * @param value The transform matrix.
   */
  void setTransform(const glm::dmat4x4& value) noexcept {
    this->_transform = value;
  }

  /**
   * @brief Returns the {@link TileID} of this tile.
   *
   * This function is not supposed to be called by clients.
   *
   * @return The tile ID.
   */
  const TileID& getTileID() const noexcept { return this->_id; }

  /**
   * @brief Set the {@link TileID} of this tile.
   *
   * This function is not supposed to be called by clients.
   *
   * @param id The tile ID.
   */
  void setTileID(const TileID& id) noexcept;

  /**
   * @brief Returns the {@link BoundingVolume} of the renderable content of this
   * tile.
   *
   * The content bounding volume is a bounding volume that tightly fits only the
   * renderable content of the tile. This enables tighter view frustum culling,
   * making it possible to exclude from rendering any content not in the view
   * frustum.
   *
   * @see Tile::getBoundingVolume
   */
  const std::optional<BoundingVolume>&
  getContentBoundingVolume() const noexcept {
    return this->_contentBoundingVolume;
  }

  /**
   * @brief Set the {@link BoundingVolume} of the renderable content of this
   * tile.
   *
   * This function is not supposed to be called by clients.
   *
   * @param value The content bounding volume
   */
  void setContentBoundingVolume(
      const std::optional<BoundingVolume>& value) noexcept {
    this->_contentBoundingVolume = value;
  }

  /**
   * @brief Returns the {@link TileContentLoadResult} for the content of this
   * tile.
   *
   * This will be a `nullptr` if the content of this tile has not yet been
   * loaded, as indicated by the indicated by the {@link Tile::getState} of this
   * tile not being {@link Tile::LoadState::ContentLoaded}.
   *
   * @return The tile content load result, or `nullptr` if no content is loaded
   */
  TileContentLoadResult* getContent() noexcept { return this->_pContent.get(); }

  /** @copydoc Tile::getContent() */
  const TileContentLoadResult* getContent() const noexcept {
    return this->_pContent.get();
  }

  /**
   * @brief Returns internal resources required for rendering this tile.
   *
   * This function is not supposed to be called by clients.
   *
   * @return The renderer resources.
   */
  void* getRendererResources() const noexcept {
    return this->_pRendererResources;
  }

  /**
   * @brief Returns the {@link LoadState} of this tile.
   */
  LoadState getState() const noexcept {
    return this->_state.load(std::memory_order::memory_order_acquire);
  }

  /**
   * @brief Returns the {@link TileSelectionState} of this tile.
   *
   * This function is not supposed to be called by clients.
   *
   * @return The last selection state
   */
  TileSelectionState& getLastSelectionState() noexcept {
    return this->_lastSelectionState;
  }

  /** @copydoc Tile::getLastSelectionState() */
  const TileSelectionState& getLastSelectionState() const noexcept {
    return this->_lastSelectionState;
  }

  /**
   * @brief Set the {@link TileSelectionState} of this tile.
   *
   * This function is not supposed to be called by clients.
   *
   * @param newState The new stace
   */
  void setLastSelectionState(const TileSelectionState& newState) noexcept {
    this->_lastSelectionState = newState;
  }

  /**
   * @brief Returns the raster overlay tiles that have been mapped to this tile.
   */
  std::vector<RasterMappedTo3DTile>& getMappedRasterTiles() noexcept {
    return this->_rasterTiles;
  }

  /** @copydoc Tile::getMappedRasterTiles() */
  const std::vector<RasterMappedTo3DTile>&
  getMappedRasterTiles() const noexcept {
    return this->_rasterTiles;
  }

  /**
   * @brief Determines if this tile is currently renderable.
   */
  bool isRenderable() const noexcept;

  /**
   * @brief Trigger the process of loading the {@link Tile::getContent}.
   *
   * This function is not supposed to be called by clients.
   *
   * If this tile is not in its initial state (indicated by the
   * {@link Tile::getState} of this tile being *not*
   * {@link Tile::LoadState::Unloaded}), then nothing will be done.
   *
   * Otherwise, the tile will go into the
   * {@link Tile::LoadState::ContentLoading} state, and the request for
   * loading the tile content will be sent out.
   * The function will then return, and the response of the request will
   * be received asynchronously. Depending on the type of the tile and
   * the response, the tile will eventually go into the
   * {@link Tile::LoadState::ContentLoaded} state, and the
   * {@link Tile::getContent} will be available.
   */
  void loadContent();

  /**
   * @brief Frees all resources that have been allocated for the
   * {@link Tile::getContent}.
   *
   * This function is not supposed to be called by clients.
   *
   * If the operation for loading the tile content is currently in progress, as
   * indicated by the {@link Tile::getState} of this tile being
   * {@link Tile::LoadState::ContentLoading}), then nothing will be done,
   * and `false` will be returned.
   *
   * Otherwise, the resources that have been allocated for the tile content will
   * be freed.
   *
   * @return Whether the content was unloaded.
   */
  bool unloadContent() noexcept;

  /**
   * @brief Gives this tile a chance to update itself each render frame.
   *
   * @param previousFrameNumber The number of the previous render frame.
   * @param currentFrameNumber The number of the current render frame.
   */
  void update(int32_t previousFrameNumber, int32_t currentFrameNumber);

  /**
   * @brief Marks the tile as permanently failing to load.
   *
   * This function is not supposed to be called by clients.
   *
   * Moves the tile from the `FailedTemporarily` state to the `Failed` state.
   * If the tile is not in the `FailedTemporarily` state, this method does
   * nothing.
   */
  void markPermanentlyFailed() noexcept;

  /**
   * @brief Determines the number of bytes in this tile's geometry and texture
   * data.
   */
  int64_t computeByteSize() const noexcept;

private:
  /**
   * @brief Set the {@link LoadState} of this tile.
   */
  void setState(LoadState value) noexcept;

  /**
   * @brief Generates texture coordiantes for the raster overlays of the content
   * of this tile.
   *
   * This will extend the accessors of the glTF model of the content of this
   * tile with accessors that contain the texture coordinate sets for different
   * projections. Further details are not specified here.
   *
   * @return The bounding region
   */
  static std::optional<CesiumGeospatial::BoundingRegion>
  generateTextureCoordinates(
      CesiumGltf::Model& model,
      const BoundingVolume& boundingVolume,
      const std::vector<CesiumGeospatial::Projection>& projections);

  /**
   * @brief Upsample the parent of this tile.
   *
   * This method should only be called when this tile's parent is already
   * loaded.
   */
  void upsampleParent(std::vector<CesiumGeospatial::Projection>&& projections);

  // Position in bounding-volume hierarchy.
  TileContext* _pContext;
  Tile* _pParent;
  std::vector<Tile> _children;

  // Properties from tileset.json.
  // These are immutable after the tile leaves TileState::Unloaded.
  BoundingVolume _boundingVolume;
  std::optional<BoundingVolume> _viewerRequestVolume;
  double _geometricError;
  TileRefine _refine;
  glm::dmat4x4 _transform;

  TileID _id;
  std::optional<BoundingVolume> _contentBoundingVolume;

  // Load state and data.
  std::atomic<LoadState> _state;
  std::unique_ptr<TileContentLoadResult> _pContent;
  void* _pRendererResources;

  // Selection state
  TileSelectionState _lastSelectionState;

  // Overlays
  std::vector<RasterMappedTo3DTile> _rasterTiles;

  CesiumUtility::DoublyLinkedListPointers<Tile> _loadedTilesLinks;

public:
  /**
   * @brief A {@link CesiumUtility::DoublyLinkedList} for tile objects.
   */
  typedef CesiumUtility::DoublyLinkedList<Tile, &Tile::_loadedTilesLinks>
      LoadedLinkedList;
};

} // namespace Cesium3DTiles


#ifndef VKST_WSI_INPUT_H
#define VKST_WSI_INPUT_H

#include <gsl.h>
#include <wsi/types.h>
#include <bitset>

namespace wsi {

enum class keys {
  eUnknown = 0,

  eSpace = 32,
  eApostrophe = 39,
  eComma = 44,
  eMinus = 45,
  ePeriod = 46,
  eSlash = 47,
  e0 = 48,
  e1 = 49,
  e2 = 50,
  e3 = 51,
  e4 = 52,
  e5 = 53,
  e6 = 54,
  e7 = 55,
  e8 = 56,
  e9 = 57,
  eSemicolon = 59,
  eEqual = 61,
  eA = 65,
  eB = 66,
  eC = 67,
  eD = 68,
  eE = 69,
  eF = 70,
  eG = 71,
  eH = 72,
  eI = 73,
  eJ = 74,
  eK = 75,
  eL = 76,
  eM = 77,
  eN = 78,
  eO = 79,
  eP = 80,
  eQ = 81,
  eR = 82,
  eS = 83,
  eT = 84,
  eU = 85,
  eV = 86,
  eW = 87,
  eX = 88,
  eY = 89,
  eZ = 90,
  eLeftBracket = 91,
  eBackslash = 92,
  eRightBracket = 93,
  eGraveAccent = 96,

  eEscape = 156,
  eEnter = 157,
  eTab = 158,
  eBackspace = 159,
  eInsert = 160,
  eDelete = 161,
  eRight = 162,
  eLeft = 163,
  eDown = 164,
  eUp = 165,
  ePageUp = 166,
  ePageDown = 167,
  eHome = 168,
  eEnd = 169,

  eCapsLock = 180,
  eScrollLock = 181,
  eNumLock = 182,
  ePrintScreen = 183,
  ePause = 184,

  eF1 = 190,
  eF2 = 191,
  eF3 = 192,
  eF4 = 193,
  eF5 = 194,
  eF6 = 195,
  eF7 = 196,
  eF8 = 197,
  eF9 = 198,
  eF10 = 199,
  eF11 = 200,
  eF12 = 201,
  eF13 = 202,
  eF14 = 203,
  eF15 = 204,
  eF16 = 205,
  eF17 = 206,
  eF18 = 207,
  eF19 = 208,
  eF20 = 209,
  eF21 = 210,
  eF22 = 211,
  eF23 = 212,
  eF24 = 213,
  eF25 = 214,

  eKeypad0 = 220,
  eKeypad1 = 221,
  eKeypad2 = 222,
  eKeypad3 = 223,
  eKeypad4 = 224,
  eKeypad5 = 225,
  eKeypad6 = 226,
  eKeypad7 = 227,
  eKeypad8 = 228,
  eKeypad9 = 229,
  eKeypadDecimal = 230,
  eKeypadDivide = 231,
  eKeypadMultiply = 232,
  eKeypadSubtract = 233,
  eKeypadAdd = 234,
  eKeypadEnter = 235,
  eKeypadEqual = 236,

  eLeftShift = 240,
  eLeftControl = 241,
  eLeftAlt = 242,
  eLeftSuper = 243,
  eRightShift = 244,
  eRightControl = 245,
  eRightAlt = 246,
  eRightSuper = 247,
  eMenu = 248,
}; // enum class keys

class keyset final {
  std::bitset<256> _keys;

public:
  using reference = decltype(_keys)::reference;

  bool all() const { return _keys.all(); }
  bool any() const { return _keys.any(); }
  bool none() const { return _keys.none(); }

  constexpr bool operator[](keys key) const {
    return _keys[static_cast<std::size_t>(key)];
  }

  reference operator[](keys key) {
    return _keys[static_cast<std::size_t>(key)];
  }

  keyset& set(keys key, bool value = true) noexcept {
    _keys[static_cast<std::size_t>(key)] = value;
    return *this;
  }

  keyset& reset(keys key) noexcept {
    _keys[static_cast<std::size_t>(key)] = false;
    return *this;
  }
}; // class keyset

enum class buttons {
  e1 = 1,
  e2,
  e3,
  e4,
  e5,
  e6,
  e7,
  e8,
  e9,
  e10,
}; // enum class buttons

class buttonset final {
  std::bitset<32> _buttons;

public:
  using reference = decltype(_buttons)::reference;

  bool all() const { return _buttons.all(); }
  bool any() const { return _buttons.any(); }
  bool none() const { return _buttons.none(); }

  constexpr bool operator[](buttons button) const {
    return _buttons[static_cast<std::size_t>(button)];
  }

  reference operator[](buttons button) {
    return _buttons[static_cast<std::size_t>(button)];
  }

  buttonset& set(buttons button, bool value = true) noexcept {
    _buttons[static_cast<std::size_t>(button)] = value;
    return *this;
  }

  buttonset& reset(buttons button) noexcept {
    _buttons[static_cast<std::size_t>(button)] = false;
    return *this;
  }
}; // class buttonset

class window;

class input final {
public:
  input(gsl::not_null<window*> win) noexcept;

  bool key_pressed(keys key) const noexcept {
    return !_prev_keys[key] && _curr_keys[key];
  }

  bool key_released(keys key) const noexcept {
    return _prev_keys[key] && !_curr_keys[key];
  }

  bool key_down(keys key) const noexcept {
    return _prev_keys[key] && _curr_keys[key];
  }

  bool button_pressed(buttons button) const noexcept {
    return !_prev_buttons[button] && _curr_buttons[button];
  }

  bool button_released(buttons button) const noexcept {
    return _prev_buttons[button] && !_curr_buttons[button];
  }

  bool button_down(buttons button) const noexcept {
    return _prev_buttons[button] && _curr_buttons[button];
  }

  int prev_scroll() const noexcept { return _prev_scroll; }
  int curr_scroll() const noexcept { return _curr_scroll; }

  int scroll_delta() const noexcept { return _curr_scroll - _prev_scroll; }

  offset2d cursor_pos() const noexcept;

  offset2d prev_cursor_pos() const noexcept { return _prev_pos; }
  offset2d curr_cursor_pos() const noexcept { return _curr_pos; }

  offset2d cursor_delta() const noexcept { return _curr_pos - _prev_pos; }

  void tick() noexcept;

private:
  window* _win;
  keyset _prev_keys, _curr_keys;
  buttonset _prev_buttons, _curr_buttons;
  int _prev_scroll, _curr_scroll;
  offset2d _prev_pos, _curr_pos;
}; // class input

} // namespace wsi

#endif // VKST_WSI_INPUT_H


#include <bindings.cmacros.h>
#include <gsl/gsl_errno.h>

BC_INLINE2(GSL_ERROR_SELECT_2,int,int,int)
BC_INLINE3(GSL_ERROR_SELECT_3,int,int,int,int)
BC_INLINE4(GSL_ERROR_SELECT_4,int,int,int,int,int)
BC_INLINE5(GSL_ERROR_SELECT_5,int,int,int,int,int,int)
BC_INLINE2VOID(GSL_STATUS_UPDATE,int*,int)


#pragma once

#include "text_parser.h"
#include "text_renderer.h"
#include "terminal_io.h"
#include "geometry.h"

#include <string>
#include <vector>

#include <gsl/span>

namespace terminal_editor {

enum class Color {
    Black	        = 30,
    Red	            = 31,
    Green	        = 32,
    Yellow	        = 33,
    Blue	        = 34,
    Magenta	        = 35,
    Cyan	        = 36,
    White	        = 37,
    Bright_Black	= 90,
    Bright_Red	    = 91,
    Bright_Green	= 92,
    Bright_Yellow	= 93,
    Bright_Blue	    = 94,
    Bright_Magenta	= 95,
    Bright_Cyan	    = 96,
    Bright_White	= 97,
};

enum class Style {
    Bold        = 1,
    Normal      = 22,
};

struct Attributes {
    Color fgColor;
    Color bgColor;
    Style style;
};

class ScreenBuffer;

class ScreenCanvas {
    ScreenBuffer& m_screenBuffer;
    Point m_origin;  ///< Point in screen coordinates that all drawing functions are relative to.
    Rect m_clipRect; ///< In screen coordinates.
public:
    /// clipRect will be clipped to screenBuffer bounds.
    ScreenCanvas(ScreenBuffer& screenBuffer, Point origin, Rect clipRect);

    /// Sub canvas will be clipped to this canvas size.
    /// @param rect     Sub rectangle of this canvas. Rect is relative to this canvas' origin.
    ScreenCanvas getSubCanvas(Rect rect);

    /// Clears canvas to given color.
    void clear(Color bgColor) {
        auto localRect = m_clipRect;
        localRect.move(-m_origin.asSize()); // In local coordinates.
        fill(localRect, bgColor);
    }

    /// Draw filled rectangle.
    void fill(Rect rect, Color bgColor);

    /// Draw rectangle.
    void fillRect(Rect rect, bool doubleEdge, bool fill, Attributes attributes);

    /// Draws given text on the canvas.
    /// Text is clipped to boundaries of the canvas.
    /// So only graphemes fully inside are drawn.
    /// @param text     Input string, doesn't have to be valid or printable UTF-8.
    void print(Point pt, const std::string& text, Attributes normal, Attributes invalid, Attributes replacement);

    /// Draws given text on the canvas.
    /// Text is clipped to boundaries of the canvas.
    /// So only graphemes fully inside are drawn.
    /// @param graphemes    Graphemes to draw.
    void print(Point pt, gsl::span<const Grapheme> graphemes, Attributes normal, Attributes invalid, Attributes replacement);
};

class ScreenBuffer {
public:
    struct Character {
        std::string text; ///< UTF-8 text to draw. If empty, then this place will be drawn by preceeding character with width > 1.
        int width;        ///< Width of text once it will be rendered.
        Attributes attributes;

        bool operator==(const Character& other) const {
            if (text != other.text) return false;
            if (width != other.width) return false;
            if (attributes.fgColor != other.attributes.fgColor) return false;
            if (attributes.bgColor != other.attributes.bgColor) return false;
            if (attributes.style != other.attributes.style) return false;
            return true;
        }
    };

private:
    Size size;
    std::vector<Character> characters;
    std::vector<Character> previousCharacters;
    bool fullRepaintNeeded;     ///< If true while screen will be repainted. Otherwise only changes from previousCharacters will be repainted.

public:
    ScreenBuffer()
        : fullRepaintNeeded(true) {
    }

    ScreenCanvas getCanvas() {
        return ScreenCanvas(*this, Point{0, 0}, getSize());
    }

    /// Sets fullRepaintNeeded flag. Used when contents of the screen were changed without ScreenBuffer.
    void setFullRepaintNeeded() {
        fullRepaintNeeded = true;
    }

    Size getSize() const {
        return size;
    }

    int getWidth() const {
        return size.width;
    }

    int getHeight() const {
        return size.height;
    }

    /// Resizes this screen buffer.
    void resize(int w, int h);

    /// Clears screen to given color.
    void clear(Color bgColor);

    /// Draws a filled rectangle with given color.
    /// Rectangle is first clipped to fit the scren buffer.
    void fillRect(Rect rect, Color bgColor);

    /// Draws given text on the screen.
    /// Throws if text is not entirely on the screen.
    /// @param text     Input string, doesn't have to be valid or printable UTF-8.
    void print(int x, int y, const std::string& text, Attributes attributes);

    /// Draws given graphemes on the screen.
    /// Throws if text is not entirely on the screen.
    /// @param graphemes    Graphemes to draw.
    void print(int x, int y, gsl::span<const Grapheme> graphemes, Attributes attributes);

    /// Draws this screen buffer to the console.
    void present();
};

/// Returns Grapheme that corresponds to given simpleCharacter.
/// @param simpleCharacter      Must be a valid UTF-8 string, that converts to a printable character of width 1.
template<int N>
Grapheme simpleGrapheme(const char (&simpleChar)[N]) {
    return { GraphemeKind::NORMAL, simpleChar, "", 1, {simpleChar, N - 1} };
}

/// Draws a rectangle with borders.
/// Rectangle is clipped by the clipRect. clipRect must be wholy inside screen buffer.
void draw_rect(ScreenBuffer& screenBuffer, Rect clipRect, Rect rect, bool doubleEdge, bool fill, Attributes attributes);

/// Measures given text on the terminal
/// @param eventQueue   Event queue to use for listening for the result.
/// @param codePoints   List of code points to measure.
/// @return Length of the code points. Can be zero.
int measureText(EventQueue& eventQueue, gsl::span<uint32_t> codePoints);

} // namespace terminal_editor


/* block/gsl_block_char.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_BLOCK_CHAR_H__
#define __GSL_BLOCK_CHAR_H__

#include <stdlib.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_types.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

struct gsl_block_char_struct
{
  size_t size;
  char *data;
};

typedef struct gsl_block_char_struct gsl_block_char;

GSL_EXPORT gsl_block_char *gsl_block_char_alloc (const size_t n);
GSL_EXPORT gsl_block_char *gsl_block_char_calloc (const size_t n);
GSL_EXPORT void gsl_block_char_free (gsl_block_char * b);

GSL_EXPORT int gsl_block_char_fread (FILE * stream, gsl_block_char * b);
GSL_EXPORT int gsl_block_char_fwrite (FILE * stream, const gsl_block_char * b);
GSL_EXPORT int gsl_block_char_fscanf (FILE * stream, gsl_block_char * b);
GSL_EXPORT int gsl_block_char_fprintf (FILE * stream, const gsl_block_char * b, const char *format);

GSL_EXPORT int gsl_block_char_raw_fread (FILE * stream, char * b, const size_t n, const size_t stride);
GSL_EXPORT int gsl_block_char_raw_fwrite (FILE * stream, const char * b, const size_t n, const size_t stride);
GSL_EXPORT int gsl_block_char_raw_fscanf (FILE * stream, char * b, const size_t n, const size_t stride);
GSL_EXPORT int gsl_block_char_raw_fprintf (FILE * stream, const char * b, const size_t n, const size_t stride, const char *format);

GSL_EXPORT size_t gsl_block_char_size (const gsl_block_char * b);
GSL_EXPORT char * gsl_block_char_data (const gsl_block_char * b);

__END_DECLS

#endif /* __GSL_BLOCK_CHAR_H__ */


/*
  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 __API_MCAS_ITF__
#define __API_MCAS_ITF__

#include <api/components.h>
#include <api/kvindex_itf.h>
#include <api/kvstore_itf.h>
#include <boost/optional.hpp>
#include <common/byte_span.h>
#include <common/pointer_cast.h>
#include <common/string_view.h>
#include <gsl/span>

#include <array>
#include <cstdint> /* uint16_t */
#include <memory>

#define DECLARE_OPAQUE_TYPE(NAME)               \
  struct Opaque_##NAME {                        \
    virtual ~Opaque_##NAME() {}                 \
  }

namespace component
{
/**
 * mcas client interface (this will include both KV and AS capabilities)
 */

class IMCAS : public component::IBase,
              public KVStore
{
public:
  // clang-format off
  DECLARE_INTERFACE_UUID(0x33af1b99,0xbc51,0x49ff,0xa27b,0xd4,0xe8,0x19,0x03,0xbb,0x02);
  // clang-format on

public:
  DECLARE_OPAQUE_TYPE(async_handle);

  using async_handle_t  = Opaque_async_handle*;
  using pool_t          = component::KVStore::pool_t;
  using key_t           = KVStore::key_t;
  using Attribute       = KVStore::Attribute;
  using Addr            = KVStore::Addr;

  static constexpr async_handle_t  ASYNC_HANDLE_INIT  = nullptr;

  enum {
        FLAGS_NONE        = KVStore::FLAGS_NONE,
        FLAGS_READ_ONLY   = KVStore::FLAGS_READ_ONLY,
        FLAGS_SET_SIZE    = KVStore::FLAGS_SET_SIZE,
        FLAGS_CREATE_ONLY = KVStore::FLAGS_CREATE_ONLY,
        FLAGS_DONT_STOMP  = KVStore::FLAGS_DONT_STOMP,
        FLAGS_NO_RESIZE   = KVStore::FLAGS_NO_RESIZE,
        FLAGS_MAX_VALUE   = KVStore::FLAGS_MAX_VALUE,
  };

  /* per-shard statistics */
  struct Shard_stats {
    uint64_t op_request_count;
    uint64_t op_put_count;
    uint64_t op_get_count;
    uint64_t op_put_direct_count;
    uint64_t op_get_direct_count;
    uint64_t op_get_twostage_count;
    uint64_t op_ado_count;
    uint64_t op_erase_count;
    uint64_t op_get_direct_offset_count;
    uint64_t op_failed_request_count;
    uint64_t last_op_count_snapshot;
    uint16_t client_count;

  public:
    Shard_stats()
      : op_request_count(0)
      , op_put_count(0), op_get_count(0), op_put_direct_count(0), op_get_direct_count(0), op_get_twostage_count(0)
      , op_ado_count(0), op_erase_count(0), op_get_direct_offset_count(0)
      , op_failed_request_count(0), last_op_count_snapshot(0), client_count(0)
    {
    }
  } __attribute__((packed));

  using ado_flags_t = uint32_t;

  static constexpr ado_flags_t ADO_FLAG_NONE = 0;
  /*< operation is asynchronous */
  static constexpr ado_flags_t ADO_FLAG_ASYNC = (1 << 0);
  /*< create KV pair if needed */
  static constexpr ado_flags_t ADO_FLAG_CREATE_ON_DEMAND = (1 << 1);
  /*< create only - allocate key,value but don't call ADO */
  static constexpr ado_flags_t ADO_FLAG_CREATE_ONLY = (1 << 2);
  /*< do not overwrite value if it already exists */
  static constexpr ado_flags_t ADO_FLAG_NO_OVERWRITE = (1 << 3);
  /*< create value but do not attach to key, unless key does not exist */
  static constexpr ado_flags_t ADO_FLAG_DETACHED = (1 << 4);
  /*< only take read lock */
  static constexpr ado_flags_t ADO_FLAG_READ_ONLY = (1 << 5);
  /*< zero any newly allocated value memory */
  static constexpr ado_flags_t ADO_FLAG_ZERO_NEW_VALUE = (1 << 6);
  /*< internal use only: on return provide IO response */
  static constexpr ado_flags_t ADO_FLAG_INTERNAL_IO_RESPONSE = (1 << 7);
  /*< internal use only: on return provide IO response with value buffer */
  static constexpr ado_flags_t ADO_FLAG_INTERNAL_IO_RESPONSE_VALUE = (1 << 8);

public:
  /**
   * Determine thread safety of the component
   *
   *
   * @return THREAD_MODEL_XXXX
   */
  virtual int thread_safety() const = 0;

  /**
   * If the ADO is configured for the shard then the ADO process is
   * instantiated "attached" to the pool.
   *
   * The "base" parameter is unused.
   */
  using KVStore::create_pool;
  using KVStore::open_pool;

  using KVStore::delete_pool;

  /**
   * Close and delete an existing pool from a pool handle. Only one
   * reference count should exist. Any ADO plugin is notified before
   * the pool is deleted.
   *
   * @param pool Pool handle
   *
   * @return S_OK or E_BUSY if reference count > 1
   */
  virtual status_t delete_pool(const pool_t pool) = 0;

  /**
   * Configure a pool
   *
   * @param setting Configuration request (e.g., AddIndex::VolatileTree)

   *
   * @return S_OK on success
   */
  virtual status_t configure_pool(const pool_t pool, const std::string& setting) = 0;

  using KVStore::put;

  virtual status_t put(const pool_t pool,
                       const std::string&  key,
                       const std::string&  value,
                       const unsigned int  flags = FLAGS_NONE)
  {
    /* this does not store any null terminator */
    return put(pool, key, value.data(), value.length(), flags);
  }

  /*
   * Asynchronous put operation.  Use check_async_completion to check for
   * completion. This operation is not normally used, simple put is fast.
   *
   * @param pool Pool handle
   * @param key Object key
   * @param value Value

   * @param value_len Value length in bytes
   * @param out_handle Async work handle
   * @param flags Optional flags
   *
   * @return S_OK or other error code
   */
  virtual status_t async_put(const IMCAS::pool_t pool,
                             const std::string&  key,
                             const void*         value,
                             const size_t        value_len,
                             async_handle_t&     out_handle,
                             const unsigned int  flags = IMCAS::FLAGS_NONE) = 0;

  virtual status_t async_put(const IMCAS::pool_t pool,
                             const std::string&  key,
                             const std::string&  value,
                             async_handle_t&     out_handle,
                             const unsigned int  flags = IMCAS::FLAGS_NONE)
  {
    (void)out_handle; // unused
    return async_put(pool, key, value.data(), value.length(), out_handle, flags);
  }

  /**
   * Zero-copy only if value size > ~2MiB or FORCE_DIRECT=1 is set.
   */
  using KVStore::put_direct;

  /**
   * Asynchronous put_direct operation.  Use check_async_completion to check for
   * completion.
   *
   * @param pool Pool handle
   * @param key Object key
   * @param value Value
   * @param value_len Value length in bytes
   * @param handle Memory registration handle
   * @param out_handle Async handle
   * @param flags Optional flags
   *
   * @return S_OK or other error code
   */
  virtual status_t async_put_direct(const IMCAS::pool_t   pool,
                                    const std::string&    key,
                                    const void*           value,
                                    const size_t          value_len,
                                    async_handle_t&       out_handle,
                                    const memory_handle_t handle = IMCAS::MEMORY_HANDLE_NONE,
                                    const unsigned int    flags  = IMCAS::FLAGS_NONE)
  {
    return
      async_put_direct(
                       pool
                       , key
                       , std::array<common::const_byte_span,1>{common::make_const_byte_span(value, value_len)}
                       , out_handle, std::array<memory_handle_t,1>{handle}
                       , flags
                       );
  }

  /**
   * Asynchronous put_direct operation.  Use check_async_completion to check for
   * completion.
   *
   * @param pool Pool handle
   * @param key Object key
   * @param values list of value sources
   * @param out_handle Async handle
   * @param handle List of memory registration handle
   * @param flags Optional flags
   *
   * @return S_OK or other error code
   */
  virtual status_t async_put_direct(const IMCAS::pool_t   pool,
                                    const std::string&    key,
                                    gsl::span<const common::const_byte_span> value,
                                    async_handle_t&       out_handle,
                                    gsl::span<const memory_handle_t> handles = gsl::span<const memory_handle_t>(),
                                    const unsigned int    flags  = IMCAS::FLAGS_NONE) = 0;

  using KVStore::get;

  virtual status_t get(const pool_t pool,
                       const std::string& key,
                       std::string& out_value)
  {
    void*  val      = nullptr;
    size_t val_size = 0;
    auto   s        = this->get(pool, key, val, val_size);

    /* copy result */
    if (s == S_OK) {
      out_value.assign(static_cast<char*>(val), val_size);
      this->free_memory(val);
    }
    return s;
  }

  /**
   * Asynchronously read an object value directly into client-provided memory.
   *
   * @param pool Pool handle
   * @param key Object key
   * @param out_value Client provided buffer for value
   * @param out_value_len [in] size of value memory in bytes [out] size of value
   * @param out_handle Async work handle
   * @param handle Memory registration handle
   *
   * @return S_OK, S_MORE if only a portion of value is read, E_BAD_ALIGNMENT on
   * invalid alignment, or other error code
   */
  virtual status_t async_get_direct(const IMCAS::pool_t          pool,
                                    const std::string&           key,
                                    void*                        out_value,
                                    size_t&                      out_value_len,
                                    async_handle_t&              out_handle,
                                    const IMCAS::memory_handle_t handle = IMCAS::MEMORY_HANDLE_NONE) = 0;

  /**
   * Read memory directly into client-provided memory.
   *
   * @param pool Pool handle
   * @param offset offset within ithe concatenation of the pool's memory regions
   * @param size requested size (becomes available size)
   * @param out_buffer Client provided buffer for value
   * @param out_handle Async work handle
   * @param handle Memory registration handle
   *
   * @return S_OK, or error code
   */
  virtual status_t async_get_direct_offset(const IMCAS::pool_t pool,
                                           const offset_t offset,
                                           size_t &size,
                                           void* out_buffer,
                                           async_handle_t& out_handle,
                                           const IMCAS::memory_handle_t handle = IMCAS::MEMORY_HANDLE_NONE) = 0;

  virtual status_t get_direct_offset(const IMCAS::pool_t pool,
                                     const offset_t offset,
                                     size_t &size,
                                     void* out_buffer,
                                     const IMCAS::memory_handle_t handle = IMCAS::MEMORY_HANDLE_NONE) = 0;

  /**
   * Write memory directly into client-provided memory.
   *
   * @param pool Pool handle
   * @param offset offset within ithe concatenation of the pool's memory regions
   * @param size offered size (becomes available size)
   * @param buffer Client provided value
   * @param out_handle Async work handle
   * @param handle Memory registration handle
   *
   * @return S_OK, or error code
   */
  virtual status_t async_put_direct_offset(const IMCAS::pool_t pool,
                                           const offset_t offset,
                                           size_t &size,
                                           const void *const buffer,
                                           async_handle_t& out_handle,
                                           const IMCAS::memory_handle_t handle = IMCAS::MEMORY_HANDLE_NONE) = 0;

  virtual status_t put_direct_offset(const IMCAS::pool_t pool,
                                     const offset_t offset,
                                     size_t &size,
                                     const void *const buffer,
                                     const IMCAS::memory_handle_t handle = IMCAS::MEMORY_HANDLE_NONE) = 0;

  /**
   * Check for completion from asynchronous invocation
   *
   * @param handle Asynchronous work handle.
   *
   * @return S_OK or E_BUSY if not yet complete
   */
  virtual status_t check_async_completion(async_handle_t& handle) = 0;

  /**
   * Perform key search based on regex or prefix
   *
   * @param pool Pool handle
   * @param key_expression Regular expression or prefix (e.g. "prefix:carKey")
   * @param offset Offset from which to search
   * @param out_matched_offset Out offset of match
   * @param out_keys Out vector of matching keys
   *
   * @return S_OK on success
   */
  virtual status_t find(const IMCAS::pool_t pool,
                        const std::string&  key_expression,
                        const offset_t      offset,
                        offset_t&           out_matched_offset,
                        std::string&        out_matched_key) = 0;

  /**
   * Erase an object asynchronously
   *
   * @param pool Pool handle
   * @param key Object key
   * @param out_handle Async work handle
   *
   * @return S_OK or error code
   */
  virtual status_t async_erase(const IMCAS::pool_t pool, const std::string& key, async_handle_t& out_handle) = 0;

  /**
   * Retrieve shard statistics
   *
   * @param out_stats
   *
   * @return S_OK on success
   */
  virtual status_t get_statistics(Shard_stats& out_stats) = 0;

  /**
   * ADO_response data structure manages response data sent back from the ADO
   * invocations.  The free function is so we can eventually support zero-copy.
   * The layer id identifies which ADO plugin the response came from.
   */
  class ADO_response {

  private:

#pragma GCC diagnostic push /* pointer members are considered inefficient */
#pragma GCC diagnostic ignored "-Weffc++"

    class Data_reference {
    public:
      Data_reference(void * data) : _data(data) { assert(data); }
      Data_reference() = delete;
      virtual ~Data_reference() { assert(_data); ::free(_data); }
      void * _data;
    };

#pragma GCC diagnostic pop

  public:
    ADO_response() = delete;

    ADO_response(void* data, size_t data_len, uint32_t layer_id)
      : _ref(std::make_shared<Data_reference>(data)),
        _data_len(data_len),
        _layer_id(layer_id) {}

    ADO_response(ADO_response&& src) noexcept
      : _ref(src.datasp()),
        _data_len(src.data_len()),
        _layer_id(src.layer_id()) {}

    inline std::string str() const { return std::string(data(), data_len()); }
    inline const char* data() const { return static_cast<const char*>(_ref->_data); }
    inline size_t data_len() const { return _data_len; }
    inline uint32_t layer_id() const { return _layer_id; }
    inline std::shared_ptr<Data_reference>& datasp() { return _ref; }

    template <typename T>
    inline T* cast_data() const  {  return static_cast<T*>(_ref->_data);   }

  private:
    std::shared_ptr<Data_reference> _ref; /* smart pointer */
    size_t                          _data_len;
    uint32_t                        _layer_id; /* optional layer identifier */
  };

  /**
   * Used to invoke an operation on an active data object
   *
   * Roughly, the shard locates a data value by key and calls ADO (with accessors to both the key and data).
   *   Several variations:
   *       1) Skip the "locate data" operation (could have been directed by a null
   *          key address in the basic_string_view form, or by a flag, but is
   *          instead indicated by a zero-length key)
   *       2) Skip the ADO call (directed by ADO_FLAG_CREATE_ONLY)
   *       3) Create uninitialized data of size value_size if the key was not found
   *          (directed by ADO_FLAG_CREATE_ON_DEMAND)
   *       4) Create uninitialized data of size value_size if the key was not found(?)
   *          not associated with the key and maybe also create a "root value" which
   *          which is attached to the key (directed by ADO_FLAG_DETACHED)
   *
   * @param pool Pool handle
   * @param key Key. Note, if key is empty, the work request is key-less.
   * @param request Request data
   * @param request_len Length of request in bytes
   * @param flags Flags for invocation (see ADO_FLAG_CREATE_ONLY, ADO_FLAG_READ_ONLY)
   * @param out_response Responses from invocation
   * @param value_size Optional parameter to define value size to create for
   * on-demand
   *
   * @return S_OK on success
   */
  virtual status_t invoke_ado(const IMCAS::pool_t           pool,
                              const basic_string_view<byte> key,
                              const basic_string_view<byte> request,
                              const ado_flags_t             flags,
                              std::vector<ADO_response>&    out_response,
                              const size_t                  value_size = 0) = 0;

  virtual status_t invoke_ado(const IMCAS::pool_t        pool,
                              const std::string&         key,
                              const void*                request,
                              const size_t               request_len,
                              const ado_flags_t          flags,
                              std::vector<ADO_response>& out_response,
                              const size_t               value_size = 0)
  {
    return
      invoke_ado(pool,
                 basic_string_view<byte>(common::pointer_cast<const byte>(key.data()), key.size()),
                 basic_string_view<byte>(static_cast<const byte *>(request), request_len),
                 flags, out_response, value_size);
  }

  inline status_t invoke_ado(const IMCAS::pool_t        pool,
                             const std::string&         key,
                             const std::string&         request,
                             const ado_flags_t          flags,
                             std::vector<ADO_response>& out_response,
                             const size_t               value_size = 0)
  {
    return
      invoke_ado(pool, key, request.data(), request.length(), flags, out_response, value_size);
  }

  /**
   * Used to asynchronously invoke an operation on an ADO
   *
   * Roughly, the shard locates a data value by key and calls ADO (with accessors to both the key and data).
   *
   * @param pool Pool handle
   * @param key Key. Note, if key is empty, the work request is key-less.
   * @param request Request data
   * @param request_len Length of request in bytes
   * @param flags Flags for invocation (see ADO_FLAG_XXX)
   * @param out_response Response passed back from ADO invocation
   * @param out_async_handle Handle to task for later result collection
   * @param value_size Optional parameter to define value size to create for on-demand
   *
   * @return S_OK on success
   */
  virtual status_t async_invoke_ado(const IMCAS::pool_t           pool,
                                    const basic_string_view<byte> key,
                                    const basic_string_view<byte> request,
                                    const ado_flags_t             flags,
                                    std::vector<ADO_response>&    out_response,
                                    async_handle_t&               out_async_handle,
                                    const size_t                  value_size = 0) = 0;

  inline status_t async_invoke_ado(const IMCAS::pool_t        pool,
                                   const std::string&         key,
                                   const std::string&         request,
                                   const ado_flags_t          flags,
                                   std::vector<ADO_response>& out_response,
                                   async_handle_t&            out_async_handle,
                                   const size_t               value_size = 0)
  {
    return
      async_invoke_ado(pool,
                       basic_string_view<byte>(common::pointer_cast<const byte>(key.data()), key.size()),
                       basic_string_view<byte>(common::pointer_cast<const byte>(request.data()), request.length()),
                       flags, out_response, out_async_handle, value_size);
  }

  inline status_t async_invoke_ado(const IMCAS::pool_t        pool,
                                   const std::string&         key,
                                   const void*                request,
                                   const size_t               request_len,
                                   const ado_flags_t          flags,
                                   std::vector<ADO_response>& out_response,
                                   async_handle_t&            out_async_handle,
                                   const size_t               value_size = 0)
  {
    return
      async_invoke_ado(pool,
                       basic_string_view<byte>(common::pointer_cast<const byte>(key.data()), key.size()),
                       basic_string_view<byte>(static_cast<const byte *>(request), request_len),
                       flags, out_response, out_async_handle, value_size);
  }

  /**
   * Used to invoke a combined put + ADO operation on an active data object.
   *
   * Roughly, the shard writes a data value by key and calls ADO (with accessors to both the key and data).
   *
   * @param pool Pool handle
   * @param key Key
   * @param request Request data
   * @param request_len Length of request data in bytes
   * @param value Value data
   * @param value_len Length of value data in bytes
   * @param root_len Length to allocate for root value (with ADO_FLAG_DETACHED)
   * @param flags Flags for invocation (ADO_FLAG_NO_OVERWRITE, ADO_FLAG_DETACHED)
   * @param out_response Response passed back from ADO invocation
   *
   * @return S_OK on success
   */
  virtual status_t invoke_put_ado(const IMCAS::pool_t           pool,
                                  const basic_string_view<byte> key,
                                  const basic_string_view<byte> request,
                                  const basic_string_view<byte> value,
                                  const size_t                  root_len,
                                  const ado_flags_t             flags,
                                  std::vector<ADO_response>&    out_response) = 0;

  virtual status_t invoke_put_ado(const IMCAS::pool_t        pool,
                                  const std::string&         key,
                                  const void*                request,
                                  const size_t               request_len,
                                  const void*                value,
                                  const size_t               value_len,
                                  const size_t               root_len,
                                  const ado_flags_t          flags,
                                  std::vector<ADO_response>& out_response)
  {
    return
      invoke_put_ado(pool,
                     basic_string_view<byte>(common::pointer_cast<const byte>(key.data()), key.size()),
                     basic_string_view<byte>(static_cast<const byte *>(request), request_len),
                     basic_string_view<byte>(static_cast<const byte *>(value), value_len),
                     root_len, flags, out_response);
  }

  inline status_t invoke_put_ado(const IMCAS::pool_t        pool,
                                 const std::string&         key,
                                 const std::string&         request,
                                 const std::string&         value,
                                 const size_t               root_len,
                                 const ado_flags_t          flags,
                                 std::vector<ADO_response>& out_response)
  {
    return invoke_put_ado(pool,
                          key,
                          request.data(),
                          request.length(),
                          value.data(),
                          value.length(),
                          root_len,
                          flags,
                          out_response);
  }

  /**
   * Used to asynchronously invoke a combined put + ADO operation on an active data object.
   *
   * Roughly, the shard writes a data value by key and calls ADO (with accessors to both the key and data).
   *
   * @param pool Pool handle
   * @param key Key
   * @param request Request data
   * @param request_len Length of request data in bytes
   * @param value Value data
   * @param value_len Length of value data in bytes
   * @param root_len Length to allocate for root value (with ADO_FLAG_DETACHED)
   * @param flags Flags for invocation (ADO_FLAG_NO_OVERWRITE, ADO_FLAG_DETACHED)
   * @param out_response Responses from ADO invocation
   * @param out_async_handle Handle to task for later result collection
   *
   * @return S_OK on success
   */
  virtual status_t async_invoke_put_ado(const IMCAS::pool_t           pool,
                                        const basic_string_view<byte> key,
                                        const basic_string_view<byte> request,
                                        const basic_string_view<byte> value,
                                        const size_t                  root_len,
                                        const ado_flags_t             flags,
                                        std::vector<ADO_response>&    out_response,
                                        async_handle_t&               out_async_handle) = 0;

  virtual status_t async_invoke_put_ado(const IMCAS::pool_t        pool,
                                        const std::string&         key,
                                        const void*                request,
                                        const size_t               request_len,
                                        const void*                value,
                                        const size_t               value_len,
                                        const size_t               root_len,
                                        const ado_flags_t          flags,
                                        std::vector<ADO_response>& out_response,
                                        async_handle_t&            out_async_handle)
  {
    return
      async_invoke_put_ado(pool,
                           basic_string_view<byte>(common::pointer_cast<const byte>(key.data()), key.size()),
                           basic_string_view<byte>(static_cast<const byte *>(request), request_len),
                           basic_string_view<byte>(static_cast<const byte *>(value), value_len),
                           root_len, flags, out_response, out_async_handle);
  }

  inline status_t async_invoke_put_ado(const IMCAS::pool_t        pool,
                                       const std::string&         key,
                                       const std::string&         request,
                                       const std::string&         value,
                                       const size_t               root_len,
                                       const ado_flags_t          flags,
                                       std::vector<ADO_response>& out_response,
                                       async_handle_t&            out_async_handle)
  {
    return async_invoke_put_ado(pool,
                                key,
                                request.data(),
                                request.length(),
                                value.data(),
                                value.length(),
                                root_len,
                                flags,
                                out_response,
                                out_async_handle);
  }

  /**
   * Debug routine
   *
   * @param pool Pool handle
   * @param cmd Debug command
   * @param arg Parameter for debug operation
   */
  virtual void debug(const IMCAS::pool_t pool, const unsigned cmd, const uint64_t arg) = 0;
};

class IMCAS_factory : public IKVStore_factory {
  using string_view = common::string_view;
public:
  // clang-format off
  DECLARE_INTERFACE_UUID(0xfacf1b99,0xbc51,0x49ff,0xa27b,0xd4,0xe8,0x19,0x03,0xbb,0x02);
  // clang-format on

  /**
   * Create a session to a remote shard
   *
   * @param debug_level         Debug level (0-3)
   * @param patience            Time out patience in seconds
   * @param owner               Owner info (not used)
   * @param src_nic_device      Client-side network device (e.g., mlx5_0, eth0)
   * @param src_ip_addr         Client-side IP address
   * @param dest_addr_with_port Server-side IP address and port (e.g. 10.0.0.21:11911, 9.1.75.6:11911:sockets)
   * @param other               Other optional parameters (e.g. { "security":"tls:auth" })
   *
   * @return Pointer to IMCAS instance. Use release_ref() to close.
   */
  virtual IMCAS* mcas_create_nsd(const unsigned, // debug_level
                                 const unsigned, // patience
                                 const string_view, // owner
                                 const string_view, // src_nic_device
                                 const string_view, // src_ip_addr
                                 const string_view, // dest_addr_with_port
                                 const string_view = string_view()) // other
  {
    throw API_exception("IMCAS_factory::mcas_create(debug_level,patience,owner,addr_with_port,nic_device) not implemented");
  }

  /**
   * Create a session to a remote shard (alternative)
   *
   * @param debug_level          Debug level
   * @param patience             Timeout patience in seconds
   * @param owner                Owner information (not used)
   * @param dest_addr_with_port  Destination server IP address and port
   * @param nic_device           Local NIC device to use (e.g., mlx5_0, eth0)
   * @param other                Other optional parameters (e.g. { "security":"tls:auth" })
   *
   * @return Pointer to IMCAS instance. Use release_ref() to close.
   */
  IMCAS* mcas_create(const unsigned    debug_level,
                     const unsigned    patience,
                     const string_view owner,
                     const string_view dest_addr_with_port,
                     const string_view nic_device,
                     const string_view other = string_view())
  {
    return mcas_create_nsd(debug_level, patience, owner, nic_device, string_view(), dest_addr_with_port, other);
  }
};

}  // namespace component

#endif


/*
 * 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 widget_c3b57c97_cea3_4794_b683_762a7b11af49_h
#define widget_c3b57c97_cea3_4794_b683_762a7b11af49_h

#include <gslib/std.h>
#include <gslib/dvt.h>
#include <gslib/uuid.h>
#include <ariel/sysop.h>
#include <ariel/painter.h>

__ariel_begin__

class wsys_manager;

class widget:
    public dvt_holder
{
    friend class wsys_manager;

public:
    widget(wsys_manager* m);
    virtual ~widget();
    virtual void* query_interface(const uuid& uid);
    virtual bool create(widget* ptr, const gchar* name, const rect& rc, uint style);
    virtual void close();
    virtual void show(bool b);
    virtual void enable(bool b);
    virtual void move(const rect& rc);
    virtual void refresh(const rect& rc, bool imm = false);
    virtual void draw(painter* paint) {}
    virtual widget* capture(bool b);
    virtual widget* focus();
    virtual bool hit_test(const point& pt) { return (_style & sm_hitable) != 0; }

public:
    enum laytag
    {
        lay_before,
        lay_after,
        lay_first,
        lay_last,
    };
    virtual void lay(widget* ptr, laytag t);

public:
    virtual void on_press(uint um, unikey uk, const point& pt);
    virtual void on_click(uint um, unikey uk, const point& pt);
    virtual void on_hover(uint um, const point& pt);
    virtual void on_leave(uint um, const point& pt);
    virtual void on_keydown(uint um, unikey uk) {}
    virtual void on_keyup(uint um, unikey uk) {}
    virtual void on_char(uint um, uint ch) {}
    virtual void on_caret() {}
    virtual void on_capture(bool b) {}
    virtual void on_focus(bool b) {}
    virtual void on_accelerator(unikey key, uint mask) {}

protected:
    wsys_manager*   _manager;
    string          _name;
    uint            _style;
    rect            _pos;
    bool            _visible;
    bool            _enabled;

public:
    const string& get_name() const { return _name; }
    const rect& get_rect() const { return _pos; }
    rectf get_rectf() const { return to_rectf(get_rect()); }
    bool is_visible() const { return _visible && (_style & sm_visible); }
    bool is_enabled() const { return _enabled; }
    bool is_focused() const;
    void hide() { show(false); }
    void disable() { enable(false); }
    point& to_global(point& pt) const;
    rect& to_global(rect& rc) const;
    point& to_local(point& pt) const;
    rect& to_local(rect& rc) const;
    void move(const point& pt);
    void resize(int w, int h);
    void refresh(bool imm);
    int get_width() const { return _pos.width(); }
    int get_height() const { return _pos.height(); }
    wsys_manager* get_manager() const { return _manager; }
    bool register_accelerator(unikey key, uint mask);
    widget* unregister_accelerator(unikey key, uint mask);

protected:
    widget*         _prev;
    widget*         _next;
    widget*         _child;
    widget*         _last_child;
    widget*         _parent;

public:
    widget* get_parent() const { return _parent; }
    widget* get_prev() const { return _prev; }
    widget* get_next() const { return _next; }
    widget* get_child() const { return _child; }
    widget* get_last_child() const { return _last_child; }

public:
    template<class _lamb>
    static int traverse_widget(widget* w, _lamb trav_to_stop)
    {
        assert(w);
        for(auto* p = w; p; p = p->get_next()) {
            if(auto r = trav_to_stop(p))
                return r;
        }
        return 0;
    }
    template<class _lamb>
    static int traverse_widget_reversed(widget* w, _lamb trav_to_stop)
    {
        assert(w);
        for(auto* p = w; p; p = p->get_prev()) {
            if(auto r = trav_to_stop(p))
                return r;
        }
        return 0;
    }
    template<class _lamb>
    int traverse_child_widget(_lamb trav_to_stop) { return traverse_widget(_child, trav_to_stop); }
    template<class _lamb>
    int traverse_child_widget_reversed(_lamb trav_to_stop) { return traverse_widget_reversed(_last_child, trav_to_stop); }
};

class button:
    public widget
{
public:
    typedef widget superref;
    typedef button self;

public:
    button(wsys_manager* m);
    virtual ~button();
    virtual void draw(painter* paint) override;
    virtual void enable(bool b) override;
    virtual void on_press(uint um, unikey uk, const point& pt) override;
    virtual void on_click(uint um, unikey uk, const point& pt) override;
    virtual void on_hover(uint um, const point& pt) override;
    virtual void on_leave(uint um, const point& pt) override;

protected:
    virtual void set_press();
    virtual void set_normal();
    virtual void set_hover();
    virtual void set_gray();

protected:
    texture2d*      _source;
    texture2d*      _bkground;
    bool            _4states;

public:
    void set_image(texture2d* img, bool fs = false);

    enum btnstate
    {
        bs_none     = 0,
        bs_press,
        bs_hover,
        bs_normal,
    };
    void set_btnstate(btnstate bs) { _btnstate = bs; }
    btnstate get_btnstate() const { return _btnstate; }

protected:
    btnstate        _btnstate;
};

class edit_line:
    public widget
{
public:
    typedef widget superref;
    typedef edit_line self;

public:
    edit_line(wsys_manager* m);
    virtual void draw(painter* paint) override;
    virtual void on_press(uint um, unikey uk, const point& pt) override;
    virtual void on_click(uint um, unikey uk, const point& pt) override;
    virtual void on_hover(uint um, const point& pt) override;
    virtual void on_char(uint um, uint ch) override;
    virtual void on_keydown(uint um, unikey uk) override;
    virtual void on_caret() override;
    virtual void on_focus(bool b) override;

protected:
    bool            _caret_on;
    int             _caretpos;
    string          _textbuf;
    int             _sel_start;
    int             _sel_end;

public:
    virtual void set_text(const gchar* str);
    virtual void set_caret(int n);
    virtual void set_select(int start, int end);
    virtual void replace_select(const gchar* str);

protected:
    virtual void draw_background(painter* paint);
    virtual void draw_normal_text(painter* paint);
    virtual void draw_select_text(painter* paint);
    virtual void draw_caret(painter* paint);

public:
    void set_font(const font& ft) { _font = ft; }
    void set_bkground(texture2d* ptr) { _bkground = ptr; }
    void set_text_color(color cr) { _txtcolor = cr; }
    void set_select_color(color cr) { _selcolor = cr; }
    void set_caret_color(color cr) { _crtcolor = cr; }
    const gchar* get_text() const { return _textbuf.c_str(); }
    void del_select();
    int hit_char(point pt);
    int prev_char(int pos);
    int next_char(int pos);
    int prev_logic_char(int pos);
    int next_logic_char(int pos);
    bool no_select() const { return _sel_start == -1 || _sel_start == _sel_end; }

protected:
    texture2d*      _bkground;
    color           _txtcolor;
    color           _selcolor;
    color           _crtcolor;
    font            _font;

protected:
    int trim_if_overrun(bool alarm);
};

typedef system_driver wsys_driver;
typedef system_notify wsys_notify;

class timer:
    public dvt_holder
{
public:
    timer(wsys_manager* mgr);
    void start(int elapse);
    void start_single(int elapse);
    void set_user_data(uint usd) { _userdata = usd; }
    uint get_user_data() const { return _userdata; }

public:
    virtual ~timer();
    virtual void on_timer(uint tid) {}
    virtual void on_notified();             /* CANNOT be used by connect notify */

protected:
    wsys_driver*    _driver;
    uint            _userdata;
    int             _elapse;
    bool            _single;

private:
    void initialize(wsys_driver* drv);
};

struct accel_key
{
    unikey          key;
    uint            mask;

public:
    accel_key(): key((unikey)-1), mask(0) {}        /* invalid */
    accel_key(unikey k, uint m): key(k), mask(m) {}
    bool is_valid() const { return key != (unikey)-1; }
    bool operator == (const accel_key& that) const { return key == that.key && mask == that.mask; }
    bool from_string(const string& str);
    const string& to_string(string& str) const;
};

class wsys_manager:
    public wsys_notify
{
public:
    wsys_manager();
    void set_wsysdrv(wsys_driver* drv);
    void set_painter(painter* paint);
    painter* get_painter() const { return _painter; }
    wsys_driver* get_driver() const { return _driver; }
    void initialize(const rect& rc);

protected:
    wsys_driver*    _driver;
    painter*        _painter;
    dirty_list      _dirty;
    int             _width;
    int             _height;
    timer*          _caret;

public:
    void set_dimension(int w, int h);
    int get_width() const { return _width; }
    int get_height() const { return _height; }
    void refresh() { _dirty.set_whole(); }
    void refresh(const rect& rc, bool imm = false);
    void update();
    void update(widget* w);
    widget* hit_test(point pt) { return hit_test(_root, pt); }
    widget* hit_test(widget* ptr, point pt);
    widget* hit_proc(const point& pt, point& pt1);
    widget* set_capture(widget* ptr, bool b);
    widget* set_focus(widget* ptr);
    void on_caret(uint);
    widget* get_capture() const { return _capture; }
    widget* get_focus() const { return _focus; }
    void set_cursor(cursor_type curty);
    void reset_cursor() { set_cursor(cur_arrow); }

public:
    virtual ~wsys_manager();
    virtual void on_show(bool b) override;
    virtual void on_create(wsys_driver* ptr, const rect& rc) override;
    virtual void on_close() override;
    virtual void on_resize(const rect& rc) override;
    virtual void on_paint(const rect& rc) override;
    virtual void on_halt() override;
    virtual void on_resume() override;
    virtual bool on_mouse_down(uint um, unikey uk, const point& pt) override;
    virtual bool on_mouse_up(uint um, unikey uk, const point& pt) override;
    virtual bool on_mouse_move(uint um, const point& pt) override;
    virtual bool on_key_down(uint um, unikey uk) override;
    virtual bool on_key_up(uint um, unikey uk) override;
    virtual bool on_char(uint um, uint ch) override;
    virtual void on_timer(uint tid) override;

protected:
    widget*         _hover;
    widget*         _capture;
    widget*         _focus;
    widget*         _root;

private:
    typedef unordered_map<string, widget*> widget_map;
    widget_map      _widget_map;

public:
    template<class _ctor>
    _ctor* add_widget(widget* ptr, const gchar* name, const rect& rc, uint style)
    {
        if(name && name[0] && _widget_map.find(name) != _widget_map.end())
            return nullptr;
        if(!ptr && _root)
            ptr = _root;
        _ctor* p = new _ctor(this);
        assert(p);
        if(!p->create(ptr, name ? name : _t(""), rc, style)) {
            delete p;
            return nullptr;
        }
        if(name && name[0]) {
            _widget_map.insert(
                std::make_pair(name, p)
                );
        }
        if(!ptr && !_root)
            _root = p;
        return p;
    }
    widget* get_root() const { return _root; }
    widget* find_widget(const string& name);
    bool remove_widget(widget* ptr);
    bool remove_widget(const string& name);

protected:
    bool remove_widget_internal(widget* ptr);

public:
    void set_ime(widget* ptr, point pt, const font& ft);
    void set_clipboard(clipfmt fmt, const void* ptr, int size);
    int get_clipboard(clipfmt fmt, const void*& ptr);
    int get_clipboard(clipboard_list& cl, int c);

protected:
    struct accel_key_hasher { size_t operator()(const accel_key& kval) const { return hash_bytes((const byte*)&kval, sizeof(kval)); } };
    typedef unordered_map<accel_key, widget*, accel_key_hasher> accel_map;
    friend bool try_proceed_accelerator(const accel_map&, unikey, uint);
    accel_map       _accel_map;

public:
    bool register_accelerator(widget* w, unikey key, uint mask);
    widget* unregister_accelerator(unikey key, uint mask);
};

extern const uuid uuid_widget;

__ariel_end__

#endif


#pragma once
#include "iarglist.h"
#include "optioninfo.h"
#include "configaccess.h"
#include "format.h"
#include "streamreader.h"
#include <gsl/gsl>
#include <cmdlime/errors.h>
#include <cmdlime/customnames.h>
#include <vector>
#include <sstream>
#include <functional>
#include <memory>

namespace cmdlime::detail{

template <typename T>
class ArgList : public IArgList{
public:
    ArgList(std::string name,
            std::string type,
            std::function<std::vector<T>&()> argListGetter)
        : info_(std::move(name), {}, std::move(type))
        , argListGetter_(std::move(argListGetter))
    {
    }

    void setDefaultValue(const std::vector<T>& value)
    {
        hasValue_ = true;
        defaultValue_ = value;
    }

    OptionInfo& info() override
    {
        return info_;
    }

    const OptionInfo& info() const override
    {
        return info_;
    }

private:
    bool read(const std::string& data) override
    {
        if (!isDefaultValueOverwritten_){
            argListGetter_().clear();
            isDefaultValueOverwritten_ = true;
        }
        auto stream = std::stringstream{data};
        argListGetter_().emplace_back();
        if (!readFromStream(stream, argListGetter_().back()))
            return false;
        hasValue_ = true;
        return true;
    }

    bool hasValue() const override
    {
        return hasValue_;
    }

    bool isOptional() const override
    {
        return defaultValue_.has_value();
    }

    std::string defaultValue() const override
    {
        if (!defaultValue_.has_value())
            return {};
        auto stream = std::stringstream{};
        stream << "{";
        auto firstVal = true;
        for (auto& val : defaultValue_.value()){
            if (firstVal)
                stream << val;
            else
                stream << ", " << val;
            firstVal = false;
        }
        stream << "}";
        return stream.str();
    }

private:
    OptionInfo info_;
    std::function<std::vector<T>&()> argListGetter_;
    bool hasValue_ = false;
    std::optional<std::vector<T>> defaultValue_;
    bool isDefaultValueOverwritten_ = false;
};

template <>
inline bool ArgList<std::string>::read(const std::string& data)
{
    argListGetter_().push_back(data);
    hasValue_ = true;
    return true;
}

template<typename T, typename TConfig>
class ArgListCreator{
    using NameProvider = typename Format<ConfigAccess<TConfig>::format()>::nameProvider;

public:
    ArgListCreator(TConfig& cfg,
                   const std::string& varName,
                   const std::string& type,
                   std::function<std::vector<T>&()> argListGetter)
        : cfg_(cfg)
    {
        Expects(!varName.empty());
        Expects(!type.empty());
        argList_ = std::make_unique<ArgList<T>>(NameProvider::fullName(varName),
                                                NameProvider::valueName(type),
                                                std::move(argListGetter));
    }

    ArgListCreator<T, TConfig>& operator<<(const std::string& info)
    {
        argList_->info().addDescription(info);
        return *this;
    }

    ArgListCreator<T, TConfig>& operator<<(const Name& customName)
    {
        argList_->info().resetName(customName.value());
        return *this;
    }

    ArgListCreator<T, TConfig>& operator<<(const ValueName& valueName)
    {
        argList_->info().resetValueName(valueName.value());
        return *this;
    }

    ArgListCreator<T, TConfig>& operator()(std::vector<T> defaultValue = {})
    {
        defaultValue_ = std::move(defaultValue);
        argList_->setDefaultValue(defaultValue_);
        return *this;
    }

    operator std::vector<T>()
    {
        ConfigAccess<TConfig>{cfg_}.setArgList(std::move(argList_));
        return defaultValue_;
    }

private:
    std::unique_ptr<ArgList<T>> argList_;
    std::vector<T> defaultValue_;
    TConfig& cfg_;
};

template <typename T, typename TConfig>
ArgListCreator<T, TConfig> makeArgListCreator(TConfig& cfg,
                                              const std::string& varName,
                                              const std::string& type,
                                              std::function<std::vector<T>&()> argListGetter)
{
    return ArgListCreator<T, TConfig>{cfg, varName, type, std::move(argListGetter)};
}

}


#ifndef LIC_RUNTIME_TYPE
#define LIC_RUNTIME_TYPE

#include <string>
#include <gsl/gsl>

#include <format/Opcodes.h>

namespace lic
{

class RuntimeType
{
public:
    virtual ~RuntimeType();

    virtual std::string Name() const = 0;
    virtual std::string ToString(gsl::byte* data) const = 0;

    virtual void PerformUnaryOp(Opcode op, gsl::byte* data) const;
    virtual void PerformBinaryOp(Opcode op, gsl::byte* leftData, RuntimeType* rightType, gsl::byte* rightData) const;

protected:
    RuntimeType();
};

}

#endif // !LIC_RUNTIME_TYPE


#pragma once

#include <gsl/gsl>

#include "halley/file_formats/config_file.h"

namespace YAML {
	class Node;
	class Emitter;
}

namespace Halley {
	class Path;

	class YAMLConvert {
	public:
		class EmitOptions {
		public:
			Vector<String> mapKeyOrder;
		};

		static void parseConfig(ConfigFile& config, gsl::span<const gsl::byte> data);
		static ConfigNode parseYAMLNode(const YAML::Node& node);
		static ConfigFile parseConfig(gsl::span<const gsl::byte> data);
		static ConfigFile parseConfig(const Bytes& data);
		static ConfigNode parseConfig(const String& str);

		static String generateYAML(const ConfigFile& config, const EmitOptions& options);
		static String generateYAML(const ConfigNode& node, const EmitOptions& options);

	private:
		static void emitNode(const ConfigNode& node, YAML::Emitter& emitter, const EmitOptions& options);
		static void emitSequence(const ConfigNode& node, YAML::Emitter& emitter, const EmitOptions& options);
		static void emitMap(const ConfigNode& node, YAML::Emitter& emitter, const EmitOptions& options);
		static bool isCompactSequence(const ConfigNode& node, int depth);
	};

}