/**
 * @file llautoreplace.h
 * @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
 */
#ifndef LLAUTOREPLACE_H
#define LLAUTOREPLACE_H

#include "lllineeditor.h"

class LLAutoReplace;

/** The configuration data for the LLAutoReplace object
 *
 * This is a separate class so that the LLFloaterAutoReplaceSettings
 * can have a copy of the configuration to manipulate before committing
 * the changes back to the LLAutoReplace singleton that provides the
 * autoreplace callback.
 */
class LLAutoReplaceSettings
{
  public:
    LLAutoReplaceSettings();
    ~LLAutoReplaceSettings();

    /// Constructor for creating a tempory copy of the current settings
    LLAutoReplaceSettings(const LLAutoReplaceSettings& settings);

    /// Replace the current settings with new ones and save them to the user settings file
    void set(const LLAutoReplaceSettings& newSettings);

    /// Load the current settings read from an LLSD file
    bool setFromLLSD(const LLSD& settingsFromLLSD);
    ///< @returns whether or not the settingsFromLLSD were valid

    // ================================================================
    ///@{ @name List Operations
    // ================================================================

    /// @returns the configured list names as an LLSD Array of strings
    LLSD getListNames();

    /// Status values returned from the addList method
    typedef enum
    {
        AddListOk,
        AddListDuplicateName,
        AddListInvalidList,
    } AddListResult;

    /// Inserts a new list at the end of the priority order
    AddListResult addList(const LLSD& newList);

    /// Inserts a list in place of an existing list of the same name
    AddListResult replaceList(const LLSD& newList);

    /// Removes the named list, @returns false if not found
    bool removeReplacementList(std::string listName);

    /// Move the named list up in the priority order
    bool increaseListPriority(std::string listName);
    ///< @returns false if the list is not found

    /// Move the named list down in the priority order
    bool decreaseListPriority(std::string listName);
    ///< @returns false if the list is not found

    /// Get a copy of just one list (for saving to an export file)
    const LLSD* exportList(std::string listName);
    /// @returns an LLSD map

    /// Checks for required elements, and that each has the correct type.
    bool listIsValid(const LLSD& listSettings);

    /// Checks for required elements, and that each has the correct type.
    bool listNameIs(const LLSD& listSettings);

    /// Checks to see if a new lists name conflicts with one in the settings
    bool listNameIsUnique(const LLSD& newList);
    /// @note must be called with LLSD that has passed listIsValid

    /// Initializes emptyList to an empty list named 'Empty'
    static void createEmptyList(LLSD& emptyList);

    /// Resets the name of a list to a new value
    static void setListName(LLSD& list, const std::string& newName);

    /// Gets the name of a list
    static std::string getListName(LLSD& list);

    ///@}
    // ================================================================
    ///@{ @name Replacement Entry Operations
    // ================================================================

    /// Get the replacements specified by a given list
    const LLSD* getListEntries(std::string listName);
    ///< @returns an LLSD Map of keyword -> replacement test pairs

    /// Get the replacement for the keyword from the specified list
    std::string replacementFor(std::string keyword, std::string listName);

    /// Adds a keywword/replacement pair to the named list
    bool addEntryToList(LLWString keyword, LLWString replacement, std::string listName);

    /// Removes the keywword and its replacement from the named list
    bool removeEntryFromList(std::string keyword, std::string listName);

    /**
     * Look for currentWord in the lists in order, returning any substitution found
     * If no configured substitution is found, returns currentWord
     */
    std::string replaceWord(const std::string currentWord /**< word to search for */ );

    /// Provides a hard-coded example of settings
    LLSD getExampleLLSD();

    /// Get the actual settings as LLSD
    const LLSD& asLLSD();
    ///< @note for use only in AutoReplace::saveToUserSettings

  private:
    /// Efficiently and safely compare list names
    bool listNameMatches( const LLSD& list, const std::string name );

    /// The actual llsd data structure
    LLSD mLists;

    static const std::string AUTOREPLACE_LIST_NAME;      ///< key for looking up list names
    static const std::string AUTOREPLACE_LIST_REPLACEMENTS; ///< key for looking up replacement map

    /**<
     * LLSD structure of the lists
     * - The configuration is an array (mLists),
     * - Each entry in the array is a replacement list
     * - Each replacement list is a map with three keys:
     * @verbatim
     *     "name"         String    the name of the list
     *     "replacements" Map       keyword -> replacement pairs
     *
     * <llsd>
     *   <array>
     *     <map>
     *       <key>name</key>    <string>List 1</string>
     *       <key>data</key>
     *         <map>
     *           <key>keyword1</key>  <string>replacement1</string>
     *           <key>keyword2</key>  <string>replacement2</string>
     *         </map>
     *     </map>
     *     <map>
     *       <key>name</key>    <string>List 2</string>
     *       <key>data</key>
     *         <map>
     *           <key>keyword1</key>  <string>replacement1</string>
     *           <key>keyword2</key>  <string>replacement2</string>
     *         </map>
     *     </map>
     *   </array>
     * </llsd>
     * @endverbatim
     */
};

/** Provides a facility to auto-replace text dynamically as it is entered.
 *
 * When the end of a word is detected (defined as any punctuation character,
 * or any whitespace except newline or return), the preceding word is used
 * as a lookup key in an ordered list of maps.  If a match is found in any
 * map, the replacement start index and length are returned along with the
 * new replacement string.
 *
 * See the autoreplaceCallback method for how to add autoreplace functionality
 * to a text entry tool.
 */
class LLAutoReplace : public LLSingleton<LLAutoReplace>
{
    LLSINGLETON(LLAutoReplace);
public:
    /// Callback that provides the hook for use in text entry methods
    void autoreplaceCallback(S32& replacement_start, S32& replacement_length, LLWString& replacement_string, S32& cursor_pos, const LLWString& input_text);

    /// Get a copy of the current settings
    LLAutoReplaceSettings getSettings();

    /// Commit new settings after making changes
    void setSettings(const LLAutoReplaceSettings& settings);

private:
    /*virtual*/ void initSingleton() override;

    LLAutoReplaceSettings mSettings; ///< configuration information

    /// Read settings from persistent storage
    void loadFromSettings();

    /// Make the newSettings active and write them to user storage
    void saveToUserSettings();

    /// Compute the user settings file name
    std::string getUserSettingsFileName();

    /// Compute the (read-ony) application settings file name
    std::string getAppSettingsFileName();

    /// basename for the settings files
    static const char* SETTINGS_FILE_NAME;
};

#endif /* LLAUTOREPLACE_H */