/**
* @file llpanelexperiencepicker.cpp
* @brief Implementation of llpanelexperiencepicker
* @author dolphin@lindenlab.com
*
* $LicenseInfo:firstyear=2014&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2014, 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;
* version 2.1 of the License only.
*
* 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
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
* $/LicenseInfo$
*/

#include "llviewerprecompiledheaders.h"

#include "llpanelexperiencepicker.h"


#include "lllineeditor.h"
#include "llfloaterreg.h"
#include "llscrolllistctrl.h"
#include "llviewerregion.h"
#include "llagent.h"
#include "llexperiencecache.h"
#include "llslurl.h"
#include "llavatarnamecache.h"
#include "llcombobox.h"
#include "llviewercontrol.h"
#include "llfloater.h"
#include "llregex.h"
#include "lltrans.h"

#define BTN_FIND        "find"
#define BTN_OK          "ok_btn"
#define BTN_CANCEL      "cancel_btn"
#define BTN_PROFILE     "profile_btn"
#define BTN_LEFT        "left_btn"
#define BTN_RIGHT       "right_btn"
#define TEXT_EDIT       "edit"
#define TEXT_MATURITY   "maturity"
#define LIST_RESULTS    "search_results"
#define PANEL_SEARCH    "search_panel"

const static std::string columnSpace = " ";

static LLPanelInjector<LLPanelExperiencePicker> t_panel_status("llpanelexperiencepicker");

LLPanelExperiencePicker::LLPanelExperiencePicker()
    :LLPanel()
{
    buildFromFile("panel_experience_search.xml");
    setDefaultFilters();
}

LLPanelExperiencePicker::~LLPanelExperiencePicker()
{
}

bool LLPanelExperiencePicker::postBuild()
{
    getChild<LLLineEditor>(TEXT_EDIT)->setKeystrokeCallback( boost::bind(&LLPanelExperiencePicker::editKeystroke, this, _1, _2),NULL);

    childSetAction(BTN_FIND, boost::bind(&LLPanelExperiencePicker::onBtnFind, this));
    getChildView(BTN_FIND)->setEnabled(true);

    LLScrollListCtrl* searchresults = getChild<LLScrollListCtrl>(LIST_RESULTS);
    searchresults->setDoubleClickCallback( boost::bind(&LLPanelExperiencePicker::onBtnSelect, this));
    searchresults->setCommitCallback(boost::bind(&LLPanelExperiencePicker::onList, this));
    getChildView(LIST_RESULTS)->setEnabled(false);
    getChild<LLScrollListCtrl>(LIST_RESULTS)->setCommentText(getString("no_results"));

    childSetAction(BTN_OK, boost::bind(&LLPanelExperiencePicker::onBtnSelect, this));
    getChildView(BTN_OK)->setEnabled(false);
    childSetAction(BTN_CANCEL, boost::bind(&LLPanelExperiencePicker::onBtnClose, this));
    childSetAction(BTN_PROFILE, boost::bind(&LLPanelExperiencePicker::onBtnProfile, this));
    getChildView(BTN_PROFILE)->setEnabled(false);

    getChild<LLComboBox>(TEXT_MATURITY)->setCurrentByIndex(gSavedPerAccountSettings.getU32("ExperienceSearchMaturity"));
    getChild<LLComboBox>(TEXT_MATURITY)->setCommitCallback(boost::bind(&LLPanelExperiencePicker::onMaturity, this));
    getChild<LLUICtrl>(TEXT_EDIT)->setFocus(true);

    childSetAction(BTN_LEFT, boost::bind(&LLPanelExperiencePicker::onPage, this, -1));
    childSetAction(BTN_RIGHT, boost::bind(&LLPanelExperiencePicker::onPage, this, 1));

    LLPanel* search_panel = getChild<LLPanel>(PANEL_SEARCH);
    if (search_panel)
    {
        // Start searching when Return is pressed in the line editor.
        search_panel->setDefaultBtn(BTN_FIND);
    }
    return true;
}

