#ifndef CNOID_BODY_SCENE_BODY_H
#define CNOID_BODY_SCENE_BODY_H

#include "Body.h"
#include "SceneDevice.h"
#include <cnoid/SceneGraph>
#include "exportdecl.h"

namespace cnoid {

class SceneBody;

class CNOID_EXPORT SceneLink : public SgPosTransform
{
public:
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW
        
    SceneLink(SceneBody* sceneBody, Link* link);
    SceneLink(const SceneLink& org) = delete;
    ~SceneLink();

    SceneBody* sceneBody(){ return sceneBody_; }
    const SceneBody* sceneBody() const { return sceneBody_; }

    Link* link() { return link_; }
    const Link* link() const { return link_; }

    const SgGroup* shapeGroup() const { return topShapeGroup; }
    const SgNode* visualShape() const;
    SgNode* visualShape();
    const SgNode* collisionShape() const;
    SgNode* collisionShape();

    void insertEffectGroup(SgGroup* effect, SgUpdateRef update = nullptr);
    void removeEffectGroup(SgGroup* effect, SgUpdateRef update = nullptr);
    
    virtual void setVisible(bool on);
    bool isVisible() const;

    void addSceneDevice(SceneDevice* sdev);
    SceneDevice* getSceneDevice(Device* device);

    float transparency() const;
    void setTransparency(float transparency, SgUpdateRef update = nullptr);
    //! \deprecated. Use setTransparency.
    void makeTransparent(float transparency);
    
    class Impl;

protected:
    SceneLink(int classId, SceneBody* sceneBody, Link* link);

private:
    void initialize(SceneBody* sceneBody, Link* link);

    Link* link_;
    SceneBody* sceneBody_;
    SgGroupPtr topShapeGroup;
    Impl* impl;
    
    friend class SceneBody;
};

typedef ref_ptr<SceneLink> SceneLinkPtr;


class CNOID_EXPORT SceneBody : public SgPosTransform
{
public:
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW

    SceneBody();
    SceneBody(Body* body);
    SceneBody(const SceneBody& org) = delete;
    virtual ~SceneBody();

    Body* body() { return body_; }
    const Body* body() const { return body_; }

    void cloneShapes(CloneMap& cloneMap);

    int numSceneLinks() const { return sceneLinks_.size(); }
    SceneLink* sceneLink(int index) { return sceneLinks_[index]; }
    const SceneLink* sceneLink(int index) const { return sceneLinks_[index]; }

    void updateLinkPositions(SgUpdateRef update = nullptr);

    SceneDevice* getSceneDevice(Device* device);
    void setSceneDeviceUpdateConnection(bool on);
    void updateSceneDevices(double time);

    void setTransparency(float transparency);

    void insertEffectGroup(SgGroup* effect, SgUpdateRef update = nullptr);
    void removeEffectGroup(SgGroup* effect, SgUpdateRef update = nullptr);

    virtual void updateSceneModel();
    void updateSceneDeviceModels(bool doNotify);

    [[deprecated("Use setTransparency.")]]
    void makeTransparent(float transparency);
    [[deprecated("Use setTransparency.")]]
    void makeTransparent(float transparency, CloneMap& cloneMap);

protected:
    void setBody(Body* body, std::function<SceneLink*(Link*)> sceneLinkFactory);

private:
    BodyPtr body_;
    std::vector<SceneLinkPtr> sceneLinks_;

    class Impl;
    Impl* impl;

    friend class SceneLink;
};
            
typedef ref_ptr<SceneBody> SceneBodyPtr;

}
    
#endif
