/**
 * @file llautoreplace.cpp
 * @brief Auto Replace Manager
 *
 * $LicenseInfo:firstyear=2012&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2012, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"
#include "llautoreplace.h"
#include "llsdserialize.h"
#include "llboost.h"
#include "llcontrol.h"
#include "llviewercontrol.h"
#include "llnotificationsutil.h"

const char* LLAutoReplace::SETTINGS_FILE_NAME = "autoreplace.xml";

void LLAutoReplace::autoreplaceCallback(S32& replacement_start, S32& replacement_length, LLWString& replacement_string, S32& cursor_pos, const LLWString& input_text)
{
    // make sure these returned values are cleared in case there is no replacement
    replacement_start = 0;
    replacement_length = 0;
    replacement_string.clear();

    static LLCachedControl<bool> perform_autoreplace(gSavedSettings, "AutoReplace", 0);
    if (perform_autoreplace)
    {
        S32 word_end = cursor_pos - 1;

        bool at_space  = (input_text[word_end] == ' ');
        bool have_word = (LLWStringUtil::isPartOfWord(input_text[word_end]));

        if (at_space || have_word)
        {
            if (at_space && word_end > 0)
            {
                // find out if this space immediately follows a word
                word_end--;
                have_word  = (LLWStringUtil::isPartOfWord(input_text[word_end]));
            }
            if (have_word)
            {
                // word_end points to the end of a word, now find the start of the word
                std::string word;
                S32 word_start = word_end;
                for (S32 back_one = word_start - 1;
                     back_one >= 0 && LLWStringUtil::isPartOfWord(input_text[back_one]);
                     back_one--
                    )
                {
                    word_start--; // walk word_start back to the beginning of the word
                }
                LL_DEBUGS("AutoReplace") << "word_start: " << word_start << " word_end: " << word_end << LL_ENDL;
                LLWString old_string = input_text.substr(word_start, word_end - word_start + 1);
                std::string last_word = wstring_to_utf8str(old_string);
                std::string replacement_word(mSettings.replaceWord(last_word));

                if (replacement_word != last_word)
                {
                    // The last word is one for which we have a replacement
                    if (at_space)
                    {
                        // return the replacement string
                        replacement_start = word_start;
                        replacement_length = word_end - word_start + 1;
                        replacement_string = utf8str_to_wstring(replacement_word);
                        S32 size_change = static_cast<S32>(replacement_string.size() - old_string.size());
                        cursor_pos += size_change;
                    }
                }
            }
        }
    }
}

std::string LLAutoReplace::getUserSettingsFileName()
{
    std::string path=gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "");

    if (!path.empty())
    {
        path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, SETTINGS_FILE_NAME);
    }
    return path;
}

std::string LLAutoReplace::getAppSettingsFileName()
{
    std::string path=gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "");

    if (!path.empty())
    {
        path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, SETTINGS_FILE_NAME);
    }
    else
    {
        LL_ERRS("AutoReplace") << "Failed to get app settings directory name" << LL_ENDL;
    }
    return path;
}

LLAutoReplaceSettings LLAutoReplace::getSettings()
{
    return mSettings;
}

void LLAutoReplace::setSettings(const LLAutoReplaceSettings& newSettings)
{
    mSettings.set(newSettings);
    /// Make the newSettings active and write them to user storage
    saveToUserSettings();
}

LLAutoReplace::LLAutoReplace()
{
}

void LLAutoReplace::initSingleton()
{
    loadFromSettings();
}

void LLAutoReplace::loadFromSettings()
{
    std::string filename=getUserSettingsFileName();
    if (filename.empty())
    {
        LL_INFOS("AutoReplace") << "no valid user settings directory." << LL_ENDL;
    }
    if(gDirUtilp->fileExists(filename))
    {
        LLSD userSettings;
        llifstream file;
        file.open(filename.c_str());
        if (file.is_open())
        {
            LLSDSerialize::fromXML(userSettings, file);
        }
        file.close();
        if ( mSettings.setFromLLSD(userSettings) )
        {
            LL_INFOS("AutoReplace") << "settings loaded from '" << filename << "'" << LL_ENDL;
        }
        else
        {
            LL_WARNS("AutoReplace") << "invalid settings found in '" << filename << "'" << LL_ENDL;
        }
    }
    else // no user settings found, try application settings
    {
        std::string defaultName = getAppSettingsFileName();
        LL_INFOS("AutoReplace") << " user settings file '" << filename << "' not found"<< LL_ENDL;

        bool gotSettings = false;
        if(gDirUtilp->fileExists(defaultName))
        {
            LLSD appDefault;
            llifstream file;
            file.open(defaultName.c_str());
            if (file.is_open())
            {
                LLSDSerialize::fromXMLDocument(appDefault, file);
            }
            file.close();

            if ( mSettings.setFromLLSD(appDefault) )
            {
                LL_INFOS("AutoReplace") << "settings loaded from '" << defaultName.c_str() << "'" << LL_ENDL;
                gotSettings = true;
            }
            else
            {
                LL_WARNS("AutoReplace") << "invalid settings found in '" << defaultName.c_str() << "'" << LL_ENDL;
            }
        }

        if ( ! gotSettings )
        {
            if (mSettings.setFromLLSD(mSettings.getExampleLLSD()))
            {
                LL_WARNS("AutoReplace") << "no settings found; loaded example." << LL_ENDL;
            }
            else
            {
                LL_WARNS("AutoReplace") << "no settings found and example invalid!" << LL_ENDL;
            }
        }
    }
}

void LLAutoReplace::saveToUserSettings()
{
    std::string filename=getUserSettingsFileName();
    llofstream file;
    file.open(filename.c_str());
    LLSDSerialize::toPrettyXML(mSettings.asLLSD(), file);
    file.close();
    LL_INFOS("AutoReplace") << "settings saved to '" << filename << "'" << LL_ENDL;
}

// ================================================================
// LLAutoReplaceSettings
// ================================================================

const std::string LLAutoReplaceSettings::AUTOREPLACE_LIST_NAME         = "name";         ///< key for looking up list names
const std::string LLAutoReplaceSettings::AUTOREPLACE_LIST_REPLACEMENTS = "replacements"; ///< key for looking up replacement map

LLAutoReplaceSettings::LLAutoReplaceSettings()
{
}

LLAutoReplaceSettings::LLAutoReplaceSettings(const LLAutoReplaceSettings& settings)
{
    // copy all values through fundamental type intermediates for thread safety
    mLists = LLSD::emptyArray();

    for ( LLSD::array_const_iterator list = settings.mLists.beginArray(), listEnd = settings.mLists.endArray();
          list != listEnd;
          list++
         )
    {
        if ( (*list).isMap() ) // can fail due to LLSD-30: ignore it
        {
            LLSD listMap = LLSD::emptyMap();
            std::string listName = (*list)[AUTOREPLACE_LIST_NAME];
            listMap[AUTOREPLACE_LIST_NAME] = listName;
            listMap[AUTOREPLACE_LIST_REPLACEMENTS] = LLSD::emptyMap();

            for ( LLSD::map_const_iterator
                      entry = (*list)[AUTOREPLACE_LIST_REPLACEMENTS].beginMap(),
                      entriesEnd = (*list)[AUTOREPLACE_LIST_REPLACEMENTS].endMap();
                  entry != entriesEnd;
                  entry++
                 )
            {
                std::string keyword = entry->first;
                std::string replacement = entry->second.asString();
                listMap[AUTOREPLACE_LIST_REPLACEMENTS].insert(keyword, LLSD(replacement));
            }

            mLists.append(listMap);
        }
    }
}

void LLAutoReplaceSettings::set(const LLAutoReplaceSettings& newSettings)
{
    mLists = newSettings.mLists;
}

bool LLAutoReplaceSettings::setFromLLSD(const LLSD& settingsFromLLSD)
{
    bool settingsValid = true;

    if ( settingsFromLLSD.isArray() )
    {
        for ( LLSD::array_const_iterator
                  list = settingsFromLLSD.beginArray(),
                  listEnd = settingsFromLLSD.endArray();
              settingsValid && list != listEnd;
              list++
             )
        {
            if ( (*list).isDefined() ) // can be undef due to LLSD-30: ignore it
            {
                settingsValid = listIsValid(*list);
            }
        }
    }
    else
    {
        settingsValid = false;
        LL_WARNS("AutoReplace") << "settings are not an array" << LL_ENDL;
    }

    if ( settingsValid )
    {
        mLists = settingsFromLLSD;
    }
    else
    {
        LL_WARNS("AutoReplace") << "invalid settings discarded; using hard coded example" << LL_ENDL;
    }

    return settingsValid;
}

bool LLAutoReplaceSettings::listNameMatches( const LLSD& list, const std::string name )
{
    return list.isMap()
        && list.has(AUTOREPLACE_LIST_NAME)
        && list[AUTOREPLACE_LIST_NAME].asString() == name;
}

const LLSD* LLAutoReplaceSettings::getListEntries(std::string listName)
{
    const LLSD* returnedEntries = NULL;
    for( LLSD::array_const_iterator list = mLists.beginArray(), endList = mLists.endArray();
         returnedEntries == NULL && list != endList;
         list++
        )
    {
        const LLSD& thisList = *list;
        if ( listNameMatches(thisList, listName) )
        {
            returnedEntries = &thisList[AUTOREPLACE_LIST_REPLACEMENTS];
        }
    }
    return returnedEntries;
}

std::string LLAutoReplaceSettings::replacementFor(std::string keyword, std::string listName)
{
    std::string replacement;
    bool foundList = false;
    for( LLSD::array_const_iterator list = mLists.beginArray(), endList = mLists.endArray();
         ! foundList && list != endList;
         list++
        )
    {
        const LLSD& thisList = *list;
        if ( listNameMatches(thisList, listName) )
        {
            foundList = true; // whether there is a replacement or not, we're done
            if (   thisList.isMap()
                && thisList.has(AUTOREPLACE_LIST_REPLACEMENTS)
                && thisList[AUTOREPLACE_LIST_REPLACEMENTS].has(keyword)
                )
            {
                replacement = thisList[AUTOREPLACE_LIST_REPLACEMENTS][keyword].asString();
                LL_DEBUGS("AutoReplace")<<"'"<<keyword<<"' -> '"<<replacement<<"'"<<LL_ENDL;
            }
        }
        if (!foundList)
        {
            LL_WARNS("AutoReplace")<<"failed to find list '"<<listName<<"'"<<LL_ENDL;
        }
    }
    if (replacement.empty())
    {
        LL_WARNS("AutoReplace")<<"failed to find '"<<keyword<<"'"<<LL_ENDL;
    }
    return replacement;
}

LLSD LLAutoReplaceSettings::getListNames()
{
    LL_DEBUGS("AutoReplace")<<"====="<<LL_ENDL;
    LLSD toReturn = LLSD::emptyArray();
    S32 counter=0;
    for( LLSD::array_const_iterator list = mLists.beginArray(), endList = mLists.endArray();
         list != endList;
         list++
        )
    {
        const LLSD& thisList = *list;
        if ( thisList.isMap() )
        {
            if ( thisList.has(AUTOREPLACE_LIST_NAME) )
            {
                std::string name = thisList[AUTOREPLACE_LIST_NAME].asString();
                LL_DEBUGS("AutoReplace")<<counter<<" '"<<name<<"'"<<LL_ENDL;
                toReturn.append(LLSD(name));
            }
            else
            {
                LL_ERRS("AutoReplace") <<counter<<" ! MISSING "<<AUTOREPLACE_LIST_NAME<< LL_ENDL;
            }
        }
        else
        {
            LL_WARNS("AutoReplace")<<counter<<" ! not a map: "<<LLSD::typeString(thisList.type())<< LL_ENDL;
        }
        counter++;
    }
    LL_DEBUGS("AutoReplace")<<"^^^^^^"<<LL_ENDL;
    return toReturn;
}

bool LLAutoReplaceSettings::listIsValid(const LLSD& list)
{
    bool listValid = true;
    if ( ! list.isMap() )
    {
        listValid = false;
        LL_WARNS("AutoReplace") << "list is not a map" << LL_ENDL;
    }
    else if (   ! list.has(AUTOREPLACE_LIST_NAME)
             || ! list[AUTOREPLACE_LIST_NAME].isString()
             || list[AUTOREPLACE_LIST_NAME].asString().empty()
             )
    {
        listValid = false;
        LL_WARNS("AutoReplace")
            << "list found without " << AUTOREPLACE_LIST_NAME
            << " (or it is empty)"
            << LL_ENDL;
    }
    else if ( ! list.has(AUTOREPLACE_LIST_REPLACEMENTS) || ! list[AUTOREPLACE_LIST_REPLACEMENTS].isMap() )
    {
        listValid = false;
        LL_WARNS("AutoReplace") << "list '" << list[AUTOREPLACE_LIST_NAME].asString() << "' without " << AUTOREPLACE_LIST_REPLACEMENTS << LL_ENDL;
    }
    else
    {
        for ( LLSD::map_const_iterator
                  entry = list[AUTOREPLACE_LIST_REPLACEMENTS].beginMap(),
                  entriesEnd = list[AUTOREPLACE_LIST_REPLACEMENTS].endMap();
              listValid && entry != entriesEnd;
              entry++
             )
        {
            if ( ! entry->second.isString() )
            {
                listValid = false;
                LL_WARNS("AutoReplace")
                    << "non-string replacement value found in list '"
                    << list[AUTOREPLACE_LIST_NAME].asString() << "'"
                    << LL_ENDL;
            }
        }
    }

    return listValid;
}

const LLSD* LLAutoReplaceSettings::exportList(std::string listName)
{
    const LLSD* exportedList = NULL;
    for ( LLSD::array_const_iterator list = mLists.beginArray(), listEnd = mLists.endArray();
          exportedList == NULL && list != listEnd;
          list++
         )
    {
        if ( listNameMatches(*list, listName) )
        {
            const LLSD& namedList = (*list);
            exportedList = &namedList;
        }
    }
    return exportedList;
}

bool LLAutoReplaceSettings::listNameIsUnique(const LLSD& newList)
{
    bool nameIsUnique = true;
    // this must always be called with a valid list, so it is safe to assume it has a name
    std::string newListName = newList[AUTOREPLACE_LIST_NAME].asString();
    for ( LLSD::array_const_iterator list = mLists.beginArray(), listEnd = mLists.endArray();
          nameIsUnique && list != listEnd;
          list++
         )
    {
        if ( listNameMatches(*list, newListName) )
        {
            LL_WARNS("AutoReplace")<<"duplicate list name '"<<newListName<<"'"<<LL_ENDL;
            nameIsUnique = false;
        }
    }
    return nameIsUnique;
}

/* static */
void LLAutoReplaceSettings::createEmptyList(LLSD& emptyList)
{
    emptyList = LLSD::emptyMap();
    emptyList[AUTOREPLACE_LIST_NAME] = "Empty";
    emptyList[AUTOREPLACE_LIST_REPLACEMENTS] = LLSD::emptyMap();
}

