/*
  libuta - a C++ widget library based on SDL (Simple Direct Layer)
  Copyright (C) 1999  Karsten Laux

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 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
  Library General Public License for more details.
  
  You should have received a copy of the GNU Library 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, SA.
*/


#ifndef _RESOURCES_H
#define _RESOURCES_H

#include "surface.h"
#include "font.h"
#include "sample.h"
#include "music.h"
#include "color.h"

#include "debug.h"

#if __GNUC__ > 2 
#include <ext/hash_map>
#else
#include <hash_map.h>
#endif

#include <sigc++/signal_system.h>
#include <sigc++/marshal.h>

namespace uta {

///
class Resource
{
 public:
  ///
  enum type_t { NONE, SURFACE, FONT, SAMPLE, MUSIC, COLOR };
  ///
  Resource() : type_(NONE), references_(0) { bind(); }; 
  ///
  void bind() { references_++; };
  ///
  void free() { references_--; if(!references_) delete this;}
  ///
  int references() { return references_; };
  ///
  type_t type() const { return type_; };
  ///
  virtual bool loadedProperly() const { return false; };
  ///
  const Surface* surface() const;
  ///
  const Font* font() const;
  ///
  const Sample* sample() const;
  ///
  const Music* music() const;
  ///
  const Color& color() const;

 protected:
  ///
  virtual ~Resource() { };
  ///
  type_t type_;
  ///
  union 
  {
    ///
    Surface* surface_;
    ///
    Font* font_;
    ///
    Sample* sample_;
    ///
    Music* music_;
    ///
    Color* color_;
  };
  ///
  int references_;
};


///load surface from file
class SurfaceResource : public Resource
{
 public:
  ///
  SurfaceResource(std::string filename_) :
    Resource() 
    {
      surface_ = new Surface;
      type_ = Resource::SURFACE;
      surface_->readFromFile(filename_);
      if(!surface_->hasAlphaChannel())
	{
	  surface_->setTransColor(black);
	  surface_->setTransparency(true);
	}
    }
  ///
  virtual bool loadedProperly() const { return !surface_->empty(); };
 protected:
  ///
  virtual ~SurfaceResource() { delete surface_; };
};

///
class FontResource : public Resource
{
 public:
  ///
  FontResource(std::string fontdescriptor_);
  ///
  virtual bool loadedProperly() const { return font_->getHeight() > 0; };

 protected:
  ///
  virtual ~FontResource() {  delete font_; };


};

///
class ColorResource : public Resource
{
 public:
  ///
  ColorResource(std::string colordesc);
  ///
  virtual bool loadedProperly() const { return true; };

 protected:
  ///
  virtual ~ColorResource() { delete color_;  };
};

///
class SampleResource : public Resource
{
 public:
  ///
  SampleResource(std::string filename_) :
    Resource() 
    {
      sample_ = new Sample;
      type_ = SAMPLE;
      loaded_ = sample_->load(filename_);
    }
  ///
  virtual bool loadedProperly() const  { return loaded_; };
 protected:
  ///
  bool loaded_;
  ///
  virtual ~SampleResource() { delete sample_; };
};


///
class MusicResource : public Resource
{
 public:
  ///
  MusicResource(std::string filename_) :
    Resource() 
    {
      music_ = new Music;
      type_ = MUSIC;
      loaded_ = music_->load(filename_);
    }
  ///
  virtual bool loadedProperly() const  { return loaded_; };
 protected:
  ///
  bool loaded_;
  ///
  virtual ~MusicResource() { delete music_; };
};

///
struct ures_eqstr
{
  bool operator()(const std::string& s1, const std::string& s2) const
    {
      return s1 == s2;
    }
};

///
struct ures_hasher
{
  size_t operator () (const std::string& s) const
    {  
      unsigned long __h = 0;
      std::string::const_iterator itr = s.begin();
      while(itr != s.end())
	__h = 5*__h + *itr++;
      
      return size_t(__h);   
    }
};

typedef __gnu_cxx::hash_map<std::string, Resource*, ures_hasher, ures_eqstr> resources_map;
typedef __gnu_cxx::hash_map<std::string, std::string, ures_hasher, ures_eqstr> files_map;

///
class Resources : public SigC::Object
{
 public:
  ///
  Resources();
  ///
  ~Resources();
  ///
  void registerSurface(std::string resname, std::string filename);
  ///
  void registerFont(std::string resname, std::string fontdescriptor);
  ///
  void registerSample(std::string resname, std::string filename);
  ///
  void registerMusic(std::string resname, std::string filename);
  ///
  void registerColor(std::string resname, std::string colordesc);

