/* -*- c++ -*-
    Copyright (C) 2003 <ryu@gpul.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/*
 *
 * Copyright (C) 2004 Mekensleep
 *
 *	Mekensleep
 *	24 rue vieille du temple
 *	75004 Paris
 *       licensing@mekensleep.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * Authors:
 *  Cedric PINSON <cpinson@freesheep.org>
 *  Loic Dachary <loic@gnu.org>
 *
 */
#ifndef __OSGCAL__MODEL_H__
#define __OSGCAL__MODEL_H__

#include <osg/Geode>
#include <osg/VertexProgram>

#include <cal3d/cal3d.h>

#include <osgCal/CoreModel>
#include <osgCal/Export>

namespace osgCal {

class OSGCAL_EXPORT Model: public osg::Geode {
public:
  typedef CoreModel::Textures2D Textures2D;
  typedef std::vector<osg::ref_ptr<osg::Drawable> > Drawables;
  typedef std::map<int,Drawables> CoreMeshId2Drawables;

  META_Object(osgCal, Model);

  Model();
  Model(const Model&, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);

protected:
  virtual ~Model();
public:

	/**
	 * @brief Create model from a templated CoreModel. Remember to set CoreModel with setCoreModel function.
	 */
  bool create(void);
  void update(void);

	/**
	 * @brief Retrieves CoreModel
	 */	
  CoreModel *getCoreModel() { return _coreModel.get(); }

	/**
	 * @brief Sets CoreModel
	 * @return \c false if case of error, otherwise \c true.
	 */
  bool setCoreModel(CoreModel* coreModel);
  
  /**
	 * @brief Retrieves the Cal3d CalCoreModel.
	 */
  CalCoreModel *getCalCoreModel(void) { return _coreModel->getCalCoreModel(); }
  
  /**
	 * @brief Retrieves the Cal3d CalModel associated with the current osgCalModel instance.
	 */
  CalModel *getCalModel(void) { return &_calModel; }

	/**
	 * @brief Retrieves the 2D textures given a material name
	 * @param materialName a valid materialName
	 *
	 * @remark Each material has map(s) associated, i.e. texture(s).
	 */
  Textures2D* getTextures2D(const std::string& materialName);

	/**
	 * @brief Retrieves the 2D textures given a material id
	 * @param materialId a valid material Id
	 *
	 * @remark Each material has map(s) associated, i.e. texture(s).
	 */
  Textures2D* getTextures2D(int coreMaterialId);
  
  /**
	 * @brief Retrieves the drawables given a material name
	 * @param materialName a valid materialName
 	 *
	 * @remark Each material has map(s) associated, i.e. texture(s). 
	 * The drawables are osg objects that can be rendered. 
	 */
  Drawables* getDrawables(const std::string& materialName);

  /**
	 * @brief Retrieves the drawables given a material id
	 * @param materialId a valid materialId
 	 *
	 * @remark Each material has map(s) associated, i.e. texture(s). 
	 * The drawables are osg objects that can be rendered. 
	 */  
	 Drawables* getDrawables(int coreMaterialId);

  bool hasDrawables(int coreMeshId);

	 /**
	 * Set private textures of current model.
	 * 
	 * @param privateTextures a container of textures names
	 *
	 * @return \c true in case of succes.
	 * @remark texture names must correspond to material names. In fact the user
	 * will with this function set the textures which are not shared with other 
	 * model instances gotten from the templated CoreModel. If CoreModel or the 
	 * material associated with a texture name doesn't exist \c false is returned.
	 */
  bool setPrivateTextures(const std::vector<std::string>& privateTextures);
  
	 /**
	 * @brief Acivates meshes.
	 *
	 * @param meshName name of the mesh to active
	 *
	 * @remark By default all the meshes are set to active.	 
	 */   
  bool setActiveMesh(const std::string& meshName);
  bool setActiveMeshes(const std::vector<int>& activeMeshes) { _activeMeshes = activeMeshes; return true; }
  const std::vector<int>& getActiveMeshes(void) const { return _activeMeshes; }
  
  
  bool bindMaterials(const std::string& meshName, const std::vector<std::string>& materials);

  bool setInvisibleMesh(const std::string& meshName);
  bool setInvisibleMeshes(const std::vector<int>& invisibleMeshes) { _invisibleMeshes = invisibleMeshes; return true; }
  const std::vector<int>& getInvisibleMeshes(void) const { return _invisibleMeshes; }

  bool setCollisionMesh(const std::string& meshName);
  bool setCollisionMeshNames(const std::vector<std::string>& meshNames);
  const std::vector<std::string>& getCollisionMeshNames(void) const { return _collisionMeshNames; }
  bool setCollisionMeshes(const std::vector<int>& collisionMeshes) { _collisionMeshes = collisionMeshes; return true; }
  const std::vector<int>& getCollisionMeshes(void) const { return _collisionMeshes; }

  bool setUseVertexProgram(bool useVertexProgram);
  bool getUseVertexProgram(void) { return _useVertexProgram; }

  bool setUseColorOrTexture(bool useColorOrTexture) { _useColorOrTexture = useColorOrTexture; return true; }
  bool getUseColorOrTexture(void) { return _useColorOrTexture; }

private:
  bool createSoftware(void);
  bool createSubMeshSoftware(int meshId, int coreMeshId);
  bool createHardware(void);
  bool setupMaterial(osg::Drawable* drawable, CalSubmesh* calSubmesh);
  void invertUVs(int coreMeshId);

  bool isPrivateTexture(int coreMaterialId) { return find(_privateTextures, coreMaterialId); }
  bool isInvisibleMesh(int coreMeshId) { return find(_invisibleMeshes, coreMeshId); }
  bool isCollisionMesh(int coreMeshId) { return find(_collisionMeshes, coreMeshId); }
  bool find(const std::vector<int>& vector, int item);

  CalHardwareModel mHardwareModel;
  bool _useVertexProgram;
  bool _useColorOrTexture;
  unsigned int mVbo[6];

  osg::ref_ptr<CoreModel> _coreModel;
  std::vector<int> _privateTextures;
  std::vector<int> _activeMeshes;
  std::vector<int> _invisibleMeshes;
  std::vector<int> _collisionMeshes;
  std::vector<std::string> _collisionMeshNames;
  CoreMeshId2Drawables _coreMeshId2Drawables;
  CoreModel::CoreMaterialId2Textures2D _coreMaterialId2PrivateTextures;
  // Cannot be a base class of Model because rtti disabled in cal3d
  CalModel _calModel;
  osg::ref_ptr<osg::VertexProgram> _vp;
  osg::BoundingBox _bbox;
};

}; // namespace osgCal

#endif
