text: #pragma once
  
  #include "CreditSystem.h"
  #include "Library.h"
  #include "RasterMappedTo3DTile.h"
  
  #include <CesiumAsync/IAssetAccessor.h>
  #include <CesiumGeospatial/Projection.h>
  #include <CesiumGltf/GltfReader.h>
  #include <CesiumUtility/IntrusivePointer.h>
  
  #include <spdlog/fwd.h>
  
  #include <cassert>
  #include <optional>
  
  namespace Cesium3DTilesSelection {
  
  class RasterOverlay;
  class RasterOverlayTile;
  class IPrepareRendererResources;
  
  /**
   * @brief Summarizes the result of loading an image of a {@link RasterOverlay}.
   */
  struct CESIUM3DTILESSELECTION_API LoadedRasterOverlayImage {
    /**
     * @brief The loaded image.
     *
     * This will be an empty optional if the loading failed. In this case,
     * the `errors` vector will contain the corresponding error messages.
     */
    std::optional<CesiumGltf::ImageCesium> image{};
  
    /**
     * @brief The projected rectangle defining the bounds of this image.
     *
     * The rectangle extends from the left side of the leftmost pixel to the
     * right side of the rightmost pixel, and similar for the vertical direction.
     */
    CesiumGeometry::Rectangle rectangle{};
  
    /**
     * @brief The {@link Credit} objects that decribe the attributions that
     * are required when using the image.
     */
    std::vector<Credit> credits{};
  
    /**
     * @brief Error messages from loading the image.
     *
     * If the image was loaded successfully, this should be empty.
     */
    std::vector<std::string> errors{};
  
    /**
     * @brief Warnings from loading the image.
     */
    // Implementation note: In the current implementation, this will
    // always be empty, but it might contain warnings in the future,
    // when other image types or loaders are used.
    std::vector<std::string> warnings{};
  
    /**
     * @brief Whether more detailed data, beyond this image, is available within
     * the bounds of this image.
     */
    bool moreDetailAvailable = false;
  };
  
  /**
   * @brief Options for {@link RasterOverlayTileProvider::loadTileImageFromUrl}.
   */
  struct LoadTileImageFromUrlOptions {
    /**
     * @brief The rectangle definining the bounds of the image being loaded,
     * expressed in the {@link RasterOverlayTileProvider}'s projection.
     */
    CesiumGeometry::Rectangle rectangle{};
  
    /**
     * @brief The credits to display with this tile.
     *
     * This property is copied verbatim to the
     * {@link LoadedRasterOverlayImage::credits} property.
     */
    std::vector<Credit> credits{};
  
    /**
     * @brief Whether more detailed data, beyond this image, is available within
     * the bounds of this image.
     */
    bool moreDetailAvailable = true;
  
    /**
     * @brief Whether empty (zero length) images are accepted as a valid
     * response.
     *
     * If true, an otherwise valid response with zero length will be accepted as
     * a valid 0x0 image. If false, such a response will be reported as an
     * error.
     *
     * {@link RasterOverlayTileProvider::loadTile} and
     * {@link RasterOverlayTileProvider::loadTileThrottled} will treat such an
     * image as "failed" and use the quadtree parent (or ancestor) image
     * instead, but will not report any error.
     *
     * This flag should only be set to `true` when the tile source uses a
     * zero-length response as an indication that this tile is - as expected -
     * not available.
     */
    bool allowEmptyImages = false;
  };
  
  /**
   * @brief Provides individual tiles for a {@link RasterOverlay} on demand.
   *
   */
  class CESIUM3DTILESSELECTION_API RasterOverlayTileProvider {
  public:
    /**
     * Constructs a placeholder tile provider.
     *
     * @see RasterOverlayTileProvider::isPlaceholder
     *
     * @param owner The raster overlay that owns this tile provider.
     * @param asyncSystem The async system used to do work in threads.
     * @param pAssetAccessor The interface used to obtain assets (tiles, etc.) for
     * this raster overlay.
     */
    RasterOverlayTileProvider(
        RasterOverlay& owner,
        const CesiumAsync::AsyncSystem& asyncSystem,
        const std::shared_ptr<CesiumAsync::IAssetAccessor>&
            pAssetAccessor) noexcept;
  
    /**
     * @brief Creates a new instance.
     *
     * @param owner The raster overlay that owns this tile provider.
     * @param asyncSystem The async system used to do work in threads.
     * @param pAssetAccessor The interface used to obtain assets (tiles, etc.) for
     * this raster overlay.
     * @param credit The {@link Credit} for this tile provider, if it exists.
     * @param pPrepareRendererResources The interface used to prepare raster
     * images for rendering.
     * @param pLogger The logger to which to send messages about the tile provider
     * and tiles.
     * @param projection The {@link CesiumGeospatial::Projection}.
     */
    RasterOverlayTileProvider(
        RasterOverlay& owner,
        const CesiumAsync::AsyncSystem& asyncSystem,
        const std::shared_ptr<CesiumAsync::IAssetAccessor>& pAssetAccessor,
        std::optional<Credit> credit,
        const std::shared_ptr<IPrepareRendererResources>&
            pPrepareRendererResources,
        const std::shared_ptr<spdlog::logger>& pLogger,
        const CesiumGeospatial::Projection& projection) noexcept;
  
    /** @brief Default destructor. */
    virtual ~RasterOverlayTileProvider() {}
  
    /**
     * @brief Returns whether this is a placeholder.
     *
     * For many types of {@link RasterOverlay}, we can't create a functioning
     * `RasterOverlayTileProvider` right away. For example, we may not know the
     * bounds of the overlay, or what projection it uses, until after we've
     * (asynchronously) loaded a metadata service that gives us this information.
     *
     * So until that real `RasterOverlayTileProvider` becomes available, we use
     * a placeholder. When {@link RasterOverlayTileProvider::getTile} is invoked
     * on a placeholder, it returns a {@link RasterOverlayTile} that is also
     * a placeholder. And whenever we see a placeholder `RasterOverTile` in
     * {@link Tile::update}, we check if the corresponding `RasterOverlay` is
     * ready yet. Once it's ready, we remove the placeholder tile and replace
     * it with the real tiles.
     *
     * So the placeholder system gives us a way to defer the mapping of raster
     * overlay tiles to geometry tiles until that mapping can be determined.
     */
    bool isPlaceholder() const noexcept { return this->_pPlaceholder != nullptr; }
  
    /**
     * @brief Returns the {@link RasterOverlay} that created this instance.
     */
    RasterOverlay& getOwner() noexcept { return *this->_pOwner; }
  
    /** @copydoc getOwner */
    const RasterOverlay& getOwner() const noexcept { return *this->_pOwner; }
  
    /**
     * @brief Get the system to use for asychronous requests and threaded work.
     */
    const std::shared_ptr<CesiumAsync::IAssetAccessor>&
    getAssetAccessor() const noexcept {
      return this->_pAssetAccessor;
    }
  
    /**
     * @brief Gets the async system used to do work in threads.
     */
    const CesiumAsync::AsyncSystem& getAsyncSystem() const noexcept {
      return this->_asyncSystem;
    }
  
    /**
     * @brief Gets the interface used to prepare raster overlay images for
     * rendering.
     */
    const std::shared_ptr<IPrepareRendererResources>&
    getPrepareRendererResources() const noexcept {
      return this->_pPrepareRendererResources;
    }
  
    /**
     * @brief Gets the logger to which to send messages about the tile provider
     * and tiles.
     */
    const std::shared_ptr<spdlog::logger>& getLogger() const noexcept {
      return this->_pLogger;
    }
  
    /**
     * @brief Returns the {@link CesiumGeospatial::Projection} of this instance.
     */
    const CesiumGeospatial::Projection& getProjection() const noexcept {
      return this->_projection;
    }
  
    /**
     * @brief Returns a new {@link RasterOverlayTile} with the given
     * specifications.
     *
     * The returned tile will not start loading immediately. To start loading,
     * call {@link RasterOverlayTileProvider::loadTile} or
     * {@link RasterOverlayTileProvider::loadTileThrottled}.
     *
     * @param rectangle The rectangle that the returned image must cover. It is
     * allowed to cover a slightly larger rectangle in order to maintain pixel
     * alignment. It may also cover a smaller rectangle when the overlay itself
     * does not cover the entire rectangle.
     * @param targetGeometricError The geometric error (in meters) of the
     * geometry tile to which the returned raster overlay tile will be attached.
     * With the typical settings, this raster overlay will be shown when the
     * geometric error, when projected to the screen, is up to 16 pixels. When the
     * error is more than 16 pixels, more detailed data will be shown instead.
     * @return The tile.
     */
    CesiumUtility::IntrusivePointer<RasterOverlayTile> getTile(
        const CesiumGeometry::Rectangle& rectangle,
        double targetGeometricError);
  
    /**
     * @brief Gets the number of bytes of tile data that are currently loaded.
     */
    int64_t getTileDataBytes() const noexcept { return this->_tileDataBytes; }
  
    /**
     * @brief Returns the number of tiles that are currently loading.
     */
    uint32_t getNumberOfTilesLoading() const noexcept {
      assert(this->_totalTilesCurrentlyLoading > -1);
      return this->_totalTilesCurrentlyLoading;
    }
  
    /**
     * @brief Removes a no-longer-referenced tile from this provider's cache and
     * deletes it.
     *
     * This function is not supposed to be called by client. Calling this method
     * in a tile with a reference count greater than 0 will result in undefined
     * behavior.
     *
     * @param pTile The tile, which must have no oustanding references.
     */
    void removeTile(RasterOverlayTile* pTile) noexcept;
  
    /**
     * @brief Get the per-TileProvider {@link Credit} if one exists.
     */
    const std::optional<Credit>& getCredit() const noexcept { return _credit; }
  
    /**
     * @brief Loads a tile immediately, without throttling requests.
     *
     * If the tile is not in the `Tile::LoadState::Unloading` state, this method
     * returns without doing anything. Otherwise, it puts the tile into the
     * `Tile::LoadState::Loading` state and begins the asynchronous process
     * to load the tile. When the process completes, the tile will be in the
     * `Tile::LoadState::Loaded` or `Tile::LoadState::Failed` state.
     *
     * Calling this method on many tiles at once can result in very slow
     * performance. Consider using {@link loadTileThrottled} instead.
     *
     * @param tile The tile to load.
     */
    void loadTile(RasterOverlayTile& tile);
  
    /**
     * @brief Loads a tile, unless there are too many tile loads already in
     * progress.
     *
     * If the tile is not in the `Tile::LoadState::Unloading` state, this method
     * returns true without doing anything. If too many tile loads are
     * already in flight, it returns false without doing anything. Otherwise, it
     * puts the tile into the `Tile::LoadState::Loading` state, begins the
     * asynchronous process to load the tile, and returns true. When the process
     * completes, the tile will be in the `Tile::LoadState::Loaded` or
     * `Tile::LoadState::Failed` state.
     *
     * The number of allowable simultaneous tile requests is provided in the
     * {@link RasterOverlayOptions::maximumSimultaneousTileLoads} property of
     * {@link RasterOverlay::getOptions}.
     *
     * @param tile The tile to load.
     * @returns True if the tile load process is started or is already complete,
     * false if the load could not be started because too many loads are already
     * in progress.
     */
    bool loadTileThrottled(RasterOverlayTile& tile);
  
  protected:
    /**
     * @brief Loads the image for a tile.
     *
     * @param overlayTile The overlay tile for which to load the image.
     * @return A future that resolves to the image or error information.
     */
    virtual CesiumAsync::Future<LoadedRasterOverlayImage>
    loadTileImage(RasterOverlayTile& overlayTile) = 0;
  
    /**
     * @brief Loads an image from a URL and optionally some request headers.
     *
     * This is a useful helper function for implementing {@link loadTileImage}.
     *
     * @param url The URL.
     * @param headers The request headers.
     * @param options Additional options for the load process.
     * @return A future that resolves to the image or error information.
     */
    CesiumAsync::Future<LoadedRasterOverlayImage> loadTileImageFromUrl(
        const std::string& url,
        const std::vector<CesiumAsync::IAssetAccessor::THeader>& headers = {},
        LoadTileImageFromUrlOptions&& options = {}) const;
  
  private:
    void doLoad(RasterOverlayTile& tile, bool isThrottledLoad);
  
    /**
     * @brief Begins the process of loading of a tile.
     *
     * This method should be called at the beginning of the tile load process.
     *
     * @param tile The tile that is starting to load.
     * @param isThrottledLoad True if the load was originally throttled.
     */
    void beginTileLoad(RasterOverlayTile& tile, bool isThrottledLoad) noexcept;
  
    /**
     * @brief Finalizes loading of a tile.
     *
     * This method should be called at the end of the tile load process,
     * no matter whether the load succeeded or failed.
     *
     * @param tile The tile that finished loading.
     * @param isThrottledLoad True if the load was originally throttled.
     */
    void finalizeTileLoad(RasterOverlayTile& tile, bool isThrottledLoad) noexcept;
  
  private:
    RasterOverlay* _pOwner;
    CesiumAsync::AsyncSystem _asyncSystem;
    std::shared_ptr<CesiumAsync::IAssetAccessor> _pAssetAccessor;
    std::optional<Credit> _credit;
    std::shared_ptr<IPrepareRendererResources> _pPrepareRendererResources;
    std::shared_ptr<spdlog::logger> _pLogger;
    CesiumGeospatial::Projection _projection;
    std::unique_ptr<RasterOverlayTile> _pPlaceholder;
    int64_t _tileDataBytes;
    int32_t _totalTilesCurrentlyLoading;
    int32_t _throttledTilesCurrentlyLoading;
    CESIUM_TRACE_DECLARE_TRACK_SET(
        _loadingSlots,
        "Raster Overlay Tile Loading Slot");
  
    static CesiumGltf::GltfReader _gltfReader;
  };
  } // namespace Cesium3DTilesSelection
  