<?php

  /**
   * Project assets manager class
   *
   * @package activeCollab.modules.files
   * @subpackage models
   */
  class ProjectAssets extends ProjectObjects {
    
    // Sharing contexts
    const BOOKMAKRS_SHARING_CONTEXT = 'bookmarks';
    const FILES_SHARING_CONTEXT = 'files';
    const YOUTUBE_SHARING_CONTEXT = 'youtube';
    const DOCUMENTS_SHARING_CONTEXT = 'text-documents';
    
    // ---------------------------------------------------
    //  Permissions
    // ---------------------------------------------------
    
    /**
     * Returns true if $user can access assets section in $project
     * 
     * @param IUser $user
     * @param Project $project
     * @param boolean $check_tab
     * @return boolean
     */
    static function canAccess(IUser $user, Project $project, $check_tab = true) {
      return ProjectObjects::canAccess($user, $project, 'file', ($check_tab ? 'files' : null));
    } // canAccess
    
    /**
     * Returns true if $user can add assets to $project
     * 
     * @param IUser $user
     * @param Project $project
     * @param boolean $check_tab
     * @return boolean
     */
    static function canAdd(IUser $user, Project $project, $check_tab = true) {
      return ProjectObjects::canAdd($user, $project, 'file', ($check_tab ? 'files' : null));
    } // canAdd
    
    /**
     * Returns true if $user can manage assets in $project
     * 
     * @param IUser $user
     * @param Project $project
     * @param boolean $check_tab
     * @return boolean
     */
    static function canManage(IUser $user, Project $project, $check_tab = true) {
      return ProjectObjects::canManage($user, $project, 'file', ($check_tab ? 'files' : null));
    } // canManage
    
    // ---------------------------------------------------
    //  Utils
    // ---------------------------------------------------
  	
  	/**
  	 * Cached array of detailed types
  	 * 
  	 * @var array
  	 */
  	private static $asset_types_detailed = false;
    
    /**
     * Cached array of asset types
     *
     * @var array
     */
    private static $asset_types = false;
    
    /**
     * Get asset types with details
     * 
     * @return array
     */
		static function getAssetTypesDetailed() {
			if (self::$asset_types_detailed === false) {
	      $asset_types = array(
	      	'Bookmark' => array(
	      		'title' => lang('Bookmarks'),
	      		'icon' => AngieApplication::getImageUrl('icons/16x16/bookmark.png', FILES_MODULE)
	      	),
	      	'File' => array(
	      		'title' => lang('Files'),
	      		'icon' => AngieApplication::getImageUrl('file-types/16x16/default.png', ENVIRONMENT_FRAMEWORK)
	      	),
	      	'TextDocument' => array(
	      		'title' => lang('Text Documents'),
	      		'icon' => AngieApplication::getImageUrl('icons/16x16/text-document.png', FILES_MODULE)
	      	),
	      	'YouTubeVideo' => array(
	      		'title' => lang('YouTube Videos'),
	      		'icon' => AngieApplication::getImageUrl('icons/16x16/youtube-video.png', FILES_MODULE)
	      	)
	     	);
	      EventsManager::trigger('on_asset_types', array(&$asset_types));
	      self::$asset_types_detailed = $asset_types;
			} // if
			
			return self::$asset_types_detailed;
		} // getAssetTypesDetailed
    
    /**
     * Return asset type name map
     *
     * @return array
     */
    static function getTypeNameMap() {
			$map = array();
			
			foreach (ProjectAssets::getAssetTypesDetailed() as $type => $details) {
				$map[$type] = array_var($details, 'title');
			} // foreach
			
			return $map;
    } // getTypeNameMap
    
    /**
     * Get asset types
     * 
     * @return array;
     */
    static function getAssetTypes() {
			if (self::$asset_types === false) {
				$type_map = ProjectAssets::getAssetTypesDetailed();
				self::$asset_types = array_keys($type_map);
			} // if
			return self::$asset_types;
    } // getAssetTypes
    
    /**
     * Make sure that $name is unique in $project
     * 
     * This function will return unique name for a given project, based on the 
     * name provided as first parameter. If original name is already unique, it 
     * will not be modified
     * 
     * @param string $name
     * @param Project $project
     * @return string
     */
    static function checkNameUniqueness($name, $project) {
      $project_id = $project instanceof Project ? $project->getId() : $project;
      $asset_types = self::getAssetTypes();
      
      $counter = 1;
      
      $check_name = $name;
      while(DB::executeFirstCell('SELECT COUNT(id) FROM ' . TABLE_PREFIX . 'project_objects WHERE project_id = ? AND name = ? AND type IN (?) AND state >= ?', $project_id, $check_name, $asset_types, STATE_TRASHED) > 0) {
        $dot_pos = strpos_utf($name, '.');
        
        if($dot_pos === false || $dot_pos == 0) {
          $check_name = $name . '-' . $counter++;
        } else {
          $check_name = substr_utf($name, 0, $dot_pos) . '-' . ($counter++) . substr_utf($name, $dot_pos);
        } // if
      } // while
      
      return $check_name;
    } // checkNameUniqueness
    
    /**
     * Return assets by project
     *
     * @param Project $project
     * @param integer $min_state
     * @param integer $min_visibility
     * @return DBResult
     */
    static function findByProject(Project $project, $min_state = STATE_VISIBLE, $min_visibility = VISIBILITY_NORMAL) {
      return ProjectAssets::findByTypeAndProject($project, self::getAssetTypes(), $min_state, $min_visibility);
    } // findByProject
    
    /**
     * Return assets by milestone
     *
     * @param Milestone $milestone
     * @param integer $min_state
     * @param integer $min_visibility
     * @return array
     */
    static function findByMilestone(Milestone $milestone, $min_state = STATE_VISIBLE, $min_visibility = VISIBILITY_NORMAL) {
      return ProjectAssets::find(array(
        'conditions' => array('milestone_id = ? AND project_id = ? AND type IN (?) AND state >= ? AND visibility >= ?', $milestone->getId(), $milestone->getProjectId(), self::getAssetTypes(), $min_state, $min_visibility), // Milestone ID + Project ID (integrity issue from activeCollab 2)
        'order' => 'created_on DESC',
      ));
    } // findByMilestone
    
    /**
     * Return assets from a given category
     * 
     * @param AssetCategory $category
     * @param integer $min_state
     * @param integer $min_visibility
     * @return DBResult
     */
    static function findByCategory(AssetCategory $category, $min_state = STATE_VISIBLE, $min_visibility = VISIBILITY_NORMAL) {
      return ProjectAssets::find(array(
        'conditions' => array('category_id = ? AND type IN (?) AND state >= ? AND visibility >= ?', $category->getId(), self::getAssetTypes(), $min_state, $min_visibility),
        'order' => 'created_on DESC',
      ));
    } // findByCategory
    
    /**
     * Return number of assets from a given category
     * 
     * @param AssetCategory $category
     * @param integer $min_state
     * @param integer $min_visibility
     * @return integer
     */
    static function countByCategory(AssetCategory $category, $min_state = STATE_VISIBLE, $min_visibility = VISIBILITY_NORMAL) {
      return ProjectAssets::count(array('category_id = ? AND type IN (?) AND state >= ? AND visibility >= ?', $category->getId(), self::getAssetTypes(), $min_state, $min_visibility));
    } // countByCategory
    
    /**
     * Return assets by type in a given project
     * 
     * $type can be a single type name or array of types
     * 
     * @param Project $project
     * @param string $type
     * @param integer $min_state
     * @param integer $min_visibility
     * @return DBResult
     */
    static function findByTypeAndProject(Project $project, $type, $min_state = STATE_VISIBLE, $min_visibility = VISIBILITY_NORMAL) {
      return ProjectAssets::find(array(
        'conditions' => array('project_id = ? AND type IN (?) AND state >= ? AND visibility >= ?', $project->getId(), $type, $min_state, $min_visibility),
        'order' => 'created_on DESC',
      ));
    } // findByTypeAndProject
    
    /**
     * Return recent assets by type in a given project
     * 
     * @param Project $project
     * @param string $type
     * @param integer $recent_assets_num
     * @param integer $min_state
     * @param integer $min_visibility
     * @return DBResult
     */
    static function findRecentByTypeAndProject(Project $project, $type, $recent_assets_num = 10, $min_state = STATE_VISIBLE, $min_visibility = VISIBILITY_NORMAL) {
      return ProjectAssets::find(array(
        'conditions' => array('project_id = ? AND type IN (?) AND state >= ? AND visibility >= ?', $project->getId(), $type, $min_state, $min_visibility),
        'order' => 'created_on DESC',
        'limit' => $recent_assets_num
      ));
    } // findRecentByTypeAndProject
    
    /**
     * Return archived assets by type in a given project
     *
     * @param Project $project
     * @param string $type
     * @param integer $state
     * @param integer $min_visibility
     * @return array
     */
    static function findArchivedByTypeAndProject(Project $project, $type, $state = STATE_ARCHIVED, $min_visibility = VISIBILITY_NORMAL) {
      return ProjectAssets::find(array(
        'conditions' => array('project_id = ? AND type IN (?) AND state = ? AND visibility >= ?', $project->getId(), $type, $state, $min_visibility),
        'order' => 'created_on DESC'
      ));
    } // findArchivedByProject
        
    /**
     * Find assets for objects list
     * 
     * @param Project $project
     * @param IUser $user
     * @return array
     */
    static function findForObjectsList(Project $project, IUser $user, $state = STATE_VISIBLE) {
      $result = array();
      
			$asset_types = ProjectAssets::getAssetTypes();
			
			if(is_foreachable($asset_types)) {
			  $assets = DB::execute("SELECT id, type, name, LOWER(SUBSTRING(name, 1, 1)) AS first_letter, category_id, milestone_id, integer_field_1 AS version_num, state, visibility FROM " . TABLE_PREFIX . "project_objects WHERE type IN (?) AND project_id = ? AND state = ? AND visibility >= ? ORDER BY name", $asset_types, $project->getId(), $state, $user->getMinVisibility());
      
      	if ($assets instanceof DBResult) {
      	  $assets->setCasting(array(
      	    'id' => DBResult::CAST_INT, 
      	    'category_id' => DBResult::CAST_INT, 
      	    'milestone_id' => DBResult::CAST_INT, 
      	    'version_num' => DBResult::CAST_INT
      	  ));

          $asset_types_detailed = ProjectAssets::getAssetTypesDetailed();

          $icons = array();
          $asset_urls = array();

          foreach ($asset_types as $type) {
            $type_lower = strtolower($type);
            $asset_urls[$type_lower] = Router::assemble('project_assets_' . Inflector::underscore($type), array('project_slug' => $project->getSlug(), 'asset_id' => '--ASSETID--'));
            $icons[$type_lower] = $asset_types_detailed[$type]['icon'];
          } // foreach
      	  
          foreach ($assets as $asset) {
            $result[] = array(
              'id'                => $asset['id'],
              'name'              => $asset['name'],
              'category_id'       => $asset['category_id'],
              'milestone_id'      => $asset['milestone_id'],
              'first_letter'      => Inflector::transliterate(strtolower_utf($asset['first_letter'])),
              'type'              => $asset['type'],
              'icon'              => strtolower($asset['type']) == 'file' ? get_file_icon_url($asset['name'], '16x16') : array_var($icons, strtolower($asset['type']), ''),
              'permalink'         => str_replace('--ASSETID--', $asset['id'], array_var($asset_urls, strtolower($asset['type']))),
              'is_archived'       => $asset['state'] == STATE_ARCHIVED ? 1 : 0,
              'is_favorite'       => Favorites::isFavorite(array($asset['type'], $asset['id']), $user),
              'visibility'        => $asset['visibility']
            );
          } // foreach
      	} // if
			} // if
    	
      return $result;
    } // findForObjectsList
    
    /**
     * Find assets for printing by grouping and filtering criteria
     * 
     * @param Project $project
     * @param integer $min_state
     * @param integer $min_visibility
     * @param string $group_by
     * @param array $filter_by
     * @return DBResult
     */
    public function findForPrint(Project $project, $min_state = STATE_VISIBLE, $min_visibility = VISIBILITY_NORMAL, $group_by = null, $filter_by = null) { 	
      // initial condition
      $conditions = array(
      	DB::prepare('(project_id = ? AND type in ("File","Bookmark","TextDocument","YouTubeVideo") AND state >= ? AND visibility >= ?)', array($project->getId(), $min_state, $min_visibility)),
      );
      
      if (!in_array($group_by, array('milestone_id', 'category_id'))) {
      	$group_by = null;
      } // if
                
      // filter by completion status
      $filter_is_completed = array_var($filter_by, 'is_archived', null);
      if ($filter_is_completed === '0') {
        $conditions[] = DB::prepare('(state = ?)',array(STATE_VISIBLE));        	
      } else if ($filter_is_completed === '1') {
      	$conditions[] = DB::prepare('(state = ?)',array(STATE_ARCHIVED));
      } // if
      
      // filter by completion status
      $filter_type = array_var($filter_by, 'type', null);
      
      if ($filter_type != '') {
        $conditions[] = DB::prepare('(type = ?)',array($filter_type));        	
      }//if
      
      // do find assets
      $assets = ProjectAssets::find(array(
      	'conditions' => implode(' AND ', $conditions),
      	'order' => $group_by ? $group_by . ', name' : 'name'
      ));
    	
    	return $assets;
    } // findForPrint

    /**
     * Get all items from result and describes array for paged list
     *
     * @param DBResult $result
     * @param Project $active_project
     * @param User $logged_user
     * @param int $items_limit
     * @return Array
     */
    static function getDescribedFileArray(DBResult $result, Project $active_project, User $logged_user, $items_limit = null) {
      $return_value = array();

      if ($result instanceof DBResult) {

        $user_ids = array();
        foreach($result as $row) {
          if ($row['created_by_id'] && !in_array($row['created_by_id'], $user_ids)) {
            $user_ids[] = $row['created_by_id'];
          } //if
        } //if

        $users_array = count($user_ids) ? Users::findByIds($user_ids)->toArrayIndexedBy('getId') : array();

        foreach($result as $row) {

          $file = array();
          // File Details
          $file['id'] = $row['id'];
          $file['name'] = clean($row['name']);
          $file['is_favorite'] = Favorites::isFavorite(array('Discussion', $file['id']), $logged_user);

          // Favorite
          $favorite_params = $logged_user->getRoutingContextParams();
          $favorite_params['object_type'] = $row['type'];
          $favorite_params['object_id'] = $row['id'];

          // Urls
          $file['urls']['remove_from_favorites'] = Router::assemble($logged_user->getRoutingContext() . '_remove_from_favorites', $favorite_params);
          $file['urls']['add_to_favorites'] = Router::assemble($logged_user->getRoutingContext() . '_add_to_favorites', $favorite_params);
          $file['urls']['view'] = Router::assemble('project_assets_file', array('project_slug' => $active_project->getSlug(), 'asset_id' => $row['id']));
          $file['urls']['edit'] = Router::assemble('project_assets_file_edit', array('project_slug' => $active_project->getSlug(), 'asset_id' => $row['id']));
          $file['urls']['trash'] = Router::assemble('project_assets_file_trash', array('project_slug' => $active_project->getSlug(), 'asset_id' => $row['id']));

          // CRUD

          $file['permissions']['can_edit'] = ProjectAssets::canManage($logged_user, $active_project);
          $file['permissions']['can_trash'] = ProjectAssets::canManage($logged_user, $active_project);

          // User & datetime details

          $file['created_on'] = datetimeval($row['created_on']);
          $file['last_commented_on'] = datetimeval($row['datetime_field_1']);

          if($row['created_by_id']) {
            $file['created_by'] = $users_array[$row['created_by_id']];
          } elseif($row['created_by_email']) {
            $file['created_by'] = new AnonymousUser($row['created_by_name'], $row['created_by_email']);
          } else {
            $file['created_by'] = null;
          } // if
          $return_value[] = $file;

          if (count($return_value) === $items_limit) {
            break;
          } //if
        } // foreach
      } //if
      return $return_value;
    } // getDescribedDiscussionArray

    /**
     * Count files by project
     *
     * @param Project $project
     * @param Category $category
     * @param integer $min_state
     * @param integer $min_visibility
     * @return number
     */
    static function countFilesByProject(Project $project, $category = null, $min_state = STATE_VISIBLE, $min_visibility = VISIBILITY_NORMAL) {
      if ($category instanceof TaskCategory) {
        return ProjectAssets::count(array('project_id = ? AND type = ? AND category_id = ? AND state >= ? AND visibility >= ?', $project->getId(), 'File', $category->getId(), $min_state, $min_visibility));
      } else {
        return ProjectAssets::count(array('project_id = ? AND type = ? AND state >= ? AND visibility >= ?', $project->getId(), 'File', $min_state, $min_visibility));
      } // if
    } // countByProject
    
  }