/*
 * Copyright 2020 Bloomberg Finance LP
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef INCLUDED_BUILDBOXCOMMON_STRINGUTILS
#define INCLUDED_BUILDBOXCOMMON_STRINGUTILS

#include <buildboxcommon_protos.h>
#include <functional>
#include <string>

namespace buildboxcommon {

struct StringUtils {

    // all the single parameter methods below default to using std::isspace
    // semantics
    static void ltrim(std::string *s);
    static void ltrim(std::string *s, const std::function<int(int)> &filter);
    static void rtrim(std::string *s);
    static void rtrim(std::string *s, const std::function<int(int)> &filter);
    static void trim(std::string *s);
    static void trim(std::string *s, const std::function<int(int)> &filter);

    // return copies
    static std::string ltrim(const std::string &s);
    static std::string ltrim(const std::string &s,
                             const std::function<int(int)> &filter);
    static std::string rtrim(const std::string &s);
    static std::string rtrim(const std::string &s,
                             const std::function<int(int)> &filter);
    static std::string trim(const std::string &s);
    static std::string trim(const std::string &s,
                            const std::function<int(int)> &filter);
    // Return the ordinal version of a number (e.g. 1 -> "1st", 2 -> "2nd")
    static std::string ordinal(const int n);

    // Return string of hex digits for random 32-bit integer (8 hex digits)
    static std::string getRandomHexString();

    // Return UUID string
    static std::string getUUIDString();

    static int64_t parseSize(const std::string &value);

    // Parse a digest in the format of "[hash in hex notation]/[size_bytes]"
    static bool parseDigest(const std::string &value, Digest *digest);

    // Parse a gRPC status code. It throws an exception if it is invalid.
    // https://grpc.github.io/grpc/core/md_doc_statuscodes.html
    static grpc::StatusCode parseStatusCode(const std::string &value);

    // Split a string or a string view into a vector of substrings by a
    // delimiter
    template <typename StringType>
    static std::vector<StringType> split(const StringType &s,
                                         const std::string &delimiter)
    {
        size_t start = 0, end = 0, delimiterSize = delimiter.size();
        if (delimiterSize == 0) {
            BUILDBOXCOMMON_THROW_EXCEPTION(std::invalid_argument,
                                           "empty delimiter");
        }
        std::vector<StringType> result;

        while ((end = s.find(delimiter, start)) != StringType::npos) {
            result.emplace_back(s.substr(start, end - start));
            start = end + delimiterSize;
        }
        // Last token
        result.emplace_back(s.substr(start));
        return result;
    }

    // Join a list of strings with a delimiter in between, the delimiter
    // defaults to a " "
    static std::string join(const std::vector<std::string> &commandLine,
                            const std::string &delimiter = " ");

    // Returns a digest from a given string, may throw if not a valid digest.
    static Digest digest_from_string(const std::string &s);

  private:
    static inline std::unordered_map<std::string, grpc::StatusCode>
        s_strToStatusCode = {
            {"OK", grpc::StatusCode::OK},
            {"CANCELLED", grpc::StatusCode::CANCELLED},
            {"UNKNOWN", grpc::StatusCode::UNKNOWN},
            {"INVALID_ARGUMENT", grpc::StatusCode::INVALID_ARGUMENT},
            {"NOT_FOUND", grpc::StatusCode::NOT_FOUND},
            {"ALREADY_EXISTS", grpc::StatusCode::ALREADY_EXISTS},
            {"PERMISSION_DENIED", grpc::StatusCode::PERMISSION_DENIED},
            {"RESOURCE_EXHAUSTED", grpc::StatusCode::RESOURCE_EXHAUSTED},
            {"FAILED_PRECONDITION", grpc::StatusCode::FAILED_PRECONDITION},
            {"ABORTED", grpc::StatusCode::ABORTED},
            {"OUT_OF_RANGE", grpc::StatusCode::OUT_OF_RANGE},
            {"UNIMPLEMENTED", grpc::StatusCode::UNIMPLEMENTED},
            {"INTERNAL", grpc::StatusCode::INTERNAL},
            {"UNAVAILABLE", grpc::StatusCode::UNAVAILABLE},
            {"DATA_LOSS", grpc::StatusCode::DATA_LOSS},
            {"UNAUTHENTICATED", grpc::StatusCode::UNAUTHENTICATED}};
};

} // namespace buildboxcommon

#endif
