//
//  omStringUtilTests.cpp
//  OmUtil
//
//  Created by David Van Brink on 10/2/16.
//  Copyright (c) 2016 omino.com. All rights reserved.
//

#include "omStringUtilTests.h"
#include "Om.h"
#include "OmAsserts.h"



OMTEST(testStartsEndsWith)
{
    ASSERT_TRUE("endswith", endsWith("abcde", "e"));
    ASSERT_TRUE("endswith", endsWith("abcde", "de"));
    ASSERT_TRUE("endswith", endsWith("abcde", ""));
    ASSERT_FALSE("endswith", endsWith("e", "de"));
    ASSERT_FALSE("endswith", endsWith("abcde", "ee"));
    
    ASSERT_TRUE("endswith", startsWith("abcde", "a"));
    ASSERT_TRUE("startsWith", startsWith("abcde", "ab"));
    ASSERT_TRUE("startsWith", startsWith("abcde", ""));
    ASSERT_FALSE("startsWith", startsWith("e", "de"));
    ASSERT_FALSE("startsWith", startsWith("abcde", "e"));
}

OMTEST(testContains)
{
    ASSERT_FALSE("contains", contains("fish", "dish"));
    ASSERT_TRUE("contains", contains("fish", "is"));
}

OMTEST(doTrim)
{
    ASSERT_EQUALS_STRING("trim", "fish", trim("fish"));
    ASSERT_EQUALS_STRING("trim", "fish", trim("\n\tfish "));
    ASSERT_EQUALS_STRING("trim", "fish", trim(" fish"));
    ASSERT_EQUALS_STRING("trim", "fish", trim("fish \n\r"));
    ASSERT_EQUALS_STRING("trim", "", trim("\t \n\r"));
}

OMTEST(stringUtilTests)
{
    std::string s;
    
    s += ssprintf("x=%d", 86);
    s += ssprintf(", y=%d", 99);
    
    ASSERT_EQUALS_STRING("ssprintg?", "x=86, y=99", s);
    
    
    
}

OMTEST(testSplitStrings)
{
    ASSERT_EQUALS_INT("breakers at the very end", 4, split(",,,", ",").size());
    
    std::vector<std::string> parts = split("this is a test", " ");
    ASSERT_EQUALS_INT("parts", 4, parts.size());
    ASSERT_EQUALS_STRING("part", "this", parts[0]);
    ASSERT_EQUALS_STRING("part", "is", parts[1]);
    ASSERT_EQUALS_STRING("part", "a", parts[2]);
    ASSERT_EQUALS_STRING("part", "test", parts[3]);
    
    parts = split("  this    is  a  test  ", "  ", false, true);
    ASSERT_EQUALS_INT("parts", 4, parts.size());
    ASSERT_EQUALS_STRING("part", "this", parts[0]);
    ASSERT_EQUALS_STRING("part", "is", parts[1]);
    ASSERT_EQUALS_STRING("part", "a", parts[2]);
    ASSERT_EQUALS_STRING("part", "test", parts[3]);
    
    
    parts = split("xx::y::zz", "::"); // like a bitbox vector set
    ASSERT_EQUALS_INT("parts", 3, parts.size());
    ASSERT_EQUALS_STRING("part", "xx", parts[0]);
    ASSERT_EQUALS_STRING("part", "y", parts[1]);
    ASSERT_EQUALS_STRING("part", "zz", parts[2]);
    
    parts = split("A;;C", ";"); // like a bitbox vector set
    ASSERT_EQUALS_INT("parts", 3, parts.size());
    ASSERT_EQUALS_STRING("part", "A", parts[0]);
    ASSERT_EQUALS_STRING("part", "", parts[1]);
    ASSERT_EQUALS_STRING("part", "C", parts[2]);
    
    parts = split("1,2,3;;4,5,6", ";"); // like a bitbox vector set
    ASSERT_EQUALS_INT("parts", 3, parts.size());
    ASSERT_EQUALS_STRING("part", "1,2,3", parts[0]);
    ASSERT_EQUALS_STRING("part", "", parts[1]);
    ASSERT_EQUALS_STRING("part", "4,5,6", parts[2]);
    
    
    parts = split("1,2,3,4,5", ",", 3);
    ASSERT_EQUALS_INT("parts", 3, parts.size());
    ASSERT_EQUALS_STRING("part", "1", parts[0]);
    ASSERT_EQUALS_STRING("part", "2", parts[1]);
    ASSERT_EQUALS_STRING("part", "3,4,5", parts[2]);
    
    ASSERT_EQUALS_INT("corner case for zero length string, with zero vector elements out of it", 0, split("", ",").size());
}

OMTEST(testJoinStrings)
{
    std::vector<std::string> sV{"a","B","c"};
    std::vector<float> sF{23,25.5,90};
    
    ASSERT_EQUALS_STRING("join strings", "a --B --c", join(sV, " --"));
    auto sFJ = join(sF, "/%/");
    ASSERT_EQUALS_STRING("join float", "23/%/25.5/%/90", sFJ);
}

OMTEST(replaceAllTest)
{
    std::string s = "this is a test";
    std::string s2 = replaceAll(s, "is", "eee");
    ASSERT_EQUALS_STRING("replace", "theee eee a test", s2);
    
    std::string rr = replaceAll("ab", "b", "c");
    ASSERT_EQUALS_STRING("replace", "ac", rr);
}