/* static */
void LLAutoReplaceSettings::setListName(LLSD& list, const std::string& newName)
{
    list[AUTOREPLACE_LIST_NAME] = newName;
}

/* static */
std::string LLAutoReplaceSettings::getListName(LLSD& list)
{
    std::string name;
    if ( list.isMap() && list.has(AUTOREPLACE_LIST_NAME) && list[AUTOREPLACE_LIST_NAME].isString() )
    {
        name = list[AUTOREPLACE_LIST_NAME].asString();
    }
    return name;
}

LLAutoReplaceSettings::AddListResult LLAutoReplaceSettings::addList(const LLSD& newList)
{
    AddListResult result;
    if ( listIsValid( newList ) )
    {
        if ( listNameIsUnique( newList ) )
        {
            mLists.append(newList);
            result = AddListOk;
        }
        else
        {
            LL_WARNS("AutoReplace") << "attempt to add duplicate name" << LL_ENDL;
            result = AddListDuplicateName;
        }
    }
    else
    {
        LL_WARNS("AutoReplace") << "attempt to add invalid list" << LL_ENDL;
        result = AddListInvalidList;
    }
    return result;
}

LLAutoReplaceSettings::AddListResult LLAutoReplaceSettings::replaceList(const LLSD& newList)
{
    AddListResult result = AddListInvalidList;
    if ( listIsValid( newList ) )
    {
        std::string listName = newList[AUTOREPLACE_LIST_NAME].asString();
        bool listFound = false;
        S32 search_index;
        LLSD targetList;
        // The following is working around the fact that LLSD arrays containing maps also seem to have undefined entries... see LLSD-30
        for ( search_index = 0, targetList = mLists[0];
              !listFound && search_index < mLists.size();
              search_index += 1, targetList = mLists[search_index]
             )
        {
            if ( targetList.isMap() )
            {
                if ( listNameMatches( targetList, listName) )
                {
                    LL_DEBUGS("AutoReplace")<<"list to replace found at "<<search_index<<LL_ENDL;
                    mLists.erase(search_index);
                    mLists.insert(search_index, newList);
                    listFound = true;
                    result = AddListOk;
                }
            }
        }

        if ( ! listFound )
        {
            LL_WARNS("AutoReplace") << "attempt to replace unconfigured list" << LL_ENDL;
        }
    }
    else
    {
        LL_WARNS("AutoReplace") << "attempt to add invalid list" << LL_ENDL;
    }
    return result;
}