void LLPanelExperiencePicker::editKeystroke( class LLLineEditor* caller, void* user_data )
{
    getChildView(BTN_FIND)->setEnabled(true);
}

void LLPanelExperiencePicker::onBtnFind()
{
    mCurrentPage=1;
    boost::cmatch what;
    std::string text = getChild<LLUICtrl>(TEXT_EDIT)->getValue().asString();
    const boost::regex expression("secondlife:///app/experience/[\\da-f-]+/profile");
    if (ll_regex_match(text.c_str(), what, expression))
    {
        LLURI uri(text);
        LLSD path_array = uri.pathArray();
        if (path_array.size() == 4)
        {
            std::string exp_id = path_array.get(2).asString();
            LLUUID experience_id(exp_id);
            if (!experience_id.isNull())
            {
                const LLSD& experience_details = LLExperienceCache::instance().get(experience_id);
                if(!experience_details.isUndefined())
                {
                    std::string experience_name_string = experience_details[LLExperienceCache::NAME].asString();
                    if(!experience_name_string.empty())
                    {
                        getChild<LLUICtrl>(TEXT_EDIT)->setValue(experience_name_string);
                    }
                }
                else
                {
                    getChild<LLScrollListCtrl>(LIST_RESULTS)->deleteAllItems();
                    getChild<LLScrollListCtrl>(LIST_RESULTS)->setCommentText(getString("searching"));

                    getChildView(BTN_OK)->setEnabled(false);
                    getChildView(BTN_PROFILE)->setEnabled(false);

                    getChildView(BTN_RIGHT)->setEnabled(false);
                    getChildView(BTN_LEFT)->setEnabled(false);
                    LLExperienceCache::instance().get(experience_id, boost::bind(&LLPanelExperiencePicker::onBtnFind, this));
                    return;
                }
            }
        }
    }


    find();
}

void LLPanelExperiencePicker::onList()
{
    bool enabled = isSelectButtonEnabled();
    getChildView(BTN_OK)->setEnabled(enabled);

    enabled = enabled && getChild<LLScrollListCtrl>(LIST_RESULTS)->getNumSelected() == 1;
    getChildView(BTN_PROFILE)->setEnabled(enabled);
}

void LLPanelExperiencePicker::find()
{
    std::string text = getChild<LLUICtrl>(TEXT_EDIT)->getValue().asString();
    mQueryID.generate();

    LLExperienceCache::instance().findExperienceByName(text, mCurrentPage,
        boost::bind(&LLPanelExperiencePicker::findResults, getDerivedHandle<LLPanelExperiencePicker>(), mQueryID, _1));

    getChild<LLScrollListCtrl>(LIST_RESULTS)->deleteAllItems();
    getChild<LLScrollListCtrl>(LIST_RESULTS)->setCommentText(getString("searching"));

    getChildView(BTN_OK)->setEnabled(false);
    getChildView(BTN_PROFILE)->setEnabled(false);

    getChildView(BTN_RIGHT)->setEnabled(false);
    getChildView(BTN_LEFT)->setEnabled(false);
}

/*static*/
void LLPanelExperiencePicker::findResults(LLHandle<LLPanelExperiencePicker> hparent, LLUUID queryId, LLSD foundResult)
{
    if (hparent.isDead())
        return;

    LLPanelExperiencePicker* panel = hparent.get();
    if (panel)
    {
        panel->processResponse(queryId, foundResult);
    }
}

bool LLPanelExperiencePicker::isSelectButtonEnabled()
{
    LLScrollListCtrl* list=getChild<LLScrollListCtrl>(LIST_RESULTS);
    return list->getFirstSelectedIndex() >=0;
}

