//
// Copyright (c) 2008-2014 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#pragma once

#include "Model.h"
#include "Skeleton.h"
#include "StaticModel.h"

namespace Urho3D
{

class Animation;
class AnimationState;

/// Animated model component.
class URHO3D_API AnimatedModel : public StaticModel
{
    OBJECT(AnimatedModel);

    friend class AnimationState;

public:
    /// Construct.
    AnimatedModel(Context* context);
    /// Destruct.
    virtual ~AnimatedModel();
    /// Register object factory. Drawable must be registered first.
    static void RegisterObject(Context* context);

    /// Load from binary data. Return true if successful.
    virtual bool Load(Deserializer& source, bool setInstanceDefault = false);
    /// Load from XML data. Return true if successful.
    virtual bool LoadXML(const XMLElement& source, bool setInstanceDefault = false);
    /// Apply attribute changes that can not be applied immediately. Called after scene load or a network update.
    virtual void ApplyAttributes();
    /// Process octree raycast. May be called from a worker thread.
    virtual void ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results);
    /// Update before octree reinsertion. Is called from a worker thread.
    virtual void Update(const FrameInfo& frame);
    /// Calculate distance and prepare batches for rendering. May be called from worker thread(s), possibly re-entrantly.
    virtual void UpdateBatches(const FrameInfo& frame);
    /// Prepare geometry for rendering. Called from a worker thread if possible (no GPU update.)
    virtual void UpdateGeometry(const FrameInfo& frame);
    /// Return whether a geometry update is necessary, and if it can happen in a worker thread.
    virtual UpdateGeometryType GetUpdateGeometryType();
    /// Visualize the component as debug geometry.
    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);

    /// Set model.
    void SetModel(Model* model, bool createBones = true);
    /// Add an animation.
    AnimationState* AddAnimationState(Animation* animation);
    /// Remove an animation by animation pointer.
    void RemoveAnimationState(Animation* animation);
    /// Remove an animation by animation name.
    void RemoveAnimationState(const String& animationName);
    /// Remove an animation by animation name hash.
    void RemoveAnimationState(StringHash animationNameHash);
    /// Remove an animation by AnimationState pointer.
    void RemoveAnimationState(AnimationState* state);
    /// Remove an animation by index.
    void RemoveAnimationState(unsigned index);
    /// Remove all animations.
    void RemoveAllAnimationStates();
    /// Set animation LOD bias.
    void SetAnimationLodBias(float bias);
    /// Set whether to update animation and the bounding box when not visible. Recommended to enable for physically controlled models like ragdolls.
    void SetUpdateInvisible(bool enable);
    /// Set vertex morph weight by index.
    void SetMorphWeight(unsigned index, float weight);
    /// Set vertex morph weight by name.
    void SetMorphWeight(const String& name, float weight);
    /// Set vertex morph weight by name hash.
    void SetMorphWeight(StringHash nameHash, float weight);
    /// Reset all vertex morphs to zero.
    void ResetMorphWeights();

    /// Return skeleton.
    Skeleton& GetSkeleton() { return skeleton_; }
    /// Return all animation states.
    const Vector<SharedPtr<AnimationState> >& GetAnimationStates() const { return animationStates_; }
    /// Return number of animation states.
    unsigned GetNumAnimationStates() const { return animationStates_.Size(); }
    /// Return animation state by animation pointer.
    AnimationState* GetAnimationState(Animation* animation) const;
    /// Return animation state by animation name.
    AnimationState* GetAnimationState(const String& animationName) const;
    /// Return animation state by animation name hash.
    AnimationState* GetAnimationState(const StringHash animationNameHash) const;
    /// Return animation state by index.
    AnimationState* GetAnimationState(unsigned index) const;
    /// Return animation LOD bias.
    float GetAnimationLodBias() const { return animationLodBias_; }
    /// Return whether to update animation when not visible.
    bool GetUpdateInvisible() const { return updateInvisible_; }
    /// Return all vertex morphs.
    const Vector<ModelMorph>& GetMorphs() const { return morphs_; }
    /// Return all morph vertex buffers.
    const Vector<SharedPtr<VertexBuffer> >& GetMorphVertexBuffers() const { return morphVertexBuffers_; }
    /// Return number of vertex morphs.
    unsigned GetNumMorphs() const { return morphs_.Size(); }
    /// Return vertex morph weight by index.
    float GetMorphWeight(unsigned index) const;
    /// Return vertex morph weight by name.
    float GetMorphWeight(const String& name) const;
    /// Return vertex morph weight by name hash.
    float GetMorphWeight(StringHash nameHash) const;
    /// Return whether is the master (first) animated model.
    bool IsMaster() const { return isMaster_; }

    /// Set model attribute.
    void SetModelAttr(ResourceRef value);
    /// Set bones' animation enabled attribute.
    void SetBonesEnabledAttr(VariantVector value);
    /// Set animation states attribute.
    void SetAnimationStatesAttr(VariantVector value);
    /// Set morphs attribute.
    void SetMorphsAttr(const PODVector<unsigned char>& value);
    /// Return model attribute.
    ResourceRef GetModelAttr() const;
    /// Return bones' animation enabled attribute.
    VariantVector GetBonesEnabledAttr() const;
    /// Return animation states attribute.
    VariantVector GetAnimationStatesAttr() const;
    /// Return morphs attribute.
    const PODVector<unsigned char>& GetMorphsAttr() const;
    /// Return per-geometry bone mappings.
    const Vector<PODVector<unsigned> >& GetGeometryBoneMappings() const { return geometryBoneMappings_; }
    /// Return per-geometry skin matrices. If empty, uses global skinning
    const Vector<PODVector<Matrix3x4> >& GetGeometrySkinMatrices() const { return geometrySkinMatrices_; }