bool LLAutoReplaceSettings::removeReplacementList(std::string listName)
{
    bool found = false;
    for( S32 index = 0; !found && mLists[index].isDefined(); index++ )
    {
        if( listNameMatches(mLists.get(index), listName) )
        {
            LL_DEBUGS("AutoReplace")<<"list '"<<listName<<"'"<<LL_ENDL;
            mLists.erase(index);
            found = true;
        }
    }
    return found;
}

/// Move the named list up in the priority order
bool LLAutoReplaceSettings::increaseListPriority(std::string listName)
{
    LL_DEBUGS("AutoReplace")<<listName<<LL_ENDL;
    bool found = false;
    S32 search_index, previous_index;
    LLSD targetList;
    // The following is working around the fact that LLSD arrays containing maps also seem to have undefined entries... see LLSD-30
    previous_index = -1;
    for ( search_index = 0, targetList = mLists[0];
          !found && search_index < mLists.size();
          search_index += 1, targetList = mLists[search_index]
         )
    {
        if ( targetList.isMap() )
        {
            if ( listNameMatches( targetList, listName) )
            {
                LL_DEBUGS("AutoReplace")<<"found at "<<search_index<<", previous is "<<previous_index<<LL_ENDL;
                found = true;
                if (previous_index >= 0)
                {
                    LL_DEBUGS("AutoReplace") << "erase "<<search_index<<LL_ENDL;
                    mLists.erase(search_index);
                    LL_DEBUGS("AutoReplace") << "insert at "<<previous_index<<LL_ENDL;
                    mLists.insert(previous_index, targetList);
                }
                else
                {
                    LL_WARNS("AutoReplace") << "attempted to move top list up" << LL_ENDL;
                }
            }
            else
            {
                previous_index = search_index;
            }
        }
        else
        {
            LL_DEBUGS("AutoReplace") << search_index<<" is "<<LLSD::typeString(targetList.type())<<LL_ENDL;
        }
    }
    return found;
}

