#ifndef _RIVE_IMPORT_STACK_HPP_
#define _RIVE_IMPORT_STACK_HPP_
#include "rive/status_code.hpp"
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <memory>

namespace rive
{
class ImportStackObject
{
public:
    virtual ~ImportStackObject() {}
    virtual StatusCode resolve() { return StatusCode::Ok; }
    virtual bool readNullObject() { return false; }
};

class ImportStack
{
public:
    template <typename T = ImportStackObject> T* latest(uint16_t coreType)
    {
        auto itr = m_latests.find(coreType);
        if (itr == m_latests.end())
        {
            return nullptr;
        }
        return static_cast<T*>(itr->second.get());
    }

    StatusCode makeLatest(uint16_t coreType,
                          std::unique_ptr<ImportStackObject> object)
    {
        // Clean up the old object in the stack.
        auto itr = m_latests.find(coreType);
        if (itr != m_latests.end())
        {
            auto stackObject = itr->second.get();

            // Remove it from latests.
            auto lastAddedItr =
                std::find(m_lastAdded.begin(), m_lastAdded.end(), stackObject);
            if (lastAddedItr != m_lastAdded.end())
            {
                m_lastAdded.erase(lastAddedItr);
            }

            StatusCode code = stackObject->resolve();
            if (code != StatusCode::Ok)
            {
                m_latests.erase(coreType);
                return code;
            }
        }

        // Set the new one.
        if (object == nullptr)
        {
            m_latests.erase(coreType);
        }
        else
        {
            m_lastAdded.push_back(object.get());
            m_latests[coreType] = std::move(object);
        }
        return StatusCode::Ok;
    }

    StatusCode resolve()
    {
        StatusCode returnCode = StatusCode::Ok;

        // Reverse iterate the last added import stack objects and resolve them.
        // If any don't resolve, capture the return code and stop resolving.
        for (auto itr = m_lastAdded.rbegin(); itr != m_lastAdded.rend(); itr++)
        {
            StatusCode code = (*itr)->resolve();
            if (code != StatusCode::Ok)
            {
                returnCode = code;
                break;
            }
        }

        m_latests.clear();
        m_lastAdded.clear();

        return returnCode;
    }

    bool readNullObject()
    {
        for (auto itr = m_lastAdded.rbegin(); itr != m_lastAdded.rend(); itr++)
        {
            if ((*itr)->readNullObject())
            {
                return true;
            }
        }
        return false;
    }

private:
    std::unordered_map<uint16_t, std::unique_ptr<ImportStackObject>> m_latests;
    std::vector<ImportStackObject*> m_lastAdded;
};
} // namespace rive
#endif
