/** @file test_shorts.cpp
 *  Contains some short examples of performing regex match and regex replace with JPCRE2
 *  @include test_shorts.cpp
 * @author [Md Jahidul Hamid](https://github.com/neurobin)
 *  */

#include <iostream>
#include "jpcre2.hpp"

typedef jpcre2::select<char> jp; 


int main(){
    size_t count;
    //Check if string matches the pattern
    /*
     * The following uses a temporary Regex object.
     */
    if(jp::Regex("(\\d)|(\\w)").match("I am the subject"))
        std::cout<<"\nmatched";
    /*
     * Using the modifier S (i.e jpcre2::JIT_COMPILE) with temporary object may or may not give you
     * any performance boost (depends on the complexity of the pattern). The more complex
     * the pattern gets, the more sense the S modifier makes.
     */

    //If you want to match all and get the match count, use the action modifier 'g':
    std::cout<<"\n"<<
        jp::Regex("(\\d)|(\\w)","m").match("I am the subject","g");

    /*
     * Modifiers passed to the Regex constructor or with compile() function are compile modifiers
     * Modifiers passed with the match() or replace() functions are action modifiers
     */

    // Substrings/Captured groups:

    /*
     * *** Getting captured groups/substring ***
     *
     * captured groups or substrings are stored in maps/vectors for each match,
     * and each match is stored in a vector.
     * Thus captured groups are in a vector of maps/vectors.
     *
     * PCRE2 provides two types of substrings:
     *  1. numbered (indexed) substring
     *  2. named substring
     *
     * For the above two, we have two vectors respectively:
     *  1. jp::VecNum (Corresponding vector: jp::NumSub)
     *  2. jp::VecNas (Corresponding map: jp::MapNas)
     *
     * Another additional vector is available to get the substring position/number
     * for a particular captured group by name. It's a vector of name to number maps
     *  * jp::VecNtN (Corresponding map: jp:MapNtN)
     */

    // ***** Get numbered substring ***** ///
    jp::VecNum vec_num;
    jp::RegexMatch rm;
    jp::Regex re("(\\w+)\\s*(\\d+)","m");
    count =
    jp::RegexMatch(&re).setSubject("I am 23, I am digits 10")
                       .setModifier("g")
                       .setNumberedSubstringVector(&vec_num)
                       .match();
    /*
    * count (the return value) is guaranteed to give you the correct number of matches,
    * while vec_num.size() may give you wrong result if any match result
    * was failed to be inserted in the vector. This should not happen
    * i.e count and vec_num.size() should always be equal.
    */
    std::cout<<"\nNumber of matches: "<<count/* or vec_num.size()*/;

    //Now vec_num is populated with numbered substrings for each match
    //The size of vec_num is the total match count
    //vec_num[0] is the first match
    //The type of vec_num[0] is jp::NumSub
    std::cout<<"\nTotal match of first match: "<<vec_num[0][0];
    std::cout<<"\nCaptured group 1 of first match: "<<vec_num[0][1];
    std::cout<<"\nCaptured group 2 of first match: "<<vec_num[0][2];

    //captured group 3 doesn't exist, (with operator [] it's a segfault)
    //std::cout<<"\nCaptured group 3 of first match: "<<vec_num[0][3];
    
    //Using at() will throw std::out_of_range exception
    //~ try {
        //~ std::cout<<"\nCaptured group 3 of first match: "<<vec_num[0].at(3);
    //~ } catch (const std::out_of_range& e) {
        //~ std::cerr<<"\n"<<e.what();
    //~ }


    //There were two matches found (vec_num.size() == 2) in the above example
    std::cout<<"\nTotal match of second match: "<<vec_num[1][0];      //Total match (group 0) from second match
    std::cout<<"\nCaptured group 1 of second match: "<<vec_num[1][1]; //captured group 1 from second match
    std::cout<<"\nCaptured group 2 of second match: "<<vec_num[1][2]; //captured group 2 from second match


    // ***** Get named substring ***** //

    jp::VecNas vec_nas;
    jp::VecNtN vec_ntn; // We will get name to number map vector too
    re.compile("(?<word>\\w+)\\s*(?<digit>\\d+)","m");
    count =
    jp::RegexMatch(&re).setSubject("I am 23, I am digits 10")
                       .setModifier("g")
                       //.setNumberedSubstringVector(vec_num) // We don't need it in this example
                       .setNamedSubstringVector(&vec_nas)
                       .setNameToNumberMapVector(&vec_ntn) // Additional (name to number maps)
                       .match();
    std::cout<<"\nNumber of matches: "<<vec_nas.size()/* or count */;
    //Now vec_nas is populated with named substrings for each match
    //The size of vec_nas is the total match count
    //vec_nas[0] is the first match
    //The type of vec_nas[0] is jp::MapNas
    std::cout<<"\nCaptured group (word) of first match: "<<vec_nas[0]["word"];
    std::cout<<"\nCaptured group (digit) of first match: "<<vec_nas[0]["digit"];

    //Trying to access a non-existence named substirng with [] operator will give you empty string
    //If the existence of a substring is important, use the std::map::find() or std::map::at() 
    //(>=C++11) function to access map elements.
    /* //>=C++11
    try{
        ///This will throw exception because the substring name 'name' doesn't exist
        std::cout<<"\nCaptured group (name) of first match: "<<vec_nas[0].at("name");
    } catch(const std::logic_error& e){
        std::cerr<<"\nCaptured group (name) doesn't exist";
    }*/

    //There were two matches found (vec_nas.size() == 2) in the above example
    std::cout<<"\nCaptured group (word) of second match: "<<vec_nas[1]["word"];
    std::cout<<"\nCaptured group (digit) of second match: "<<vec_nas[1]["digit"];

    //Get the position (number) of a captured group name (that was found in match)
    std::cout<<"\nPosition of captured group (word) in first match: "<<vec_ntn[0]["word"];
    std::cout<<"\nPosition of captured group (digit) in first match: "<<vec_ntn[0]["digit"];

    /*
     * Replacement Examples
     * Replace pattern in a string with a replacement string
     *
     * The Regex::replace() function can take a subject and replacement string as argument.
     * 
     * You can also pass the subject with setSubject() function in method chain,
     * replacement string with setReplaceWith() function in method chain, etc ...
     * A call to RegexReplace::replace() in the method chain will return the resultant string
     */

    std::cout<<"\n"<<
    //replace first occurrence of a digit with @
    jp::Regex("\\d").replace("I am the subject string 44", "@");

    std::cout<<"\n"<<
    //replace all occurrences of a digit with @
    jp::Regex("\\d").replace("I am the subject string 44", "@", "g");

    //swap two parts of a string
    std::cout<<"\n"<<
    jp::Regex("^([^\t]+)\t([^\t]+)$")
        .replace("I am the subject\tTo be swapped according to tab", "$2 $1");
        
    //Doing the above with method chain:
    re.compile("^([^\t]+)\t([^\t]+)$");
    jp::RegexReplace(&re).setSubject("I am the subject\tTo be swapped according to tab")
                         .setReplaceWith("$2 $1")
                         .replace();
    return 0;
}