/// Move the named list down in the priority order
bool LLAutoReplaceSettings::decreaseListPriority(std::string listName)
{
    LL_DEBUGS("AutoReplace")<<listName<<LL_ENDL;
    S32 found_index = -1;
    S32 search_index;
    for ( search_index = 0;
          found_index == -1 && search_index < mLists.size();
          search_index++
         )
    {
        if ( listNameMatches( mLists[search_index], listName) )
        {
            LL_DEBUGS("AutoReplace")<<"found at index "<<search_index<<LL_ENDL;
            found_index = search_index;
        }
    }
    if (found_index != -1)
    {
        S32 next_index;
        for ( next_index = found_index+1;
              next_index < mLists.size() && ! mLists[next_index].isMap();
              next_index++
             )
        {
            // skipping over any undefined slots (see LLSD-30)
            LL_WARNS("AutoReplace")<<next_index<<" ! not a map: "<<LLSD::typeString(mLists[next_index].type())<< LL_ENDL;
        }
        if ( next_index < mLists.size() )
        {
            LLSD next_list = mLists[next_index];
            LL_DEBUGS("AutoReplace") << "erase "<<next_index<<LL_ENDL;
            mLists.erase(next_index);
            LL_DEBUGS("AutoReplace") << "insert at "<<found_index<<LL_ENDL;
            mLists.insert(found_index, next_list);
        }
        else
        {
            LL_WARNS("AutoReplace") << "attempted to move bottom list down" << LL_ENDL;
        }
    }
    else
    {
        LL_WARNS("AutoReplace") << "not found" << LL_ENDL;
    }
    return (found_index != -1);
}


