/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "StdAfx.h"

#include "../NetworkGridmateMarshaling.h"
#include "../NetworkGridmateDebug.h"

#include "EntityReplicaSpawnParams.h"

#include "../Compatibility/GridMateRMI.h"

#include <GridMate/Serialize/CompressionMarshal.h>

namespace GridMate
{
    static const unsigned int kMaxExtFlags = 8; // maximum number of extended flags supported
    static const unsigned int kMaxStrLen = 255; // maximum length of strings(name, className, archetype) in spawn parameters

    class CryNameMarshaler
    {
    public:
        void Marshal(WriteBuffer& wb, const string& name)
        {
            AZ_Assert(name.size() <= kMaxStrLen, "String is too long");
            wb.Write(static_cast<AZ::u8>(name.size()));
            wb.WriteRaw(name.data(), name.size());
        }

        void Unmarshal(string& name, ReadBuffer& rb)
        {
            AZ::u8 len = 0;
            rb.Read(len);
            char str[kMaxStrLen];
            rb.ReadRaw(str, len);
            name.assign(str, str + len);
        }
    };
    //-----------------------------------------------------------------------------
    //-----------------------------------------------------------------------------
    EntitySpawnParamsStorage::EntitySpawnParamsStorage()
        : m_channelId(kInvalidChannelId)
        , m_orientation(IDENTITY)
        , m_paramsFlags(0)
    {
    }

    EntitySpawnParamsStorage::EntitySpawnParamsStorage(const EntitySpawnParamsStorage& another)
        : m_id(another.m_id)
        , m_guid(another.m_guid)
        , m_entityName(another.m_entityName)
        , m_className(another.m_className)
        , m_archetypeName(another.m_archetypeName)
        , m_flags(another.m_flags)
        , m_flagsExtended(another.m_flagsExtended)
        , m_channelId(another.m_channelId)
        , m_position(another.m_position)
        , m_orientation(another.m_orientation)
        , m_scale(another.m_scale)
        , m_paramsFlags(another.m_paramsFlags)
    {

    }


    //-----------------------------------------------------------------------------
    EntitySpawnParamsStorage::EntitySpawnParamsStorage(const SEntitySpawnParams& sourceParams,
        ChannelId channelId,
        uint32 paramsFlags /*= kParamsFlag_None*/)
    {
        AZ_Assert(sourceParams.nFlagsExtended <= ((1 << kMaxExtFlags) - 1), "Too many extended flags, must be < %d", kMaxExtFlags);
        AZ_Assert(sourceParams.sName && strlen(sourceParams.sName) <= kMaxStrLen, "Name is too long, must be < %d", kMaxStrLen);
        AZ_Assert(!sourceParams.pClass || strlen(sourceParams.pClass->GetName()) <= kMaxStrLen, "Class name is too long, must be < %d", kMaxStrLen);
        AZ_Assert(!sourceParams.pArchetype || strlen(sourceParams.pArchetype->GetName()) <= kMaxStrLen, "Archetype name is too long, must be < %d", kMaxStrLen);

        m_id = sourceParams.id;
        m_guid = sourceParams.guid;
        m_entityName = sourceParams.sName;
        m_className = sourceParams.pClass ? sourceParams.pClass->GetName() : "";
        m_archetypeName = sourceParams.pArchetype ? sourceParams.pArchetype->GetName() : "";
        m_flags = sourceParams.nFlags;
        m_flagsExtended = sourceParams.nFlagsExtended;
        m_position = sourceParams.vPosition;
        m_orientation = sourceParams.qRotation;
        m_scale = sourceParams.vScale;

        m_channelId = channelId;
        m_paramsFlags = paramsFlags;

        if (sourceParams.bCreatedThroughPool)
        {
            m_paramsFlags |= kParamsFlag_CreatedThroughPool;
        }
    }

    //-----------------------------------------------------------------------------
    SEntitySpawnParams EntitySpawnParamsStorage::ToEntitySpawnParams() const
    {
        SEntitySpawnParams spawnParams;
        spawnParams.id = m_id;
        spawnParams.guid = m_guid;
        spawnParams.sName = m_entityName.c_str();

        IEntitySystem* entitySystem = GetEntitySystem();
        IEntityClassRegistry* classRegistry = entitySystem ? entitySystem->GetClassRegistry() : nullptr;

        spawnParams.pClass = classRegistry ? classRegistry->FindClass(m_className.c_str()) : nullptr;

        GM_ASSERT_TRACE(spawnParams.pClass, "Could not locate entity class \"%s\".", m_className.c_str());

        if (!m_archetypeName.empty() && m_archetypeName[ 0 ])
        {
            spawnParams.pArchetype = entitySystem->LoadEntityArchetype(m_archetypeName.c_str());
            GM_ASSERT_TRACE(spawnParams.pClass, "Could not locate entity archetype \"%s\".", m_archetypeName.c_str());
        }

        spawnParams.nFlags = m_flags;
        spawnParams.nFlagsExtended = m_flags;
        spawnParams.bCreatedThroughPool = IsCreatedThroughPool();

        spawnParams.vPosition = m_position;
        spawnParams.qRotation = m_orientation;
        spawnParams.vScale = m_scale;

        return spawnParams;
    }

