#include "Rendering/AutoBlendQueue.h"

void AutoBlendQueue::Insert(AutoBlendJob autoBlendJob)
{
    if(!autoBlendJob.RootNode.HasComponent("Model")) {
        return;
    }

    AutoblendNode blendNode;
    blendNode.BlendJob = autoBlendJob;
    blendNode.StartTime = autoBlendJob.Delay;
    blendNode.EndTime = autoBlendJob.Delay + autoBlendJob.Duration;



    if (autoBlendJob.AnimationEntity.Valid()) {
        if (autoBlendJob.AnimationEntity.HasComponent("Animation")) {
            Model* model;
            try {
                model = ResourceManager::Load<::Model, true>((std::string)autoBlendJob.RootNode["Model"]["Resource"]);
            } catch (const std::exception&) {
                return;
            }

            Skeleton* skeleton = model->m_Skeleton;
            if (skeleton == nullptr) {
                return;
            }

            const Skeleton::Animation* animation = skeleton->GetAnimation((std::string)autoBlendJob.AnimationEntity["Animation"]["AnimationName"]);

            if (animation == nullptr) {
                return;
            }

            double AnimationDuration = 0.0;

            double animationSpeed = (double)autoBlendJob.AnimationEntity["Animation"]["Speed"];
            double animationTime = (double)autoBlendJob.AnimationEntity["Animation"]["Time"];

            if (animationSpeed != 0) {
                if ((bool)autoBlendJob.AnimationEntity["Animation"]["Reverse"]) {
                    AnimationDuration = (animation->Duration / animationSpeed) - (animation->Duration - animationTime);
                } else {
                    AnimationDuration = (animation->Duration / animationSpeed) - animationTime;
                }
            } else {
                return;
            }

            blendNode.StartTime += AnimationDuration;
            blendNode.EndTime += AnimationDuration;
            if (m_BlendQueue.size() == 0) {
                m_BlendQueue.push_back(blendNode);
            } else {
                for (auto it = m_BlendQueue.begin(); it != m_BlendQueue.end(); it++) {
                    auto next = std::next(it, 1);


                    if (it->BlendJob.BlendInfo.NodeName == autoBlendJob.BlendInfo.NodeName) {
                        (*it) = blendNode;
                    }

                    if (next != m_BlendQueue.end()) {
                        if (it->StartTime >= blendNode.StartTime && next->StartTime <= blendNode.StartTime) {
                            m_BlendQueue.insert(next, blendNode);
                            return;
                        }
                    } else if(it->StartTime > blendNode.StartTime){
                        m_BlendQueue.push_front(blendNode);
                        return;
                    } else if (it->StartTime <= blendNode.StartTime) {
                        m_BlendQueue.push_back(blendNode);
                        return;
                    }
                }
            }
        }
    }

    m_BlendQueue.clear();
    m_BlendQueue.push_back(blendNode);
}

void AutoBlendQueue::UpdateTime(double dt)
{
    for (auto it = m_BlendQueue.begin(); it != m_BlendQueue.end();) {
        if (it->EndTime <= 0) {
            it = m_BlendQueue.erase(it);
        } else {
            it->EndTime -= dt;
            it->StartTime -= dt;
            it++;
        }
    }
}

void AutoBlendQueue::PrintQueue()
{
    for (auto it = m_BlendQueue.begin(); it != m_BlendQueue.end(); it++) {
        LOG_INFO("Start: %f End: %f \t %s", it->StartTime, it->EndTime, it->BlendJob.BlendInfo.NodeName.c_str());
    }
}


bool AutoBlendQueue::HasActiveBlendJob()
{
    if(m_BlendQueue.empty()) {
        return false;
    } else {
        AutoblendNode blendNode = m_BlendQueue.front();
        if (blendNode.StartTime <= 0) {
            AutoBlendJob blendJob = blendNode.BlendJob;

            if (!blendJob.RootNode.Valid()) {
                m_BlendQueue.pop_front();
                return false;
            }

            if (!blendJob.RootNode.HasComponent("Model")) {
                m_BlendQueue.pop_front();
                return HasActiveBlendJob();
            }

            Model* model;
            try {
                model = ResourceManager::Load<::Model, true>(blendJob.RootNode["Model"]["Resource"]);
            } catch (const std::exception&) {
                //m_BlendQueue.pop_front();
                return HasActiveBlendJob();
            }

            Skeleton* skeleton = model->m_Skeleton;
            if (skeleton == nullptr) {
                m_BlendQueue.pop_front();
                return HasActiveBlendJob();
            }


            if (skeleton->BlendTrees.find(blendJob.RootNode) != skeleton->BlendTrees.end()) {
                return true;
            } else {
                m_BlendQueue.pop_front();
                return HasActiveBlendJob();
            }

            
        } else {
            return false;
        }
    }
}


std::shared_ptr<BlendTree> AutoBlendQueue::GetBlendTree()
{
    AutoblendNode blendNode = m_BlendQueue.front();
    AutoBlendJob blendJob = blendNode.BlendJob;

    if (!blendJob.RootNode.Valid()) {
        m_BlendQueue.pop_front();
        return false;
    }

    if (!blendJob.RootNode.HasComponent("Model")) {
        return nullptr;
    }

    Model* model;
    try {
        model = ResourceManager::Load<::Model, true>(blendJob.RootNode["Model"]["Resource"]);
    } catch (const std::exception&) {
        return nullptr;
    }

    Skeleton* skeleton = model->m_Skeleton;
    if (skeleton == nullptr) {
        return nullptr;
    }


    if (skeleton->BlendTrees.find(blendJob.RootNode) != skeleton->BlendTrees.end()) {
        return skeleton->BlendTrees.at(blendJob.RootNode);
    } else {
        return nullptr;
    }



}

AutoBlendQueue::AutoBlendJob& AutoBlendQueue::GetActiveBlendJob()
{
    AutoblendNode& blendNode = m_BlendQueue.front();
    blendNode.BlendJob.CurrentTime = -blendNode.StartTime;
    return blendNode.BlendJob;
}