void LLPanelExperiencePicker::getSelectedExperienceIds( const LLScrollListCtrl* results, uuid_vec_t &experience_ids )
{
    std::vector<LLScrollListItem*> items = results->getAllSelected();
    for(std::vector<LLScrollListItem*>::iterator it = items.begin(); it != items.end(); ++it)
    {
        LLScrollListItem* item = *it;
        if (item->getUUID().notNull())
        {
            experience_ids.push_back(item->getUUID());
        }
    }
}

void LLPanelExperiencePicker::setAllowMultiple( bool allow_multiple )
{
    getChild<LLScrollListCtrl>(LIST_RESULTS)->setAllowMultipleSelection(allow_multiple);
}


void name_callback(const LLHandle<LLPanelExperiencePicker>& floater, const LLUUID& experience_id, const LLUUID& agent_id, const LLAvatarName& av_name)
{
    if(floater.isDead())
        return;
    LLPanelExperiencePicker* picker = floater.get();
    LLScrollListCtrl* search_results = picker->getChild<LLScrollListCtrl>(LIST_RESULTS);

    LLScrollListItem* item = search_results->getItem(experience_id);
    if(!item)
        return;

    item->getColumn(2)->setValue(columnSpace+av_name.getDisplayName());

}

void LLPanelExperiencePicker::processResponse( const LLUUID& query_id, const LLSD& content )
{
    if(query_id != mQueryID)
    {
        return;
    }

    mResponse = content;

    getChildView(BTN_RIGHT)->setEnabled(content.has("next_page_url"));
    getChildView(BTN_LEFT)->setEnabled(content.has("previous_page_url"));

    filterContent();

}

void LLPanelExperiencePicker::onBtnSelect()
{
    if(!isSelectButtonEnabled())
    {
        return;
    }

    if(mSelectionCallback)
    {
        const LLScrollListCtrl* results = getChild<LLScrollListCtrl>(LIST_RESULTS);
        uuid_vec_t experience_ids;

        getSelectedExperienceIds(results, experience_ids);
        mSelectionCallback(experience_ids);
        getChild<LLScrollListCtrl>(LIST_RESULTS)->deselectAllItems(true);
        if(mCloseOnSelect)
        {
            mCloseOnSelect = false;
            onBtnClose();
        }
    }
    else
    {
        onBtnProfile();
    }
}

void LLPanelExperiencePicker::onBtnClose()
{
    LLFloater* floater = getParentByType<LLFloater>();
    if (floater)
    {
        floater->closeFloater();
    }
}

void LLPanelExperiencePicker::onBtnProfile()
{
    LLScrollListItem* item = getChild<LLScrollListCtrl>(LIST_RESULTS)->getFirstSelected();
    if(item)
    {
        LLFloaterReg::showInstance("experience_profile", item->getUUID(), true);
    }
}

std::string LLPanelExperiencePicker::getMaturityString(int maturity)
{
    if(maturity <= SIM_ACCESS_PG)
    {
        return getString("maturity_icon_general");
    }
    else if(maturity <= SIM_ACCESS_MATURE)
    {
        return getString("maturity_icon_moderate");
    }
    return getString("maturity_icon_adult");
}