std::string LLAutoReplaceSettings::replaceWord(const std::string currentWord)
{
    std::string returnedWord = currentWord; // in case no replacement is found
    static LLCachedControl<bool> autoreplace_enabled(gSavedSettings, "AutoReplace", false);
    if ( autoreplace_enabled )
    {
        LL_DEBUGS("AutoReplace")<<"checking '"<<currentWord<<"'"<< LL_ENDL;
        //loop through lists in order
        bool found = false;
        for( LLSD::array_const_iterator list = mLists.beginArray(), endLists = mLists.endArray();
             ! found && list != endLists;
             list++
            )
        {
            const LLSD& checkList = *list;
            const LLSD& replacements = checkList[AUTOREPLACE_LIST_REPLACEMENTS];

            if ( replacements.has(currentWord) )
            {
                found = true;
                LL_DEBUGS("AutoReplace")
                    << "  found in list '" << checkList[AUTOREPLACE_LIST_NAME].asString()
                    << " => '" << replacements[currentWord].asString() << "'"
                    << LL_ENDL;
                returnedWord = replacements[currentWord].asString();
            }
        }
    }
    return returnedWord;
}

bool LLAutoReplaceSettings::addEntryToList(LLWString keyword, LLWString replacement, std::string listName)
{
    bool added = false;

    if ( ! keyword.empty() && ! replacement.empty() )
    {
        bool isOneWord = true;
        for (S32 character = 0; isOneWord && character < keyword.size(); character++ )
        {
            if ( ! LLWStringUtil::isPartOfWord(keyword[character]) )
            {
                LL_WARNS("AutoReplace") << "keyword '" << wstring_to_utf8str(keyword) << "' not a single word (len "<<keyword.size()<<" '"<<character<<"')" << LL_ENDL;
                isOneWord = false;
            }
        }

        if ( isOneWord )
        {
            bool listFound = false;
            for( LLSD::array_iterator list = mLists.beginArray(), endLists = mLists.endArray();
                 ! listFound && list != endLists;
                 list++
                )
            {
                if ( listNameMatches(*list, listName) )
                {
                    listFound = true;
                    (*list)[AUTOREPLACE_LIST_REPLACEMENTS][wstring_to_utf8str(keyword)]=wstring_to_utf8str(replacement);
                }
            }
            if (listFound)
            {
                added = true;
            }
            else
            {
                LL_WARNS("AutoReplace") << "list '" << listName << "' not found" << LL_ENDL;
            }
        }
    }

    return added;
}