protected:
    /// Handle node being assigned.
    virtual void OnNodeSet(Node* node);
    /// Handle node transform being dirtied.
    virtual void OnMarkedDirty(Node* node);
    /// Recalculate the world-space bounding box.
    virtual void OnWorldBoundingBoxUpdate();

private:
    /// Assign skeleton and animation bone node references as a postprocess. Called by ApplyAttributes.
    void AssignBoneNodes();
    /// Remove (old) skeleton root bone.
    void RemoveRootBone();
    /// Mark animation and skinning to require an update.
    void MarkAnimationDirty();
    /// Mark animation and skinning to require a forced update (blending order changed.)
    void MarkAnimationOrderDirty();
    /// Mark morphs to require an update.
    void MarkMorphsDirty();
    /// Set skeleton.
    void SetSkeleton(const Skeleton& skeleton, bool createBones);
    /// Set mapping of subgeometry bone indices.
    void SetGeometryBoneMappings();
    /// Clone geometries for vertex morphing.
    void CloneGeometries();
    /// Copy morph vertices.
    void CopyMorphVertices(void* dest, void* src, unsigned vertexCount, VertexBuffer* clone, VertexBuffer* original);
    /// Recalculate animations. Called from Update().
    void UpdateAnimation(const FrameInfo& frame);
    /// Recalculate the bone bounding box.
    void UpdateBoneBoundingBox();
    /// Recalculate skinning.
    void UpdateSkinning();
    /// Reapply all vertex morphs.
    void UpdateMorphs();
    /// Apply a vertex morph.
    void ApplyMorph(VertexBuffer* buffer, void* destVertexData, unsigned morphRangeStart, const VertexBufferMorph& morph, float weight);
    /// Handle model reload finished.
    void HandleModelReloadFinished(StringHash eventType, VariantMap& eventData);

    /// Skeleton.
    Skeleton skeleton_;
    /// Morph vertex buffers.
    Vector<SharedPtr<VertexBuffer> > morphVertexBuffers_;
    /// Vertex morphs.
    Vector<ModelMorph> morphs_;
    /// Animation states.
    Vector<SharedPtr<AnimationState> > animationStates_;
    /// Skinning matrices.
    PODVector<Matrix3x4> skinMatrices_;
    /// Mapping of subgeometry bone indices, used if more bones than skinning shader can manage.
    Vector<PODVector<unsigned> > geometryBoneMappings_;
    /// Subgeometry skinning matrices, used if more bones than skinning shader can manage.
    Vector<PODVector<Matrix3x4> > geometrySkinMatrices_;
    /// Subgeometry skinning matrix pointers, if more bones than skinning shader can manage.
    Vector<PODVector<Matrix3x4*> > geometrySkinMatrixPtrs_;
    /// Bounding box calculated from bones.
    BoundingBox boneBoundingBox_;
    /// Attribute buffer.
    mutable VectorBuffer attrBuffer_;
    /// The frame number animation LOD distance was last calculated on.
    unsigned animationLodFrameNumber_;
    /// Morph vertex element mask.
    unsigned morphElementMask_;
    /// Animation LOD bias.
    float animationLodBias_;
    /// Animation LOD timer.
    float animationLodTimer_;
    /// Animation LOD distance, the minimum of all LOD view distances last frame.
    float animationLodDistance_;
    /// Update animation when invisible flag.
    bool updateInvisible_;
    /// Animation dirty flag.
    bool animationDirty_;
    /// Animation order dirty flag.
    bool animationOrderDirty_;
    /// Vertex morphs dirty flag.
    bool morphsDirty_;
    /// Skinning dirty flag.
    bool skinningDirty_;
    /// Bone bounding box dirty flag.
    bool boneBoundingBoxDirty_;
    /// Master model flag.
    bool isMaster_;
    /// Loading flag. During loading bone nodes are not created, as they will be serialized as child nodes.
    bool loading_;
    /// Bone nodes assignment pending flag.
    bool assignBonesPending_;
};

}
