/**
 *  GraphTFT plugin for the Video Disk Recorder 
 * 
 *  cache.h - A plugin for the Video Disk Recorder
 *
 *  (c) 2004 Lars Tegeler, Sascha Volkenandt  
 *
 * This code is distributed under the terms and conditions of the
 * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
 *
 * $Id: cache.h,v 1.3 2007/06/01 15:35:14 root Exp $
 *
 **/

#ifndef __GTFT_CACHE_H
#define __GTFT_CACHE_H

#include <map>
#include "common.h"
#include <sys/time.h>

// template class generic cache

template<class K,class D>

class DataCache
{
   private:
      struct Item
      {
         K      _key;
         D      _data;
         time_t _lastUsed;

         Item  *_next;
         Item  *_prev;

         Item() { _next = _prev = NULL; }
      };

      typedef std::map <K,Item*> DataMap;

      DataMap _items;
      int     _maxItems;
      Item   *_first;
      Item   *_last;

      void Unlink(Item *item);
      void Update(Item *item);
      void Delete(Item *item);
      void Delete(K &key, D &data);

   public:
      DataCache(int maxItems);
      ~DataCache();

      bool Contains(const K &key);
      D &operator[](const K &key);
      void Clear();
};

template<class K,class D>

inline void DataCache<K,D>::Unlink(Item *item)
{
   if (item == _first)
   {
      _first = item->_next;
      if (_first)
         _first->_prev = NULL;
      else
         _last = NULL;
   }
   else if (item == _last)
   {
      _last = item->_prev;
      _last->_next = NULL;
   }
   else
   {
      item->_prev->_next = item->_next;
      item->_next->_prev = item->_prev;
   }
}

template<class K,class D>

inline void DataCache<K,D>::Update(Item *item)
{
   struct timeval t;

   item->_lastUsed = 0;

   if (gettimeofday(&t, NULL) == 0)
      item->_lastUsed = (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000;

   if (item->_next != NULL || item->_prev != NULL)
      Unlink(item);

   item->_next = NULL;
   item->_prev = _last;

   if (_last)
      _last->_next = item;

   _last = item;

   if (!_first)
      _first = item;

   while ((int)_items.size() > _maxItems)
   {
      Item *aged = _first;
      _items.erase(aged->_key);
      Unlink(aged);
      Delete(aged);
   }
}

template<class K,class D>

inline void DataCache<K,D>::Delete(Item *item)
{
   Delete(item->_key, item->_data);
   delete item;
}

template<class K,class D>
inline void DataCache<K,D>::Delete(K &key, D &Data)
{}

template<class K,class D>
inline bool DataCache<K,D>::Contains(const K &key)
{
   return (_items.find(key) != _items.end());
}

template<class K,class D>
DataCache<K,D>::DataCache(int maxItems)
{
   _maxItems = maxItems;
   _first = _last = NULL;
}

template<class K,class D>
DataCache<K,D>::~DataCache()
{}

template<class K,class D>
D &DataCache<K,D>::operator[](const K &key)
{
   Item *item;
   if (Contains(key))
   {
      item = _items[key];
   }
   else
   {
      item = new Item;
      item->_key = key;
      _items[key] = item;
   }
   Update(item);
   return item->_data;
}

template<class K,class D>
void DataCache<K,D>::Clear()
{
   Item *item = _first;
   while (item)
   {
      Item *next = item->_next;
      Delete(item);
      item = next;
   }
   _first = _last = NULL;
   _items.clear();
}

#include <directfb.h>
#include <string>
#include <memory>

// instantiation of image cache

typedef DataCache<std::string,IDirectFBSurface*> ImageCache;

template<>
inline void ImageCache::Delete(std::string &key, IDirectFBSurface *&data)
{
   data->Release(data);
}

extern ImageCache imageCache;

// instantiation of font cache

typedef std::pair<std::string,int> FontFaceSize;
typedef DataCache<FontFaceSize,IDirectFBFont*> FontCache;

template<>
inline void FontCache::Delete(FontFaceSize &key, IDirectFBFont *&data)
{
   data->Release(data);
}

extern FontCache fontCache;

#endif // __GTFT_CACHE_H
