//This file is part of Photon (http://photon.sourceforge.net)
//Copyright (C) 2004-2005 James Turk
//
// Author:
//  James Turk (jpt2433@rit.edu)
//
// Version:
//  $Id: ResourceManager.hpp,v 1.10 2005/07/19 01:31:37 cozman Exp $

#ifndef PHOTON_RESOURCEMANAGER_HPP
#define PHOTON_RESOURCEMANAGER_HPP

#include <map>
#include <string>

#include <boost/utility.hpp>

#include "types.hpp"
#include "exceptions.hpp"

namespace photon
{

class Resource
{
public:
    Resource() : 
        refCount(0)
    { }
    
public:
    uint refCount;
};

class ResourceDescriptor
{
public:
    ResourceDescriptor() { }
    ResourceDescriptor(const std::string& p) :
        path(p)
    { }
    
public:
    std::string path;
};

// Class: ResourceManager
//  Templated base class for managing resources like textures and music.
// 
//  All ResourceManager work is done behind the scenes, it and all classes 
//  derived from it are therefore left without public documentation.
template<class resT, class ResDescT_=ResourceDescriptor>
class ResourceManager : public boost::noncopyable
{
public:
    typedef ResDescT_ ResDescT;
    
public:
    ResourceManager();

    virtual ~ResourceManager();

    void newResource(const std::string& name, const ResDescT& path);
    
    resT& getResource(const std::string& name);
    
    void delRef(const std::string& name);
    void cleanUp();
    
private:
    virtual void loadResourceData(resT &res, const ResDescT& path)=0;
    virtual void freeResourceData(resT &res)=0;

    void deleteResource(const std::string& name);
    
public:
    void printReport(std::ostream& os);

private:
    typedef std::map<std::string,resT> MapT;
    typedef typename MapT::iterator MapIterator;
    MapT resourceMap_;
};

// implementation (damn you templor, cruel god of templates!)

template<class resT, class ResDescT>
ResourceManager<resT, ResDescT>::ResourceManager()
{ }

template<class resT, class ResDescT>
ResourceManager<resT, ResDescT>::~ResourceManager()
{ }

template<class resT, class ResDescT>
void ResourceManager<resT, ResDescT>::newResource(const std::string& name, 
                                                    const ResDescT& desc)
{
    resT resource;

    try
    {
        // attempt to load
        loadResourceData(resource, desc);
    }
    catch(ResourceException& e)
    {
        // rethrow any exceptions with specific information 
        throw ResourceException("Could not load " + desc.path + " as " + name +
            ": " + e.getDesc());
    }

    resourceMap_[name] = resource;     // add the resource to resourceMap
}

template<class resT, class ResDescT>
resT& ResourceManager<resT, ResDescT>::getResource(const std::string& name)
{
    MapIterator resource( resourceMap_.find(name) );    // fetch resource
    
    if(resource != resourceMap_.end())
    {
        // increment the refCount and return the resource for use
        ++resource->second.refCount;
        return resource->second;
    }
    else
    {
        throw ResourceException("No resource named \"" + name + "\" exists.");
    }
}

template<class resT, class ResDescT>
void ResourceManager<resT, ResDescT>::deleteResource(const std::string& name)
{
    MapIterator resource( resourceMap_.find(name) );    // fetch resource
    
    // if the resource was found
    if(resource != resourceMap_.end())
    {
        // free resource and remove it from the map
        freeResourceData(resource->second);
        resourceMap_.erase(name);
    }
}

template<class resT, class ResDescT>
void ResourceManager<resT, ResDescT>::delRef(const std::string& name)
{
    MapIterator resource( resourceMap_.find(name) );    // fetch resource
    
    // if the resource was found
    if(resource != resourceMap_.end())
    {
        if(--resource->second.refCount <= 0)
        {
            deleteResource(name);
        }
    }
}

template<class resT, class ResDescT>
void ResourceManager<resT, ResDescT>::cleanUp()
{
    // delete resources, until none are left
    while(!resourceMap_.empty())
    {
        deleteResource(resourceMap_.begin()->first);
    }
}

template<class resT, class ResDescT>
void ResourceManager<resT, ResDescT>::printReport(std::ostream& os)
{
    // go through all resources and output debug info (name/path/refCount)
    for(MapIterator i = resourceMap_.begin(); i != resourceMap_.end(); ++i)
    {
        os << i->second.name << "\t" << i->second.path << "\t" 
            << i->second.refCount << "\n";
    }
}


}

#endif  //PHOTON_RESOURCEMANAGER_HPP
