<?php

  /**
   * Data object pool
   * 
   * Static class that's used to cache object instancess acorss the application
   * 
   * @package angie.library.database
   */
  final class DataObjectPool {
    
    /**
     * Cache all objects in this variable, indexed by type and ID
     *
     * @var unknown_type
     */
    static $pool = array();
    
    /**
     * Return object by type -> id pair
     * 
     * @param string $type
     * @param integer $id
     * @param Closure $alternative
     * @param boolean $force_reload
     * @return DataObject
     */
    static function &get($type, $id, $alternative = null, $force_reload = false) {
      if($id) {
        if(isset(self::$pool[$type]) && isset(self::$pool[$type][$id])) {
          return self::$pool[$type][$id];
        } else {
          $object = null;
          
          if(class_exists($type, true)) {
            $object = new $type($id);
            
            if($object instanceof DataObject && $object->isLoaded()) {
              self::$pool[$type][$id] = $object;
              
              return self::$pool[$type][$id];
            } // if
          } // if
        } // if
      } // if
      
      if($alternative instanceof Closure) {
        $result = $alternative();
      } else {
        $result = $alternative;
      } // if
      
      return $result;
    } // get
    
    /**
     * set object cache
     * 
     * @param DataObject $object
     */
    static function set($object) {
    	self::$pool[get_class($object)][$object->getId()] = $object;
    } // set
    
    /**
     * Remove object from the pool
     * 
     * @param string $type
     * @param integer $id
     */
    static function forget($type, $id) {
      if(isset(self::$pool[$type]) && isset(self::$pool[$type][$id])) {
        unset(self::$pool[$type][$id]);
      } // if
    } // forget
    
    /**
     * Return objects by type -> ids map
     * 
     * @param array $map
     * @return array
     */
    static function getByTypeIdsMap($map) {
      if(is_foreachable($map)) {
        $result = array();
        
        foreach($map as $type => $ids) {
          $result[$type] = self::getByIds($type, $ids);
          
          if(empty($result[$type])) {
            unset($result[$type]);
          } // if
          
//          $result[$type] = array();
//          
//          foreach($ids as $id) {
//            $object = self::get($type, $id);
//            
//            if($object) {
//              $result[$type][$id] = $object;
//            } // if
//          } // foreach
        } // foreach
        
        return $result;
      } else {
        return null;
      } // if
    } // getByTypeIdsMap
    
    /**
     * Registered type loaders
     *
     * @var array
     */
    static $type_loaders = array();
    
    /**
     * Register type loader
     * 
     * $type can be a signle type or an array of types
     * 
     * $callback can be a closure or callback array
     * 
     * @param string $type
     * @param mixed $callback
     */
    static function registerTypeLoader($type, $callback) {
      if(is_array($type)) {
        foreach($type as $v) {
          self::$type_loaders[$v] = $callback;
        } // foreach
      } else {
        self::$type_loaders[$type] = $callback;
      } // if
    } // registerTypeLoader
    
    static function getByIds($type, $ids) {
      $type_loader = isset(self::$type_loaders[$type]) && self::$type_loaders[$type] instanceof Closure ? self::$type_loaders[$type] : null;
      
      if($type_loader) {
        $loader_result = $type_loader($ids);
        
        if($loader_result) {
          $objects = array();
          
          foreach($loader_result as $v) {
            $objects[$v->getId()] = $v;
          } // foreach
        } else {
          $objects = null;
        } // if
      } else {
        $objects = array();
        
        foreach($ids as $id) {
          $object = self::get($type, $id);

          if($object) {
            $objects[$id] = $object;
          } // if
        } // foreach
      } // if
      
      return empty($objects) ? null : $objects;
    } // getByIds
    
  }