/*
 * Copyright 2026 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#ifndef skgpu_graphite_sparse_strips_OracleValidator_DEFINED
#define skgpu_graphite_sparse_strips_OracleValidator_DEFINED

#include "include/core/SkPath.h"
#include "include/core/SkPoint.h"
#include "include/private/SkTDArray.h"
#include "src/core/SkVx.h"
#include "src/gpu/graphite/sparse_strips/Flatten.h"
#include "src/gpu/graphite/sparse_strips/MSAA_LUT.h"
#include "src/gpu/graphite/sparse_strips/Polyline.h"
#include "src/gpu/graphite/sparse_strips/Strip.h"
#include "src/gpu/graphite/sparse_strips/Tiler.h"
#include "tests/Test.h"
#include "tests/graphite/sparse_strips/Oracle.h"

#include <array>
#include <cstdint>
#include <unordered_map>
#include <vector>

namespace skgpu::graphite {

// Zero-copy reference validator that evaluates the SparseStrips stream against the analytical
// ScanlineOracle8x ground truth row by row, pixel by pixel, and subsample by subsample.
template <uint16_t kTileWidth, uint16_t kTileHeight> class OracleValidator {
public:
    enum class Strictness {
        kStrict,     // Exact sample matching against oracle
        kNonStrict,  // Ignore winding errors on subsamples within flattening tolerance of any
                     // chord in tile
    };

    OracleValidator(const SkPath& path,
                    const SkTDArray<Strip>& stripBuf,
                    const SkTDArray<uint8_t>& alphaBuf,
                    const SkTDArray<skvx::int8>& exactWindings,
                    const Polyline* polyline = nullptr,
                    const Tiles<kTileWidth, kTileHeight>* tiler = nullptr,
                    const char* testName = "OracleValidator",
                    Strictness strictness = Strictness::kStrict);

    static skvx::int8 GetOracleWinding(
            int px, const std::vector<ScanlineOracle8x::RowWindingInterval>& intervals);

    bool validate(skiatest::Reporter* reporter,
                  uint16_t viewportWidth,
                  uint16_t viewportHeight,
                  uint32_t* maxSampleDiffOut = nullptr) const;

private:
    bool validateStreamInvariants(skiatest::Reporter* reporter,
                                  uint16_t viewportWidth,
                                  uint16_t viewportHeight) const;

    bool validateBoundaryColumn(
            skiatest::Reporter* reporter,
            uint16_t px,
            uint16_t py,
            const std::vector<ScanlineOracle8x::RowWindingInterval>& oracleIntervals,
            bool expectedFill) const;

    bool validateGapSpan(skiatest::Reporter* reporter,
                         int startPx,
                         int endPx,
                         uint16_t py,
                         const std::vector<ScanlineOracle8x::RowWindingInterval>& oracleIntervals,
                         bool expectedFill) const;

    bool validateAlphaTileSpan(
            skiatest::Reporter* reporter,
            const Strip& strip,
            uint32_t startAlphaIdx,
            uint16_t spannedTiles,
            uint16_t py,
            const std::vector<ScanlineOracle8x::RowWindingInterval>& oracleIntervals,
            uint32_t* maxSampleDiffOut) const;

    static float DistanceSqToSegment(SkPoint p, SkPoint a, SkPoint b);
    void buildTileLineMap();
    bool isWithinTolerance(SkPoint subPt, uint16_t tileX, uint16_t tileY) const;

    const SkPath fPath;
    const SkTDArray<Strip>& fStrips;
    const SkTDArray<uint8_t>& fAlphaBuf;
    const SkTDArray<skvx::int8>& fExactWindings;
    const Polyline* fPolyline;
    const Tiles<kTileWidth, kTileHeight>* fTiler;
    const char* fTestName;
    const Strictness fStrictness;
    const float fToleranceSq = static_cast<float>(Flatten::kQuadTolerance2);
    std::unordered_map<uint32_t, std::vector<Line>> fTileLineMap;
    mutable ScanlineOracle8x fOracle;

    // We only explicitly instantiate 4 and 8
    static_assert(kTileWidth == kTileHeight && (kTileWidth == 4 || kTileWidth == 8));
};

}  // namespace skgpu::graphite

#endif  // skgpu_graphite_sparse_strips_OracleValidator_DEFINED
