#include "StringUtils.h"

#include <locale>
#include <algorithm>
#include <sstream>
#include <iomanip>

bool StringUtils::isAlphaNumeric(char c)
{
    return std::isalnum(c);
}

bool StringUtils::isSpace(char c)
{
    return std::isspace(c);
}

bool StringUtils::isAlphabetical(char c)
{
    return std::isalpha(c);
}

std::vector<unsigned char> StringUtils::toBytes(const std::string& input)
{
    return {input.begin(), input.end()};
}

std::string StringUtils::toString(const std::vector<unsigned char>& bytes)
{
    return {bytes.begin(), bytes.end()};
}

std::vector<std::string> StringUtils::toLines(const std::string& input)
{
    auto result = std::vector<std::string>{};
    auto ss = std::stringstream{input};
    for (std::string line; std::getline(ss, line, '\n');)
    {
        result.push_back(line);
    }
    return result;
}

bool StringUtils::isWhitespaceOnly(const std::string& input)
{
    if (input.empty())
    {
        return true;
    }
    else
    {
        return std::all_of(input.cbegin(), input.cend(), [](char c){ return std::isspace(c); });
    }
}

std::size_t StringUtils::countFirstConsecutiveHits(const std::string& input, char c)
{
    auto found_id = input.find(c);
    if(found_id == std::string::npos)
    {
        return 0;
    }
    else
    {
        std::size_t count = 1;
        for(std::size_t idx=found_id+1; idx<input.size(); idx++)
        {
            if(input[idx] == c)
            {
                count++;
            }
            else
            {
                return count;
            }
        }
        return count;
    }
}

std::string StringUtils::stripSurroundingWhitepsace(const std::string& input)
{
    if (input.empty())
    {
        return {};
    }

    std::size_t first_nonspace = 0;
    std::size_t last_nonspace = input.size() - 1;
    for (std::size_t idx = 0; idx < input.size(); idx++)
    {
        if (!std::isspace(input[idx]))
        {
            first_nonspace = idx;
            break;
        }
    }

    if (first_nonspace == last_nonspace)
    {
        return {};
    }

    for (std::size_t idx = last_nonspace; idx > 0; idx--)
    {
        if (!std::isspace(input[idx]))
        {
            last_nonspace = idx;
            break;
        }
    }
    return input.substr(first_nonspace, last_nonspace-first_nonspace + 1);
}

std::vector<std::string> StringUtils::split(const std::string& input)
{
    std::vector<std::string> substrings;
    std::string working_string;
    std::locale loc;
    bool last_was_nonspace{ false };
    for (auto c : input)
    {
        if (std::isspace(c, loc))
        {
            if (last_was_nonspace)
            {
                substrings.push_back(working_string);
                working_string = "";
                last_was_nonspace = false;
            }
        }
        else
        {
            last_was_nonspace = true;
            working_string += c;
        }
    }
    if (!working_string.empty())
    {
        substrings.push_back(working_string);
    }
    return substrings;
}

std::string StringUtils::toLower(const std::string& s)
{
    std::string ret;
    std::transform(s.begin(), s.end(), ret.begin(), [](unsigned char c){ return std::tolower(c); });
    return ret;
}

std::string StringUtils::toPaddedString(unsigned numBytes, unsigned entry)
{
    std::stringstream sstr;
    sstr << std::setfill('0') << std::setw(numBytes) << entry;
    return sstr.str();
}

std::string StringUtils::stripQuotes(const std::string& input)
{
    if (input.size() < 3)
    {
        return input;
    }
    std::size_t start_index = 0;
    std::size_t end_index = input.size()-1;
    if (input[start_index] == '"')
    {
        start_index = 1;
    }
    if (input[end_index] == '"')
    {
        end_index = end_index - 1;
    }
    return input.substr(start_index, end_index - start_index + 1);
}

std::string StringUtils::removeUpTo(const std::string& input, const std::string& prefix)
{
    std::size_t found = input.find(prefix);
    if (found != std::string::npos)
    {
        return input.substr(found + prefix.size(), input.size()-found);
    }
    else
    {
        return input;
    }
}

bool StringUtils::startsWith(const std::string& input, const std::string& prefix, bool ignoreWhitespace)
{
    if(ignoreWhitespace)
    {
        const auto loc = input.find(prefix);
        if (loc == std::string::npos)
        {
            return false;
        }
        else
        {
            return isWhitespaceOnly(input.substr(0, loc));
        }
    }
    else
    {
        return input.find(prefix) == 0;
    }
}