void LLPanelExperiencePicker::filterContent()
{
    LLScrollListCtrl* search_results = getChild<LLScrollListCtrl>(LIST_RESULTS);

    const LLSD& experiences=mResponse["experience_keys"];

    search_results->deleteAllItems();

    LLSD item;
    LLSD::array_const_iterator it = experiences.beginArray();
    for ( ; it != experiences.endArray(); ++it)
    {
        const LLSD& experience = *it;

        if(isExperienceHidden(experience))
            continue;

        std::string experience_name_string = experience[LLExperienceCache::NAME].asString();
        if (experience_name_string.empty())
        {
            experience_name_string = LLTrans::getString("ExperienceNameUntitled");
        }

        item["id"]=experience[LLExperienceCache::EXPERIENCE_ID];
        LLSD& columns = item["columns"];
        columns[0]["column"] = "maturity";
        columns[0]["value"] = getMaturityString(experience[LLExperienceCache::MATURITY].asInteger());
        columns[0]["type"]="icon";
        columns[0]["halign"]="right";
        columns[1]["column"] = "experience_name";
        columns[1]["value"] = columnSpace+experience_name_string;
        columns[2]["column"] = "owner";
        columns[2]["value"] = columnSpace+getString("loading");
        search_results->addElement(item);
        LLAvatarNameCache::get(experience[LLExperienceCache::AGENT_ID], boost::bind(name_callback, getDerivedHandle<LLPanelExperiencePicker>(), experience[LLExperienceCache::EXPERIENCE_ID], _1, _2));
    }

    if (search_results->isEmpty())
    {
        LLStringUtil::format_map_t map;
        std::string search_text = getChild<LLUICtrl>(TEXT_EDIT)->getValue().asString();
        map["[TEXT]"] = search_text;
        if (search_text.empty())
        {
            getChild<LLScrollListCtrl>(LIST_RESULTS)->setCommentText(getString("no_results"));
        }
        else
        {
            getChild<LLScrollListCtrl>(LIST_RESULTS)->setCommentText(getString("not_found", map));
        }
        search_results->setEnabled(false);
        getChildView(BTN_OK)->setEnabled(false);
        getChildView(BTN_PROFILE)->setEnabled(false);
    }
    else
    {
        getChildView(BTN_OK)->setEnabled(true);
        search_results->setEnabled(true);
        search_results->sortByColumnIndex(1, true);
        std::string text = getChild<LLUICtrl>(TEXT_EDIT)->getValue().asString();
        if (!search_results->selectItemByLabel(text, true, 1))
        {
            search_results->selectFirstItem();
        }
        onList();
        search_results->setFocus(true);
    }
}

void LLPanelExperiencePicker::onMaturity()
{
    gSavedPerAccountSettings.setU32("ExperienceSearchMaturity", getChild<LLComboBox>(TEXT_MATURITY)->getCurrentIndex());
    if(mResponse.has("experience_keys") && mResponse["experience_keys"].beginArray() != mResponse["experience_keys"].endArray())
    {
        filterContent();
    }
}

bool LLPanelExperiencePicker::isExperienceHidden( const LLSD& experience) const
{
    bool hide=false;
    filter_list::const_iterator it = mFilters.begin();
    for(/**/;it != mFilters.end(); ++it)
    {
        if((*it)(experience)){
            return true;
        }
    }

    return hide;
}

bool LLPanelExperiencePicker::FilterOverRating( const LLSD& experience )
{
    int maturity = getChild<LLComboBox>(TEXT_MATURITY)->getSelectedValue().asInteger();
    return experience[LLExperienceCache::MATURITY].asInteger() > maturity;
}

bool LLPanelExperiencePicker::FilterWithProperty( const LLSD& experience, S32 prop)
{
    return (experience[LLExperienceCache::PROPERTIES].asInteger() & prop) != 0;
}

bool LLPanelExperiencePicker::FilterWithoutProperties( const LLSD& experience, S32 prop)
{
    return ((experience[LLExperienceCache::PROPERTIES].asInteger() & prop) == prop);
}

bool LLPanelExperiencePicker::FilterWithoutProperty( const LLSD& experience, S32 prop )
{
    return (experience[LLExperienceCache::PROPERTIES].asInteger() & prop) == 0;
}

void LLPanelExperiencePicker::setDefaultFilters()
{
    mFilters.clear();
    addFilter(boost::bind(&LLPanelExperiencePicker::FilterOverRating, this, _1));
}

bool LLPanelExperiencePicker::FilterMatching( const LLSD& experience, const LLUUID& id )
{
    if(experience.isUUID())
    {
        return experience.asUUID() == id;
    }
    return experience[LLExperienceCache::EXPERIENCE_ID].asUUID() == id;
}

void LLPanelExperiencePicker::onPage( S32 direction )
{
    mCurrentPage += direction;
    if(mCurrentPage < 1)
    {
        mCurrentPage = 1;
    }
    find();
}