<?php

  /**
   * Project creation process implementation
   * 
   * @package activeCollab.modules.system
   * @subpackage models
   */
  final class ProjectCreator {
    
    // Names of additional steps implemented by system module
    const STEP_IMPORT_TEMPLATE = 'import-template';
    const STEP_AUTO_ADD_USERS = 'auto-add-users';
    const STEP_RESCHEDULE = 'reschedule';
    const STEP_MASTER_CATEGORIES = 'import-master-categories';
    const STEP_CLOSE_REQUEST = 'close-request';
    
    /**
     * Project that's being created
     * 
     * @var Project
     */
    static private $project;
  
    /**
     * Create a project, and do all the additional steps right now
     * 
     * @param string $name
     * @param array $additional
     * @param boolean $instantly
     * @return Project
     */
    static function create($name, $additional = null, $instantly = true) {
      $logged_user = Authentication::getLoggedUser();
      
      $based_on = array_var($additional, 'based_on');
      $template = array_var($additional, 'template');
      
      $leader = array_var($additional, 'leader');
      if(!($leader instanceof User)) {
        $leader = $logged_user;
      } // if

      // Create a new project instance
      self::createProject($name, $leader, array_var($additional, 'overview'), array_var($additional, 'company'), array_var($additional, 'category'), $template, array_var($additional, 'first_milestone_starts_on'), $based_on, array_var($additional, 'label_id'), array_var($additional, 'currency_id'), array_var($additional, 'budget'), array_var($additional, 'custom_field_1'), array_var($additional, 'custom_field_2'), array_var($additional, 'custom_field_3'));
      
      // Add leader and person who created a project to the project
      self::$project->users()->add($logged_user);
      
      if($logged_user->getId() != $leader->getId()) {
        self::$project->users()->add($leader); 
      } // if
      
      if($instantly) {
        if(is_foreachable(self::$project->getAdditionalSteps($logged_user))) {
          foreach(self::$project->getAdditionalSteps($logged_user) as $step_name => $step) {
            self::executeAdditionalStep($step_name, self::$project, $logged_user);
          } // if
        } // if
      } // if
      
      return self::$project;
    } // create
    
    /**
     * Execute additional, post creation step
     * 
     * @param string $step
     * @param Project $project
     * @param IUser $user
     */
    static function executeAdditionalStep($step, Project $project, IUser $user) {
      try {
        DB::beginWork('Executing project creation step @ ' . __CLASS__);
        
        switch($step) {
          
          // Copy template items to target project
          case self::STEP_IMPORT_TEMPLATE:
            $template = $project->getTemplate();
            
            if($template instanceof Project) {
              $template->copyItems($project);
            } // if
            
            break;
            
          // Add users that are set to be automatically added to the project
          case self::STEP_AUTO_ADD_USERS:
            Users::importAutoAssignIntoProject($project);
            
            break;
            
          // Reschedule
          case self::STEP_RESCHEDULE:
            $template = $project->getTemplate();
            
            if($template instanceof Project) {
              try {
                DB::beginWork('Rescheduling project @ ' . __CLASS__);
                
                $reference_first_milestone_date = Milestones::getFirstMilestoneStartsOn($template);
                $new_first_milestone_date = ConfigOptions::getValueFor('first_milestone_starts_on', $project);
                
                if(empty($new_first_milestone_date)) {
                  $new_first_milestone_date = DateValue::makeFromString($project->getCreatedOn()->toMySQL());
                } else {
                  $new_first_milestone_date = DateValue::makeFromString($new_first_milestone_date);
                } // if
                
                ProjectScheduler::rescheduleProject($project, $reference_first_milestone_date, $new_first_milestone_date, ConfigOptions::getValue('skip_days_off_when_rescheduling'));
                
                ConfigOptions::removeValuesFor($project, 'first_milestone_starts_on');
                
                DB::commit('Project rescheduled @ ' . __CLASS__);
              } catch(Exception $e) {
                DB::rollback('Failed to rescheduled project @ ' . __CLASS__);
                throw $e;
              } // try
            } // if
            
            break;
            
          // Create default set of categories based on master categories
          case self::STEP_MASTER_CATEGORIES:
            $project->availableCategories()->importMasterCategories($user);
            
            break;
            
          // Close project request if this project was based on a request
          case self::STEP_CLOSE_REQUEST:
            $based_on = $project->getBasedOn();
            
            if($based_on instanceof ProjectRequest) {
              $based_on->close($user);
            } // if

            if($based_on instanceof Quote) {
              $based_on->markAsWon($user);
            } // if
            
            break;
            
          // Custom step
          default:
            EventsManager::trigger('on_project_additional_step', array($step, &$project, &$user));
        } // switch
        
        DB::commit('Project creation step executed @ ' . __CLASS__);
      } catch(Exception $e) {
        DB::rollback('Failed to execute project creation step @ ' . __CLASS__);
        throw $e;
      } // try
    } // executeAdditionalStep
    
    /**
     * Create a new project
     * 
     * @param string $name
     * @param User $leader
     * @param string $overview
     * @param Company $client
     * @param Category $category
     * @param Project $template
     * @param DateValue $first_milestone_starts_on
     * @param IProjectBasedOn $based_on
     * @param int $label_id
     * @param integer $currency_id
     * @parma integer $budget
     * @return Project
     */
    static private function createProject($name, User $leader, $overview, $client = null, $category = null, $template = null, $first_milestone_starts_on = null, $based_on = null, $label_id = null, $currency_id = null, $budget = null, $custom_field_1 = null, $custom_field_2 = null, $custom_field_3 = null) {
      try {
        DB::beginWork('Creating a project @ ' . __CLASS__);
        
        self::$project = new Project();

        self::$project->setAttributes(array(
          'name' => $name, 
          'slug' => Inflector::slug($name),
        	'label_id' => (isset($label_id) && $label_id) ? $label_id : 0,
          'overview' => trim($overview) ? trim($overview) : null, 
          'company_id' => $client instanceof Company ? $client->getId() : null, 
          'category_id' => $category instanceof ProjectCategory ? $category->getId() : null,
          'custom_field_1' => $custom_field_1,
          'custom_field_2' => $custom_field_2,
          'custom_field_3' => $custom_field_3,
        ));
        
        self::$project->setState(STATE_VISIBLE);
        self::$project->setLeader($leader);
        
        if($template instanceof Project) {
          self::$project->setTemplate($template);
        } // if
        
        if($based_on instanceof IProjectBasedOn) {
          self::$project->setBasedOn($based_on);
        } // if
        
        self::$project->setCurrencyId($currency_id);
        
        if(AngieApplication::isModuleLoaded('tracking') && $budget) {
          self::$project->setBudget($budget);
        } // if
        
        self::$project->save();
        
        if($template instanceof Project && $first_milestone_starts_on instanceof DateValue) {
          ConfigOptions::setValueFor('first_milestone_starts_on', self::$project, $first_milestone_starts_on->toMySQL());
        } // if
        
        DB::commit('Project created @ ' . __CLASS__);
      } catch(Exception $e) {
        DB::rollback('Failed to create project @ ' . __CLASS__);
        throw $e;
      } // try
    } // createProject
    
  }