// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "include/core/SkString.h"
#include "include/private/base/SkTo.h"
#include "modules/skunicode/include/SkUnicode.h"
#include "src/base/SkBitmaskEnum.h"
#include "src/base/SkTime.h"
#include "tools/unicode_comparison/cpp/bridge.h"

namespace {
static sk_sp<SkUnicode> gUnicode = nullptr;
static skia_private::TArray<SkUnicode::CodeUnitFlags, true> gCodeUnitFlags;
static std::vector<SkUnicode::Position> gSentences;
static std::vector<SkUnicode::Position> gWords;
}

bool init_skunicode_impl(char* impl) {

    SkString unicodeName(impl);
    if (unicodeName.equals("icu")) {
        gUnicode = SkUnicode::ICU::Make();
    } else if (unicodeName.equals("icu4x")) {
        gUnicode = SkUnicode::ICU4X::Make();
    } else if (unicodeName.equals("libgrapheme")) {
        gUnicode = SkUnicode::Libgrapheme::Make();
    } else {
        SkDebugf("Implementation '%s' not supported\n", impl);
        return false;
    }
    auto ptr = reinterpret_cast<void*>(gUnicode.get());
    if (ptr == nullptr) {
        SkDebugf("Could not create Unicode object\n");
        return false;
    }
    return true;
}

void cleanup_unicode_impl() {
    if (gUnicode == nullptr) {
        SkDebugf("Unicode object does not exist\n");
        return;
    }
    delete gUnicode.get();
}

double perf_compute_codeunit_flags(char* text) {
    if (gUnicode == nullptr) {
        SkDebugf("Unicode object does not exist\n");
        return -1;
    }
    double time = SkTime::GetNSecs();
    gUnicode->computeCodeUnitFlags(text, strlen(text), false, &gCodeUnitFlags);
    if (gCodeUnitFlags.size() < strlen(text)) {
        SkDebugf("computeCodeUnitFlags failed: %d < %zu\n%s\n\n\n", gCodeUnitFlags.size(), strlen(text),  text);
        return -1;
    }
    std::vector<SkUnicode::Position> positions;
    gUnicode->getUtf8Words(text, strlen(text), nullptr, &positions);
    double result = SkTime::GetNSecs() - time;
    for (auto pos : positions) {
        gCodeUnitFlags[pos] |= SkUnicode::CodeUnitFlags::kWordBreak;
    }
    return result;
}

int getFlags(int index) {
    if (gUnicode == nullptr) {
        SK_ABORT("Unicode object does not exist");
    } else if (gCodeUnitFlags.size() == 0) {
        SK_ABORT("Unicode object is empty or not initialized\n");
    } else if (index < 0 || index >= gCodeUnitFlags.size()) {
        SK_ABORT("Index value %d outside of valid range [%d:%d)\n", index, 0, gCodeUnitFlags.size());
    }
    return gCodeUnitFlags[index];
}

void* getSentences(char* text, int* length) {
    if (gUnicode == nullptr) {
        SkDebugf("Unicode object does not exist");
        return nullptr;
    }

    gSentences.clear();
    gUnicode->getSentences(text, strlen(text), nullptr, &gSentences);
    *length = gSentences.size();

    return reinterpret_cast<SkUnicode::Position*>(gSentences.data());
}

bool trimSentence(char* text, int* sentence, int wordLimit) {
    *sentence = 0;
    if (gUnicode == nullptr) {
        SkDebugf("Unicode object does not exist");
        return true;
    }

    gWords.clear();
    gUnicode->getUtf8Words(text, strlen(text), nullptr, &gWords);

    for (auto word : gWords) {
        if (word > wordLimit) {
            return true;
        } else {
            *sentence = word;
        }
    }
    if (strlen(text) <= wordLimit) {
        *sentence = strlen(text);
        return false;
    }
    return true;
}


void* toUpper(char* str) {
    if (gUnicode == nullptr) {
        SkDebugf("Unicode object does not exist");
        return nullptr;
    }
    auto res = new SkString(gUnicode->toUpper(SkString(str)));
    return reinterpret_cast<void*>(res);
}

void  print(void* str) {
    auto ptr = reinterpret_cast<SkString*>(str);
    SkDebugf("%s\n", ptr->c_str());
}
