#include "InfoStringLoaderPhysPresetIW5.h"

#include "Game/IW5/IW5.h"
#include "Game/IW5/InfoString/InfoStringToStructConverter.h"
#include "Game/IW5/PhysPreset/PhysPresetFieldsIW5.h"
#include "Utils/Logging/Log.h"

#include <cassert>
#include <limits>

using namespace IW5;

namespace
{
    class InfoStringToPhysPresetConverter final : public InfoStringToStructConverter
    {
    public:
        InfoStringToPhysPresetConverter(const InfoString& infoString,
                                        PhysPresetInfo& physPreset,
                                        ZoneScriptStrings& zoneScriptStrings,
                                        MemoryManager& memory,
                                        AssetCreationContext& context,
                                        AssetRegistration<AssetPhysPreset>& registration,
                                        const cspField_t* fields,
                                        const size_t fieldCount)
            : InfoStringToStructConverter(infoString, &physPreset, zoneScriptStrings, memory, context, registration, fields, fieldCount)
        {
        }

    protected:
        bool ConvertExtensionField(const cspField_t& field, const std::string& value) override
        {
            switch (static_cast<physPresetFieldType_t>(field.iFieldType))
            {
            case PPFT_SCALING:
                return ConvertEnumInt(field.szName, value, field.iOffset, szPhysPresetScalingNames, std::extent_v<decltype(szPhysPresetScalingNames)>);

            default:
                assert(false);
                return false;
            }
        }
    };

    void CopyFromPhysPresetInfo(const PhysPresetInfo& physPresetInfo, PhysPreset& physPreset)
    {
        physPreset.mass = physPresetInfo.mass;
        physPreset.bounce = physPresetInfo.bounce;

        if (physPresetInfo.isFrictionInfinity != 0)
            physPreset.friction = std::numeric_limits<float>::max();
        else
            physPreset.friction = physPresetInfo.friction;

        physPreset.bulletForceScale = physPresetInfo.bulletForceScale;
        physPreset.explosiveForceScale = physPresetInfo.explosiveForceScale;
        physPreset.sndAliasPrefix = physPresetInfo.sndAliasPrefix;
        physPreset.piecesSpreadFraction = physPresetInfo.piecesSpreadFraction;
        physPreset.piecesUpwardVelocity = physPresetInfo.piecesUpwardVelocity;
        physPreset.minMomentum = physPresetInfo.minMomentum;
        physPreset.maxMomentum = physPresetInfo.maxMomentum;
        physPreset.minPitch = physPresetInfo.minPitch;
        physPreset.maxPitch = physPresetInfo.maxPitch;
        physPreset.volumeType = physPresetInfo.volumeType;
        physPreset.pitchType = physPresetInfo.pitchType;
        physPreset.tempDefaultToCylinder = physPresetInfo.tempDefaultToCylinder != 0;
        physPreset.perSurfaceSndAlias = physPresetInfo.perSurfaceSndAlias != 0;
    }
} // namespace

namespace phys_preset
{
    InfoStringLoaderIW5::InfoStringLoaderIW5(MemoryManager& memory, Zone& zone)
        : m_memory(memory),
          m_zone(zone)
    {
    }

    AssetCreationResult InfoStringLoaderIW5::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context)
    {
        auto* physPreset = m_memory.Alloc<PhysPreset>();
        physPreset->name = m_memory.Dup(assetName.c_str());

        AssetRegistration<AssetPhysPreset> registration(assetName, physPreset);

        PhysPresetInfo physPresetInfo{};
        InfoStringToPhysPresetConverter converter(infoString,
                                                  physPresetInfo,
                                                  m_zone.m_script_strings,
                                                  m_memory,
                                                  context,
                                                  registration,
                                                  phys_preset_fields,
                                                  std::extent_v<decltype(phys_preset_fields)>);
        if (!converter.Convert())
        {
            con::error("Failed to parse phys preset: \"{}\"", assetName);
            return AssetCreationResult::Failure();
        }

        CopyFromPhysPresetInfo(physPresetInfo, *physPreset);

        return AssetCreationResult::Success(context.AddAsset(std::move(registration)));
    }
} // namespace phys_preset
