<?php

  /**
   * Milestones manager class
   *
   * @package activeCollab.modules.milestones
   * @subpackage models
   */
  class Milestones extends ProjectObjects {
  	
  	/**
  	 * Default ordering of milestones
  	 * 
  	 * @var string
  	 */
  	static private $order_milestones_by = 'NOT ISNULL(completed_on), ISNULL(date_field_1), date_field_1, position, created_on';
    
    /**
     * Returns true if $user can access milestones seciton of $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, 'milestone', ($check_tab ? 'milestones' : null));
    } // canAccess
    
    /**
     * Returns true if $user can add milestones 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, 'milestone', ($check_tab ? 'milestones' : null));
    } // canAdd
    
    /**
     * Returns true if $user can manage discussions 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, 'milestone', ($check_tab ? 'milestones' : null));
    } // canManage
    
    // ---------------------------------------------------
    //  Finders
    // ---------------------------------------------------
    
    /**
     * Return milestones by a given list of ID-s
     *
     * @param array $ids
     * @param integer $min_state
     * @param integer $min_visibility
     * @return array
     */
    static function findByIds($ids, $min_state = STATE_VISIBLE, $min_visibility = VISIBILITY_NORMAL) {
      return ProjectObjects::find(array(
        'conditions' => array('id IN (?) AND type = ? AND state >= ? AND visibility >= ?', $ids, 'Milestone', $min_state, $min_visibility),
        'order' => self::$order_milestones_by,
      ));
    } // findByIds
    
    /**
     * Return all visible milestone by a project
     *
     * @param Project $project
     * @param integer $min_visibility
     * @return array
     */
    static function findAllByProject($project, $min_visibility = VISIBILITY_NORMAL) {
    	return ProjectObjects::find(array(
        'conditions' => array('project_id = ? AND type = ? AND state >= ? AND visibility >= ?', $project->getId(), 'Milestone', STATE_VISIBLE, $min_visibility),
        'order' => self::$order_milestones_by,
      ));
    } // findAllByProject
  
    /**
     * Return all milestones for a given project
     *
     * @param Project $project
     * @param User $user
     * @return Milestone[]
     */
    static function findByProject(Project $project, User $user) {
      if(Milestones::canAccess($user, $project)) {
        return ProjectObjects::find(array(
          'conditions' => array('project_id = ? AND type = ? AND state >= ? AND visibility >= ?', $project->getId(), 'Milestone', STATE_VISIBLE, $user->getMinVisibility()),
          'order' => self::$order_milestones_by,
        ));
      } // if
      return null;
    } // findByProject

    /**
     * Find archived notebooks by project
     *
     * @param Project $project
     * @param integer $state
     * @param integer $min_visibility
     * @return array
     */
    static function findArchivedByProject(Project $project, $min_visibility = VISIBILITY_NORMAL) {
      return ProjectObjects::find(array(
        'conditions' => array('project_id = ? AND type = ? AND state = ? AND visibility >= ?', $project->getId(), 'Milestone', STATE_ARCHIVED, $min_visibility),
        'order' => self::$order_milestones_by
      ));
    } // findArchivedByProject
    
    /**
     * Count milestones by project
     * 
     * @param Project $project
     * @param integer $min_state
     * @param integer $min_visibility
     * @return number
     */
    static function countByProject(Project $project, $min_state = STATE_VISIBLE, $min_visibility = VISIBILITY_NORMAL) {
   	  return Milestones::count(array('project_id = ? AND type = ? AND state >= ? AND visibility >= ?', $project->getId(), 'Milestone', $min_state, $min_visibility));
    } // countByProject
    
    /**
     * Return milestones for timeline
     * 
     * @param Project $project
     * @param User $user
     * @return DBResult
     */
    static function findForTimeline(Project $project, User $user, $state = STATE_VISIBLE) {
      if(Milestones::canAccess($user, $project)) {
        return ProjectObjects::find(array(
          'conditions' => array('project_id = ? AND type = ? AND state = ? AND visibility >= ?', $project->getId(), 'Milestone', $state, $user->getMinVisibility()),
          'order' => self::$order_milestones_by,
        ));
      } // if
      return null;    	
    } // findForTimeline
    
    /**
     * Return all active milestones in a given project
     *
     * @param Project $project
     * @param integer $min_state
     * @param integer $min_visibility
     * @return array
     */
    static function findActiveByProject($project, $min_state = STATE_VISIBLE, $min_visibility = VISIBILITY_NORMAL) {
      return ProjectObjects::find(array(
        'conditions' => array('project_id = ? AND type = ? AND state >= ? AND visibility >= ? AND completed_on IS NULL', $project->getId(), 'Milestone', $min_state, $min_visibility),
        'order' => self::$order_milestones_by
      ));
    } // findActiveByProject
    
    /**
     * Return completed milestones by project
     *
     * @param Project $project
     * @param integer $min_state
     * @param integer $min_visibility
     * @return array
     */
    static function findCompletedByProject($project, $min_state = STATE_VISIBLE, $min_visibility = VISIBILITY_NORMAL) {
      return ProjectObjects::find(array(
        'conditions' => array('project_id = ? AND type = ? AND state >= ? AND visibility >= ? AND completed_on IS NOT NULL', $project->getId(), 'Milestone', $min_state, $min_visibility),
        'order' => self::$order_milestones_by,
      ));
    } // findCompletedByProject
    
    /**
     * Find successive milestones by a given milestone
     *
     * @param Milestone $milestone
     * @param integer $min_state
     * @param integer $min_visibility
     * @return array
     */
    static function findSuccessiveByMilestone(Milestone $milestone, $min_state = STATE_VISIBLE, $min_visibility = VISIBILITY_NORMAL) {
      $start_on = $milestone->getStartOn();
      
      if($start_on instanceof DateValue) {
        return Milestones::find(array(
          'conditions' => array('project_id = ? AND type = ? AND date_field_1 > ? AND state >= ? AND visibility >= ? AND id != ?', $milestone->getProjectId(), 'Milestone', $start_on, $min_state, $min_visibility, $milestone->getId()),
          'order' => 'date_field_1',
        ));
      } else {
        return null;
      } // if
    } // findSuccessiveByMilestone

    // ---------------------------------------------------
    //  Utilities
    // ---------------------------------------------------
    
    /**
     * Returns ID name map
     * 
     * $filter can be:
     * 
     * - Project instance, only milestones from that project will be returned
     * - Array of milestone IDs
     * - NULL, in that case all milestones with given state will be returned
     *
     * @param mixed $filter
     * @param integer $min_state
     * @return array
     */
    static function getIdNameMap($filter = null, $min_state = STATE_VISIBLE) {
      if($filter instanceof Project) {
        $rows = DB::execute('SELECT id, name FROM ' . TABLE_PREFIX . 'project_objects WHERE project_id = ? AND type = ? AND state >= ? ORDER BY ' . self::$order_milestones_by, $filter->getId(), 'Milestone', $min_state);
      } elseif(is_array($filter)) {
        $rows = DB::execute('SELECT id, name FROM ' . TABLE_PREFIX . 'project_objects WHERE id IN (?) AND type = ? AND state >= ? ORDER BY ' . self::$order_milestones_by, $filter, 'Milestone', $min_state);
      } else {
        $rows = DB::execute('SELECT id, name FROM ' . TABLE_PREFIX . 'project_objects WHERE type = ? AND state >= ? ORDER BY ' . self::$order_milestones_by, 'Milestone', $min_state);
      } // if
      
      if(is_foreachable($rows)) {
        $result = array();
        
        foreach($rows as $row) {
          $result[(integer) $row['id']] = $row['name'];
        } // foreach
        
        return $result;
      } else {
        return null;
      } // if
    } // getIdNameMap
    
    /**
     * Return ID-s by list of milestone names
     * 
     * @param array $names
     * @param Project $project
     * @return array
     */
    static function getIdsByNames($names, $project = null) {
      if($names) {
        if($project instanceof Project) {
          $ids = DB::executeFirstColumn('SELECT id FROM ' . TABLE_PREFIX . 'project_objects WHERE project_id = ? AND name IN (?) AND type = ?', $project->getId(), $names, 'Milestone');
        } else {
          $ids = DB::executeFirstColumn('SELECT id FROM ' . TABLE_PREFIX . 'project_objects WHERE name IN (?) AND type = ?', $names, 'Milestone');
        } // if
        
        if($ids) {
          foreach($ids as $k => $v) {
            $ids[$k] = (integer) $v;
          } // foreach
        } // if
        
        return $ids;
      } else {
        return null;
      } // if
    } // getIdsByNames
    
    /**
     * Return date when first project milestone starts on
     * 
     * @param Project $project
     * @return DateValue
     */
    static function getFirstMilestoneStartsOn(Project $project) {
      $first_milestone_starts_on = DB::executeFirstCell('SELECT date_field_1 FROM ' . TABLE_PREFIX . 'project_objects WHERE project_id = ? AND type = ? AND state >= ?', $project->getId(), 'Milestone', STATE_VISIBLE);
      
      if($first_milestone_starts_on) {
        return DateValue::makeFromString($first_milestone_starts_on);
      } else {
        return DateValue::make($project->getCreatedOn()->getMonth(), $project->getCreatedOn()->getDay(), $project->getCreatedOn()->getYear());
      } // if
    } // getFirstMilestoneStartsOn

    /**
     * Fix milestone IDs for a given project
     *
     * @param Project $project
     */
    static function fixMilestoneIds(Project $project) {
      $project_objects_table = TABLE_PREFIX . 'project_objects';

      $milestone_ids = DB::executeFirstColumn("SELECT DISTINCT id FROM $project_objects_table WHERE type = 'Milestone' AND project_id = ? AND state >= ?", $project->getId(), STATE_TRASHED);

      // Reset all milestone ID-s if they are set, but don't belong to project's milestone
      if($milestone_ids) {
        DB::execute("UPDATE $project_objects_table SET milestone_id = NULL WHERE milestone_id IS NOT NULL AND milestone_id NOT IN (?) AND project_id = ?", $milestone_ids, $project->getId());

      // Reset all milestone ID-s if they are set, because this project does not have any visible milestones
      } else {
        DB::execute("UPDATE $project_objects_table SET milestone_id = NULL WHERE milestone_id IS NOT NULL AND project_id = ?", $project->getId());
      } // if
    } // fixMilestoneIds
  
  }