/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Tencent is pleased to support the open source community by making behaviac available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at http://opensource.org/licenses/BSD-3-Clause
//
// Unless required by applicable law or agreed to in writing, software distributed under the License is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "behaviac/base/base.h"
#include "behaviac/behaviortree/nodes/composites/compositestochastic.h"
#include "behaviac/base/randomgenerator/randomgenerator.h"
#include "behaviac/base/object/method.h"

#include "behaviac/agent/agent.h"
#include "behaviac/behaviortree/nodes/actions/action.h"

namespace behaviac
{
    CompositeStochastic::CompositeStochastic() : m_method(0)
    {}

    CompositeStochastic::~CompositeStochastic()
    {
        BEHAVIAC_DELETE(m_method);
    }

    //CMethodBase* LoadMethod(const char* value);

    void CompositeStochastic::load(int version, const char* agentType, const properties_t& properties)
    {
        super::load(version, agentType, properties);

        for (propertie_const_iterator_t it = properties.begin(); it != properties.end(); ++it)
        {
            const property_t& p = (*it);

            if (strcmp(p.name, "RandomGenerator") == 0)
            {
                if (p.value[0] != '\0')
                {
                    this->m_method = Action::LoadMethod(p.value);
                }//if (p.value[0] != '\0')

            }
            else
            {
                //BEHAVIAC_ASSERT(0, "unrecognised property %s", p.name);
            }
        }
    }

    bool CompositeStochastic::IsValid(Agent* pAgent, BehaviorTask* pTask) const
    {
        if (!CompositeStochastic::DynamicCast(pTask->GetNode()))
        {
            return false;
        }

        return super::IsValid(pAgent, pTask);
    }

    CompositeStochasticTask::CompositeStochasticTask() : CompositeTask()
    {
    }

    CompositeStochasticTask::~CompositeStochasticTask()
    {
    }

    //generate a random double value between 0.0 and 1.0
    double GetRandomValue(CMethodBase* method, Agent* pAgent)
    {
        double value = 0;

        if (method)
        {
            //ParentType pt = method->GetParentType();
            Agent* pParent = pAgent;
            /*	if (pt == PT_INSTANCE)
            	{*/
            pParent = Agent::GetInstance(method->GetInstanceNameString(), pParent->GetContextId());
            BEHAVIAC_ASSERT(pParent);
            //}

            method->run(pParent, pAgent);
            bool bOk = method->GetReturnValue(pParent, value);
            BEHAVIAC_UNUSED_VAR(bOk);

        }
        else
        {
            RandomGenerator* pRandomGenerator = RandomGenerator::GetInstance();

            value = (*pRandomGenerator)();
        }

        return value;
    }

    void CompositeStochasticTask::random_child(Agent* pAgent)
    {
        BEHAVIAC_ASSERT(!this->GetNode() || CompositeStochastic::DynamicCast(this->GetNode()));
        const CompositeStochastic* pNode = (const CompositeStochastic*)(this->GetNode());

        if (this->m_set.size() != this->m_children.size())
        {
            this->m_set.resize(this->m_children.size());
        }

        uint32_t n = this->m_set.size();

        for (uint32_t i = 0; i < n; ++i)
        {
            this->m_set[i] = i;
        }

        for (uint32_t i = 0; i < n; ++i)
        {
            uint32_t index1 = (uint32_t)(n * GetRandomValue(pNode ? pNode->m_method : 0, pAgent));
            BEHAVIAC_ASSERT(index1 < n);

            uint32_t index2 = (uint32_t)(n * GetRandomValue(pNode ? pNode->m_method : 0, pAgent));
            BEHAVIAC_ASSERT(index2 < n);

            //swap
            if (index1 != index2)
            {
                uint32_t old = this->m_set[index1];
                this->m_set[index1] = this->m_set[index2];
                this->m_set[index2] = old;
            }
        }
    }

    void CompositeStochasticTask::copyto(BehaviorTask* target) const
    {
        super::copyto(target);

        BEHAVIAC_ASSERT(CompositeStochasticTask::DynamicCast(target));
        CompositeStochasticTask* ttask = (CompositeStochasticTask*)target;

        ttask->m_set = this->m_set;
    }

    void CompositeStochasticTask::save(ISerializableNode* node) const
    {
        super::save(node);

        if (this->m_status != BT_INVALID)
        {
            CSerializationID  setId("set");
            node->setAttr(setId, this->m_set);
        }
    }

    void CompositeStochasticTask::load(ISerializableNode* node)
    {
        super::load(node);

        if (this->m_status != BT_INVALID)
        {
            CSerializationID  setId("set");
            behaviac::string attrStr;
            node->getAttr(setId, attrStr);
            StringUtils::FromString(attrStr.c_str(), this->m_set);
        }
    }

    bool CompositeStochasticTask::onenter(Agent* pAgent)
    {
        BEHAVIAC_UNUSED_VAR(pAgent);
        BEHAVIAC_ASSERT(this->m_children.size() > 0);

        this->random_child(pAgent);

        this->m_activeChildIndex = 0;
        return true;
    }

    EBTStatus CompositeStochasticTask::update(Agent* pAgent, EBTStatus childStatus)
    {
        BEHAVIAC_UNUSED_VAR(pAgent);

        bool bFirst = true;

        BEHAVIAC_ASSERT(this->m_activeChildIndex != CompositeTask::InvalidChildIndex);

        // Keep going until a child behavior says its running.
        for (;;)
        {
            EBTStatus s = childStatus;

            if (!bFirst || s == BT_RUNNING)
            {
                uint32_t childIndex = this->m_set[this->m_activeChildIndex];
                BehaviorTask* pBehavior = this->m_children[childIndex];
                s = pBehavior->exec(pAgent);
            }

            bFirst = false;

            // If the child succeeds, or keeps running, do the same.
            if (s != BT_FAILURE)
            {
                return s;
            }

            // Hit the end of the array, job done!
            ++this->m_activeChildIndex;

            if (this->m_activeChildIndex >= (int)this->m_children.size())
            {
                return BT_FAILURE;
            }
        }
    }

    void CompositeStochasticTask::onexit(Agent* pAgent, EBTStatus s)
    {
        BEHAVIAC_UNUSED_VAR(pAgent);
        BEHAVIAC_UNUSED_VAR(s);
    }
}