    //-----------------------------------------------------------------------------
    //-----------------------------------------------------------------------------
    void EntitySpawnParamsStorage::Marshaler::Marshal(GridMate::WriteBuffer& wb, const EntitySpawnParamsStorage& s)
    {
        static_assert(kParamsFlag_Max <= BIT(4) + 1, "Using 4 bits for params flags");
        static_assert(kMarshalFlag_Max <= BIT(4) + 1, "Using 4 bits for marshal flags");

        auto flagsMarker = wb.InsertMarker<AZ::u8>();

        AZ::u8 marshalFlags = 0;

        wb.Write(s.m_id);

        if (s.m_guid)
        {
            wb.Write(s.m_guid);
            marshalFlags |= kMarshalFlag_HasGuid;
        }

        wb.Write(s.m_flags, VlqU32Marshaler());
        wb.Write(s.m_flagsExtended);
        wb.Write(s.m_channelId);

        wb.Write(s.m_position);
        if (s.m_orientation.IsUnit())
        {
            AZ::Quaternion ori(s.m_orientation.v[0], s.m_orientation.v[1], s.m_orientation.v[2], s.m_orientation.w);
            wb.Write(ori, QuatCompNormMarshaler());
            marshalFlags |= kMarshalFlag_OrientationNorm;
        }
        else
        {
            wb.Write(s.m_orientation);
        }

        if (!s.m_scale.IsEquivalent(Vec3(1.f, 1.f, 1.f)))
        {
            wb.Write(s.m_scale);
            marshalFlags |= kMarshalFlag_HasScale;
        }

        wb.Write(s.m_entityName, CryNameMarshaler());
        wb.Write(s.m_className, CryNameMarshaler());
        if (!s.m_archetypeName.empty())
        {
            wb.Write(s.m_archetypeName, CryNameMarshaler());
            marshalFlags |= kMarshalFlag_HasArchetype;
        }

        flagsMarker = (marshalFlags << 4) | (s.m_paramsFlags & 0xF); // hi 4 bits - marshal flags, low 4 bits - params flags
    }

    //-----------------------------------------------------------------------------
    void EntitySpawnParamsStorage::Marshaler::Unmarshal(EntitySpawnParamsStorage& s, GridMate::ReadBuffer& rb)
    {
        AZ::u8 flags = 0;

        rb.Read(flags);
        AZ::u8 marshalFlags = flags >> 4; // hi 4 bits - marshal flags, low 4 bits - params flags
        s.m_paramsFlags = flags & 0xF;

        rb.Read(s.m_id);

        s.m_guid = 0;
        if (marshalFlags & kMarshalFlag_HasGuid)
        {
            rb.Read(s.m_guid);
        }

        rb.Read(s.m_flags, VlqU32Marshaler());
        rb.Read(s.m_flagsExtended);
        rb.Read(s.m_channelId);

        rb.Read(s.m_position);
        if (marshalFlags & kMarshalFlag_OrientationNorm)
        {
            AZ::Quaternion ori;
            rb.Read(ori, QuatCompNormMarshaler());
            s.m_orientation = Quat(ori.GetW(), ori.GetX(), ori.GetY(), ori.GetZ());
        }
        else
        {
            rb.Read(s.m_orientation);
        }

        s.m_scale = Vec3(1.f, 1.f, 1.f);
        if (marshalFlags & kMarshalFlag_HasScale)
        {
            rb.Read(s.m_scale);
        }

        rb.Read(s.m_entityName, CryNameMarshaler());
        rb.Read(s.m_className, CryNameMarshaler());

        s.m_archetypeName.clear();
        if (marshalFlags & kMarshalFlag_HasArchetype)
        {
            rb.Read(s.m_archetypeName, CryNameMarshaler());
        }
    }

    //-----------------------------------------------------------------------------
    void EntityExtraSpawnInfo::Marshaler::Marshal(GridMate::WriteBuffer& wb, const EntityExtraSpawnInfo::Ptr& v)
    {
        if (v)
        {
            EntityExtraSpawnInfo::DataBuffer::Marshaler().Marshal(wb, v->m_buffer);
        }
        else
        {
            EntityExtraSpawnInfo::DataBuffer::Marshaler().Marshal(wb, EntityExtraSpawnInfo::DataBuffer());
        }
    }

    //-----------------------------------------------------------------------------
    void EntityExtraSpawnInfo::Marshaler::Unmarshal(EntityExtraSpawnInfo::Ptr& v, GridMate::ReadBuffer& rb)
    {
        v = new EntityExtraSpawnInfo();
        EntityExtraSpawnInfo::DataBuffer::Marshaler().Unmarshal(v->m_buffer, rb);
    }
} // namespace GridMate