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

#include "tools/text/SkTextBlobTrace.h"

#include "include/core/SkData.h"
#include "include/core/SkFont.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkStream.h"
#include "include/core/SkString.h"
#include "include/core/SkTextBlob.h"
#include "include/core/SkTypeface.h"
#include "include/private/base/SkDebug.h"
#include "src/base/SkTLazy.h"
#include "src/core/SkChecksum.h"
#include "src/core/SkFontPriv.h"
#include "src/core/SkPtrRecorder.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkTextBlobPriv.h"
#include "src/core/SkWriteBuffer.h"
#include "src/text/GlyphRun.h"

#include <utility>

std::vector<SkTextBlobTrace::Record> SkTextBlobTrace::CreateBlobTrace(
        SkStream* stream, sk_sp<SkFontMgr> lastResortMgr) {
    std::vector<SkTextBlobTrace::Record> trace;

    uint32_t typefaceCount;
    if (!stream->readU32(&typefaceCount)) {
        return trace;
    }

    std::vector<sk_sp<SkTypeface>> typefaceArray;
    for (uint32_t i = 0; i < typefaceCount; i++) {
        typefaceArray.push_back(SkTypeface::MakeDeserialize(stream, lastResortMgr));
    }

    uint32_t restOfFile;
    if (!stream->readU32(&restOfFile)) {
        return trace;
    }
    sk_sp<SkData> data = SkData::MakeFromStream(stream, restOfFile);
    SkReadBuffer readBuffer{data->data(), data->size()};
    readBuffer.setTypefaceArray(typefaceArray.data(), typefaceArray.size());

    while (!readBuffer.eof()) {
        SkTextBlobTrace::Record record;
        record.origUniqueID = readBuffer.readUInt();
        record.paint = readBuffer.readPaint();
        readBuffer.readPoint(&record.offset);
        record.blob = SkTextBlobPriv::MakeFromBuffer(readBuffer);
        trace.push_back(std::move(record));
    }
    return trace;
}

void SkTextBlobTrace::DumpTrace(const std::vector<SkTextBlobTrace::Record>& trace) {
    for (const SkTextBlobTrace::Record& record : trace) {
        const SkTextBlob* blob = record.blob.get();
        const SkPaint& p = record.paint;
        bool weirdPaint = p.getStyle() != SkPaint::kFill_Style
        || p.getMaskFilter() != nullptr
        || p.getPathEffect() != nullptr;

        SkDebugf("Blob %u ( %g %g ) %d\n  ",
                blob->uniqueID(), record.offset.x(), record.offset.y(), weirdPaint);
        SkTextBlobRunIterator iter(blob);
        int runNumber = 0;
        while (!iter.done()) {
            SkDebugf("Run %d\n    ", runNumber);
            SkFont font = iter.font();
            SkDebugf("Font %u %g %g %g %d %d %d\n    ",
                    font.getTypeface()->uniqueID(),
                    font.getSize(),
                    font.getScaleX(),
                    font.getSkewX(),
                    SkFontPriv::Flags(font),
                    (int)font.getEdging(),
                    (int)font.getHinting());
            uint32_t glyphCount = iter.glyphCount();
            const uint16_t* glyphs = iter.glyphs();
            for (uint32_t i = 0; i < glyphCount; i++) {
                SkDebugf("%02X ", glyphs[i]);
            }
            SkDebugf("\n");
            runNumber += 1;
            iter.next();
        }
    }
}

SkTextBlobTrace::Capture::Capture() : fTypefaceSet(new SkRefCntSet), fWriteBuffer({}) {
    fWriteBuffer.setTypefaceRecorder(fTypefaceSet);
}

SkTextBlobTrace::Capture::~Capture() = default;

void SkTextBlobTrace::Capture::capture(
        const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) {
    const SkTextBlob* blob = glyphRunList.blob();
    if (blob != nullptr) {
        fWriteBuffer.writeUInt(blob->uniqueID());
        fWriteBuffer.writePaint(paint);
        fWriteBuffer.writePoint(glyphRunList.origin());
        SkTextBlobPriv::Flatten(*blob, fWriteBuffer);
        fBlobCount++;
    }
}

void SkTextBlobTrace::Capture::dump(SkWStream* dst) const {
    SkTLazy<SkFILEWStream> fileStream;
    if (!dst) {
        uint32_t id = SkChecksum::Mix(reinterpret_cast<uintptr_t>(this));
        SkString f = SkStringPrintf("diff-canvas-%08x-%04zu.trace", id, fBlobCount);
        dst = fileStream.init(f.c_str());
        if (!fileStream->isValid()) {
            SkDebugf("Error opening '%s'.\n", f.c_str());
            return;
        }
        SkDebugf("Saving trace to '%s'.\n", f.c_str());
    }
    SkASSERT(dst);
    int count = fTypefaceSet->count();
    dst->write32(count);
    SkPtrSet::Iter iter(*fTypefaceSet);
    while (void* ptr = iter.next()) {
        ((const SkTypeface*)ptr)->serialize(dst, SkTypeface::SerializeBehavior::kDoIncludeData);
    }
    dst->write32(fWriteBuffer.bytesWritten());
    fWriteBuffer.writeToStream(dst);
}