bool LLAutoReplaceSettings::removeEntryFromList(std::string keyword, std::string listName)
{
    bool found = false;
    for( LLSD::array_iterator list = mLists.beginArray(), endLists = mLists.endArray();
         ! found && list != endLists;
         list++
        )
    {
        if ( listNameMatches(*list, listName) )
        {
            found = true;
            (*list)[AUTOREPLACE_LIST_REPLACEMENTS].erase(keyword);
        }
    }
    if (!found)
    {
        LL_WARNS("AutoReplace") << "list '" << listName << "' not found" << LL_ENDL;
    }
    return found;
}

LLSD LLAutoReplaceSettings::getExampleLLSD()
{
    LL_DEBUGS("AutoReplace")<<LL_ENDL;
    LLSD example = LLSD::emptyArray();

    example[0] = LLSD::emptyMap();
    example[0][AUTOREPLACE_LIST_NAME]    = "Example List 1";
    example[0][AUTOREPLACE_LIST_REPLACEMENTS]    = LLSD::emptyMap();
    example[0][AUTOREPLACE_LIST_REPLACEMENTS]["keyword1"] = "replacement string 1";
    example[0][AUTOREPLACE_LIST_REPLACEMENTS]["keyword2"] = "replacement string 2";

    example[1] = LLSD::emptyMap();
    example[1][AUTOREPLACE_LIST_NAME]    = "Example List 2";
    example[1][AUTOREPLACE_LIST_REPLACEMENTS]    = LLSD::emptyMap();
    example[1][AUTOREPLACE_LIST_REPLACEMENTS]["mistake1"] = "correction 1";
    example[1][AUTOREPLACE_LIST_REPLACEMENTS]["mistake2"] = "correction 2";

    return example;
}

const LLSD& LLAutoReplaceSettings::asLLSD()
{
    return mLists;
}

LLAutoReplaceSettings::~LLAutoReplaceSettings()
{
}