  ///
  bool isAvailable(const std::string& resname);
  /// 
  const Resource* get(std::string resname_);
  
  ///
  struct StopOnValid
    {
      typedef Resource* InType;
      typedef Resource* OutType;
      OutType  return_value_;

      OutType value() { return return_value_; }
      static OutType default_value() { return NULL; }
      bool marshal(const InType& val) 
      { 
	if(val) 
	  {
	    return_value_ = val; 
	    return true; 
	  }
	return false;
      }

      StopOnValid() : return_value_(NULL) {}
    };

  /* unknown resource.
     Signals gets emitted when an unknown resource is requested.
     An application may return a valid Resource for the given
     resource name, which in return Resources will store for
     further access.
     If the application cannot return a valid Resource it is meant
     to return NULL.
     StopOnValid is a marshaller which forces the signal to return
     as soon as a first non-NULL value is returned from one slot
  */
  SigC::Signal1<Resource*, std::string, StopOnValid> unknownResource;

  ///
  bool unregister(std::string resname);

  ///
  const std::vector<std::string>& surfaceList() { return surfaces; };
  ///
  const std::vector<std::string>& fontList() { return fonts; };
  ///
  const std::vector<std::string>& sampleList() { return samples; };
  ///
  const std::vector<std::string>& songList() { return songs; };  
  ///
  const std::vector<std::string>& colorList() { return colors; };  
  ///
  void unregisterAll();

  ///
  static Resources* instance;

 protected:
  ///
  std::vector<std::string> surfaces;
  ///
  std::vector<std::string> fonts;
  ///
  std::vector<std::string> samples;
  ///
  std::vector<std::string> songs;
  ///
  std::vector<std::string> colors;
  ///
  Resource* check(std::string);
  ///
  bool create(std::string resname, Resource* res);
  ///
  resources_map resources_;
  ///
  files_map loadedFiles_;

  Resource* default_;
};

} // namespace

/** Helper class.
    It creates a application wide single instance of Resources
    and sets Resources::instance.
*/
class ResourcesInit
{
 private:
  ///
  static unsigned int count_;
 public:
  ///
  ResourcesInit();
  ///
  ~ResourcesInit();
};

///every module including this header will own a ResourcesInit instance :)
static ResourcesInit resourcesInit;



#define FONT_RES(name) uta::Resources::instance->get(name)->font()
#define SAMPLE_RES(name) uta::Resources::instance->get(name)->sample()
#define MUSIC_RES(name) uta::Resources::instance->get(name)->music()
#define SURFACE_RES(name) uta::Resources::instance->get(name)->surface()
#define COLOR_RES(name) uta::Resources::instance->get(name)->color()
#define RES_AVAILABLE(name) uta::Resources::instance->isAvailable(name)

#define REG_FONT_RES(name, spec) \
uta::Resources::instance->registerFont(name,spec)
#define REG_SAMPLE_RES(name, spec) \
uta::Resources::instance->registerSample(name,spec)
#define REG_MUSIC_RES(name, spec) \
uta::Resources::instance->registerMusic(name,spec)
#define REG_SURFACE_RES(name, spec) \
uta::Resources::instance->registerSurface(name,spec)
#define REG_COLOR_RES(name, spec) \
uta::Resources::instance->registerColor(name,spec)





#endif