#define FC(_fourChar) stringToFourChar(#_fourChar)
OMTEST(testFourChar)
{
    ASSERT_EQUALS_INT("four char", 'dcba', stringToFourChar("abcd"));
    ASSERT_EQUALS_INT("four char", FC(abcd), stringToFourChar("abcd"));
    ASSERT_EQUALS_STRING("four char", "wxyz", fourCharToString('zyxw'));
    ASSERT_EQUALS_STRING("four char", "wxyz", fourCharToString(FC(wxyz)));
}

OMTEST(testUpperLower)
{
    ASSERT_EQUALS_STRING("toUpper", "HELLO_THERE_NUMBER_6!", toUpper("hello_ThERE_number_6!"));
    ASSERT_EQUALS_STRING("toLower", "hello_there_number_6!", toLower("hello_ThERE_number_6!"));
    ASSERT_EQUALS_STRING("toLower", "Hello_there_number_6!", toLower("hello_ThERE_number_6!", true));
}

OMTEST(testMatching)
{
    ASSERT_TRUE("matching", matchGlob("fish", "fish"));
    ASSERT_TRUE("matching", matchGlob("fi*sh", "fish"));
    ASSERT_TRUE("matching", matchGlob("fi*sh", "fizsh"));
    ASSERT_FALSE("matching", matchGlob("fi*sh", "fsh"));
    ASSERT_TRUE("matching", matchGlob("a*z", "axlez"));
    ASSERT_FALSE("matching", matchGlob("a*z", "axlez2"));
    
    ASSERT_TRUE("matching", matchRe("(a.*z)", "axlez"));
    ASSERT_FALSE("matching", matchRe("a.*z", "axlez2"));
    
    auto parts = matchParts(".*(this.*?)(t.*)","is this what I think it is?");
    ASSERT_EQUALS_INT("parts matched", 3, parts.size());
    
    parts = matchParts("(.*)[.](.*?)$", "param[name=b].value");
    if(ASSERT_EQUALS_INT("parts", 3, parts.size()))
    {
        ASSERT_EQUALS_STRING("left part", "param[name=b]", parts[1]);
        ASSERT_EQUALS_STRING("right part", "value", parts[2]);
    }
}


static void check1(std::string s, int x)
{
    ASSERT_EQUALS_INT(s.c_str(), x, toInt(s));
}

static void checkf(std::string s, double x)
{
    ASSERT_EQUALS_FLOAT(s.c_str(), x, toDouble(s));
}

OMTEST(doNumbers)
{
    check1("0", 0);
    check1("-3", -3);
    check1("1.8", 1);
    check1("+5", 5);
    check1("23-4", 23);
    check1("0x011", 17);
    check1("0XAb", 171);
    check1("0x3", 3);
    check1("0XffF", 4095);
    
    checkf("0", 0);
    checkf("0.5", 0.5);
    checkf("23", 23);
    checkf("2e4", 20000);
    checkf("-1.7e-3", -.0017);
    checkf("+1.7e+3", 1700);
    
    checkf(" 0.5", 0.5);
    checkf("23 ", 23);
    checkf(" 2e4 ", 20000);
    checkf("   -1.7e-3 ", -.0017);
    
    checkf("0x45", 69);
}

OMTEST(doToFloats)
{
    float f[6] = {0};
    toFloats("1,  2,  -3  , 4e4,5,6", f, 4);
    ASSERT_EQUALS_FLOAT("toFloats", 1, f[0]);
    ASSERT_EQUALS_FLOAT("toFloats", 2, f[1]);
    ASSERT_EQUALS_FLOAT("toFloats", -3, f[2]);
    ASSERT_EQUALS_FLOAT("toFloats", 4e4, f[3]);
    ASSERT_EQUALS_FLOAT("toFloats", 0, f[4]);
    ASSERT_EQUALS_FLOAT("toFloats", 0, f[5]);
    
    toFloats("1", f, 6);
    ASSERT_EQUALS_FLOAT("toFloats", 1, f[0]);
    ASSERT_EQUALS_FLOAT("toFloats", 0, f[1]);
    ASSERT_EQUALS_FLOAT("toFloats", 0, f[2]);
    ASSERT_EQUALS_FLOAT("toFloats", 0, f[3]);
    ASSERT_EQUALS_FLOAT("toFloats", 0, f[4]);
    ASSERT_EQUALS_FLOAT("toFloats", 0, f[5]);
    
    auto fv = toFloats("1, 2, \n3,4   \n   ,5");
    ASSERT_EQUALS_INT("toFloats size", 5, fv.size());
    ASSERT_EQUALS_FLOAT("v", 1.0, fv[0]);
    ASSERT_EQUALS_FLOAT("v", 2.0, fv[1]);
    ASSERT_EQUALS_FLOAT("v", 3.0, fv[2]);
    ASSERT_EQUALS_FLOAT("v", 4.0, fv[3]);
    ASSERT_EQUALS_FLOAT("v", 5.0, fv[4]);
}

OMTEST(testShiftKeys)
{
#define CSK(_key, _expectedShiftKey) ASSERT_EQUALS_INT("shift", _expectedShiftKey, shift(_key));
    
    CSK('a', 'A');
    CSK('2', '@');
    CSK('[', '{');
    CSK('=', '+');
}

OMTEST(testCamelCase)
{
    ASSERT_EQUALS_STRING("camel", "thisIsIt", underscoreToCamelCase("FRONT_THIS_IS_IT"));
}

void doStringUtilTests()
{
    stringUtilTests();
    testSplitStrings();
    replaceAllTest();
    testStartsEndsWith();
    testContains();
    doTrim();
    testFourChar();
    testUpperLower();
    testJoinStrings();
    testMatching();
    doNumbers();
    doToFloats();
    testShiftKeys();
    testCamelCase();

    OMTESTEND();
}
