/*
 * 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 "src/gpu/ops/GrSmallPathAtlasMgr.h"

#include "src/gpu/geometry/GrStyledShape.h"
#include "src/gpu/ops/GrSmallPathShapeData.h"

#ifdef DF_PATH_TRACKING
static int g_NumCachedShapes = 0;
static int g_NumFreedShapes = 0;
#endif

GrSmallPathAtlasMgr::GrSmallPathAtlasMgr() {}

GrSmallPathAtlasMgr::~GrSmallPathAtlasMgr() {
    this->reset();
}

void GrSmallPathAtlasMgr::reset() {
    ShapeDataList::Iter iter;
    iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart);
    GrSmallPathShapeData* shapeData;
    while ((shapeData = iter.get())) {
        iter.next();
        delete shapeData;
    }

    fShapeList.reset();
    fShapeCache.reset();

#ifdef DF_PATH_TRACKING
    SkDebugf("Cached shapes: %d, freed shapes: %d\n", g_NumCachedShapes, g_NumFreedShapes);
#endif

    fAtlas = nullptr;
}

bool GrSmallPathAtlasMgr::initAtlas(GrProxyProvider* proxyProvider, const GrCaps* caps) {
    if (fAtlas) {
        return true;
    }

    static constexpr size_t kMaxAtlasTextureBytes = 2048 * 2048;
    static constexpr size_t kPlotWidth = 512;
    static constexpr size_t kPlotHeight = 256;

    const GrBackendFormat format = caps->getDefaultBackendFormat(GrColorType::kAlpha_8,
                                                                 GrRenderable::kNo);

    GrDrawOpAtlasConfig atlasConfig(caps->maxTextureSize(), kMaxAtlasTextureBytes);
    SkISize size = atlasConfig.atlasDimensions(kA8_GrMaskFormat);
    fAtlas = GrDrawOpAtlas::Make(proxyProvider, format,
                                 GrColorType::kAlpha_8, size.width(), size.height(),
                                 kPlotWidth, kPlotHeight, this,
                                 GrDrawOpAtlas::AllowMultitexturing::kYes, this);

    return SkToBool(fAtlas);
}

void GrSmallPathAtlasMgr::deleteCacheEntry(GrSmallPathShapeData* shapeData) {
    fShapeCache.remove(shapeData->fKey);
    fShapeList.remove(shapeData);
    delete shapeData;
}

GrSmallPathShapeData* GrSmallPathAtlasMgr::findOrCreate(const GrSmallPathShapeDataKey& key) {
    auto shapeData = fShapeCache.find(key);
    if (!shapeData) {
        // TODO: move the key into the ctor
        shapeData = new GrSmallPathShapeData(key);
        fShapeCache.add(shapeData);
        fShapeList.addToTail(shapeData);
#ifdef DF_PATH_TRACKING
        ++g_NumCachedShapes;
#endif
    } else if (!fAtlas->hasID(shapeData->fAtlasLocator.plotLocator())) {
        shapeData->fAtlasLocator.invalidatePlotLocator();
    }

    return shapeData;
}

GrSmallPathShapeData* GrSmallPathAtlasMgr::findOrCreate(const GrStyledShape& shape,
                                                        int desiredDimension) {
    GrSmallPathShapeDataKey key(shape, desiredDimension);

    // TODO: move the key into 'findOrCreate'
    return this->findOrCreate(key);
}

GrSmallPathShapeData* GrSmallPathAtlasMgr::findOrCreate(const GrStyledShape& shape,
                                                        const SkMatrix& ctm) {
    GrSmallPathShapeDataKey key(shape, ctm);

    // TODO: move the key into 'findOrCreate'
    return this->findOrCreate(key);
}

GrDrawOpAtlas::ErrorCode GrSmallPathAtlasMgr::addToAtlas(GrResourceProvider* resourceProvider,
                                                         GrDeferredUploadTarget* target,
                                                         int width, int height, const void* image,
                                                         GrDrawOpAtlas::AtlasLocator* locator) {
    return fAtlas->addToAtlas(resourceProvider, target, width, height, image, locator);
}

void GrSmallPathAtlasMgr::setUseToken(GrSmallPathShapeData* shapeData,
                                      GrDeferredUploadToken token) {
    fAtlas->setLastUseToken(shapeData->fAtlasLocator, token);
}

// Callback to clear out internal path cache when eviction occurs
void GrSmallPathAtlasMgr::evict(GrDrawOpAtlas::PlotLocator plotLocator) {
    // remove any paths that use this plot
    ShapeDataList::Iter iter;
    iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart);
    GrSmallPathShapeData* shapeData;
    while ((shapeData = iter.get())) {
        iter.next();
        if (plotLocator == shapeData->fAtlasLocator.plotLocator()) {
            fShapeCache.remove(shapeData->fKey);
            fShapeList.remove(shapeData);
            delete shapeData;
#ifdef DF_PATH_TRACKING
            ++g_NumFreedShapes;
#endif
        }
    }
}
