/*
 * Copyright 2020 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/core/SkFont.h"
#include "include/core/SkTypeface.h"
#include "src/core/SkScalerCache.h"
#include "src/core/SkStrikeSpec.h"
#include "src/core/SkTaskGroup.h"
#include "tests/Test.h"
#include "tools/ToolUtils.h"

#include <atomic>

class Barrier {
public:
    Barrier(int threadCount) : fThreadCount(threadCount) { }
    void waitForAll() {
        fThreadCount -= 1;
        while (fThreadCount > 0) { }
    }

private:
    std::atomic<int> fThreadCount;
};

DEF_TEST(SkScalerCacheMultiThread, Reporter) {
    sk_sp<SkTypeface> typeface =
            ToolUtils::create_portable_typeface("serif", SkFontStyle::Italic());
    static constexpr int kThreadCount = 4;

    Barrier barrier{kThreadCount};

    SkFont font;
    font.setEdging(SkFont::Edging::kAntiAlias);
    font.setSubpixel(true);
    font.setTypeface(typeface);

    SkGlyphID glyphs['z'];
    SkPoint pos['z'];
    for (int c = ' '; c < 'z'; c++) {
        glyphs[c] = font.unicharToGlyph(c);
        pos[c] = {30.0f * c + 30, 30.0f};
    }
    constexpr size_t glyphCount = 'z' - ' ';
    auto data = SkMakeZip(glyphs, pos).subspan(SkTo<int>(' '), glyphCount);

    SkPaint defaultPaint;
    SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
            font, defaultPaint, SkSurfaceProps(0, kUnknown_SkPixelGeometry),
            SkScalerContextFlags::kNone, SkMatrix::I());

    // Make our own executor so the --threads parameter doesn't mess things up.
    auto executor = SkExecutor::MakeFIFOThreadPool(kThreadCount);
    for (int tries = 0; tries < 100; tries++) {
        SkScalerContextEffects effects;
        std::unique_ptr<SkScalerContext> ctx{
                typeface->createScalerContext(effects, &strikeSpec.descriptor())};
        SkScalerCache scalerCache{strikeSpec.descriptor(), std::move(ctx)};

        auto perThread = [&](int threadIndex) {
            barrier.waitForAll();

            auto local = data.subspan(threadIndex * 2, data.size() - kThreadCount * 2);
            for (int i = 0; i < 100; i++) {
                SkDrawableGlyphBuffer drawable;
                SkSourceGlyphBuffer rejects;

                drawable.ensureSize(glyphCount);
                rejects.setSource(local);

                drawable.startBitmapDevice(rejects.source(), {0, 0}, SkMatrix::I(),
                                           scalerCache.roundingSpec());
                scalerCache.prepareForMaskDrawing(&drawable, &rejects);
                rejects.flipRejectsToSource();
                drawable.reset();
            }
        };

        SkTaskGroup(*executor).batch(kThreadCount, perThread);
    }
}