/**
* @file llmenuoptionpathfindingrebakenavmesh.cpp
* @brief Implementation of llmenuoptionpathfindingrebakenavmesh
* @author Prep@lindenlab.com
*
* $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;
* 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 "llmenuoptionpathfindingrebakenavmesh.h"

#include <boost/bind.hpp>
#include <boost/signals2.hpp>

#include "llagent.h"
#include "llnotificationsutil.h"
#include "llpathfindingmanager.h"
#include "llpathfindingnavmesh.h"
#include "llpathfindingnavmeshstatus.h"
#include "llviewerregion.h"

LLMenuOptionPathfindingRebakeNavmesh::LLMenuOptionPathfindingRebakeNavmesh() :
    mIsInitialized(false),
    mCanRebakeRegion(false),
    mRebakeNavMeshMode(kRebakeNavMesh_Default),
    mNavMeshSlot(),
    mRegionCrossingSlot(),
    mAgentStateSlot()
{
}

LLMenuOptionPathfindingRebakeNavmesh::~LLMenuOptionPathfindingRebakeNavmesh()
{
    if (mIsInitialized)
    {
        if (mRebakeNavMeshMode == kRebakeNavMesh_RequestSent)
        {
            LL_WARNS("navmeshRebaking") << "During destruction of the LLMenuOptionPathfindingRebakeNavmesh "
                << "singleton, the mode indicates that a request has been sent for which a response has yet "
                << "to be received.  This could contribute to a crash on exit." << LL_ENDL;
        }

        quit();
    }
}

void LLMenuOptionPathfindingRebakeNavmesh::initialize()
{
    if (!mIsInitialized)
    {
        mIsInitialized = true;

        setMode(kRebakeNavMesh_Default);

        createNavMeshStatusListenerForCurrentRegion();

        if ( !mRegionCrossingSlot.connected() )
        {
            mRegionCrossingSlot = gAgent.addRegionChangedCallback(boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleRegionBoundaryCrossed, this));
        }

        if (!mAgentStateSlot.connected())
        {
            mAgentStateSlot = LLPathfindingManager::getInstance()->registerAgentStateListener(boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleAgentState, this, _1));
        }
        LLPathfindingManager::getInstance()->requestGetAgentState();
    }
}

void LLMenuOptionPathfindingRebakeNavmesh::quit()
{
    if (mIsInitialized)     // Quitting from the login screen leaves this uninitialized
    {
        if (mNavMeshSlot.connected())
        {
            mNavMeshSlot.disconnect();
        }

        if (mRegionCrossingSlot.connected())
        {
            mRegionCrossingSlot.disconnect();
        }

        if (mAgentStateSlot.connected())
        {
            mAgentStateSlot.disconnect();
        }

        mIsInitialized = false;
    }
}

bool LLMenuOptionPathfindingRebakeNavmesh::canRebakeRegion() const
{
    if (!mIsInitialized)
    {
        LL_ERRS("navmeshRebaking") << "LLMenuOptionPathfindingRebakeNavmesh class has not been initialized "
            << "when the ability to rebake navmesh is being requested." << LL_ENDL;
    }
    return mCanRebakeRegion;
}

LLMenuOptionPathfindingRebakeNavmesh::ERebakeNavMeshMode LLMenuOptionPathfindingRebakeNavmesh::getMode() const
{
    if (!mIsInitialized)
    {
        LL_ERRS("navmeshRebaking") << "LLMenuOptionPathfindingRebakeNavmesh class has not been initialized "
            << "when the mode is being requested." << LL_ENDL;
    }
    return mRebakeNavMeshMode;
}

void LLMenuOptionPathfindingRebakeNavmesh::sendRequestRebakeNavmesh()
{
    if (!mIsInitialized)
    {
        LL_ERRS("navmeshRebaking") << "LLMenuOptionPathfindingRebakeNavmesh class has not been initialized "
            << "when the request is being made to rebake the navmesh." << LL_ENDL;
    }
    else
    {
        if (!canRebakeRegion())
        {
            LL_WARNS("navmeshRebaking") << "attempting to rebake navmesh when user does not have permissions "
                << "on this region" << LL_ENDL;
        }
        if (getMode() != kRebakeNavMesh_Available)
        {
            LL_WARNS("navmeshRebaking") << "attempting to rebake navmesh when mode is not available"
                << LL_ENDL;
        }

        setMode(kRebakeNavMesh_RequestSent);
        LLPathfindingManager::getInstance()->requestRebakeNavMesh(boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleRebakeNavMeshResponse, this, _1));
    }
}

void LLMenuOptionPathfindingRebakeNavmesh::setMode(ERebakeNavMeshMode pRebakeNavMeshMode)
{
    mRebakeNavMeshMode = pRebakeNavMeshMode;
}

void LLMenuOptionPathfindingRebakeNavmesh::handleAgentState(bool pCanRebakeRegion)
{
    llassert(mIsInitialized);
    mCanRebakeRegion = pCanRebakeRegion;
}

void LLMenuOptionPathfindingRebakeNavmesh::handleRebakeNavMeshResponse(bool pResponseStatus)
{
    llassert(mIsInitialized);
    if (mIsInitialized)
    {
        if (getMode() == kRebakeNavMesh_RequestSent)
        {
            setMode(pResponseStatus ? kRebakeNavMesh_InProgress : kRebakeNavMesh_Default);
        }

        if (!pResponseStatus)
        {
            LLNotificationsUtil::add("PathfindingCannotRebakeNavmesh");
        }
    }
}

void LLMenuOptionPathfindingRebakeNavmesh::handleNavMeshStatus(const LLPathfindingNavMeshStatus &pNavMeshStatus)
{
    llassert(mIsInitialized);
    if (mIsInitialized)
    {
        ERebakeNavMeshMode rebakeNavMeshMode = kRebakeNavMesh_Default;
        if (pNavMeshStatus.isValid())
        {
            switch (pNavMeshStatus.getStatus())
            {
            case LLPathfindingNavMeshStatus::kPending :
            case LLPathfindingNavMeshStatus::kRepending :
                rebakeNavMeshMode = kRebakeNavMesh_Available;
                break;
            case LLPathfindingNavMeshStatus::kBuilding :
                rebakeNavMeshMode = kRebakeNavMesh_InProgress;
                break;
            case LLPathfindingNavMeshStatus::kComplete :
                rebakeNavMeshMode = kRebakeNavMesh_NotAvailable;
                break;
            default :
                rebakeNavMeshMode = kRebakeNavMesh_Default;
                llassert(0);
                break;
            }
        }

        setMode(rebakeNavMeshMode);
    }
}

void LLMenuOptionPathfindingRebakeNavmesh::handleRegionBoundaryCrossed()
{
    llassert(mIsInitialized);
    if (mIsInitialized)
    {
        createNavMeshStatusListenerForCurrentRegion();
        mCanRebakeRegion = false;
        LLPathfindingManager::getInstance()->requestGetAgentState();
    }
}

void LLMenuOptionPathfindingRebakeNavmesh::createNavMeshStatusListenerForCurrentRegion()
{
    if (mNavMeshSlot.connected())
    {
        mNavMeshSlot.disconnect();
    }

    LLViewerRegion *currentRegion = gAgent.getRegion();
    if (currentRegion != NULL)
    {
        mNavMeshSlot = LLPathfindingManager::getInstance()->registerNavMeshListenerForRegion(currentRegion, boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleNavMeshStatus, this, _2));
        LLPathfindingManager::getInstance()->requestGetNavMeshForRegion(currentRegion, true);
    }
}