<?php

  /**
   * RecurringProfile class
   *
   * @package activeCollab.modules.invoicing
   * @subpackage models
   */
  class RecurringProfile extends BaseRecurringProfile implements IRoutingContext, IState, INotifierContext {
  
    // Frequency
    const FREQUENCY_DAILY = 'daily';
    const FREQUENCY_WEEKLY = 'weekly';
    const FREQUENCY_TWO_WEEKS = '2 weeks';
    const FREQUENCY_MONTHLY = 'monthly';
    const FREQUENCY_TWO_MONTHS = '2 months';
    const FREQUENCY_THREE_MONTHS = '3 months';
    const FREQUENCY_SIX_MONTHS = '6 months';
    const FREQUENCY_YEARLY = 'yearly';
    
    /**
     * Return invoice due after text
     */
    function getInvoiceDueAfterText() {
      switch ($this->getInvoiceDueAfter()) {
        case 0:
          return lang('Due Upon Receipt');
          break;
        case 10:
          return lang('10 Days After Issue (NET 10)');
          break;
        case 15:
          return lang('15 Days After Issue (NET 15)');
          break;
        case 30:
          return lang('30 Days After Issue (NET 30)');
          break;
        case 60:
          return lang('60 Days After Issue (NET 60)');
          break;
       
      }//switch
    }//getInvoiceDueAfterText
    
    
    
    /**
     * Return proper type name in user's language
     *
     * @param boolean $lowercase
     * @param Language $language
     * @return string
     */
    function getVerboseType() {
      return lang('Recurring Profile');
    } // getVerboseType
    
    
    
    /**
     * Cached Recipient
     * 
     * @var User
     */
    var $recipient = false;
    
    /**
     * Return recipient
     * 
     * @return User
     */
    function getRecipient() {
      if($this->recipient === false) {
        if($this->getRecipientId()) { 
          $this->recipient = new User($this->getRecipientId());
          if(!$this->recipient instanceof IUser) {
           $this->recipient = new AnonymousUser($this->getRecipientName(),$this->getRecipientEmail());
          }//if
        }//if
       } // if
      
      return $this->recipient;
    }//getRecipient
    
    /**
     * Ser recipient
     * 
     * @param $recipient - can be instance of IUser or id of user
     */
    function setRecipient($recipient) {
      if(!$recipient instanceof IUser) {
        $recipient = Users::findById($recipient);
      }//if
      
      if($recipient instanceof IUser) {
        $this->setRecipientEmail($recipient->getEmail());
        $this->setRecipientId($recipient->getId());
        $this->setRecipientName($recipient->getName());
      }//if
    }//setRecipient
    
    /**
     * Return true if profile started already
     * 
     */
    function isStarted() {
      $today = new DateValue();
      return strtotime($today) >= strtotime($this->getStartOn());
    }//isStarted
    
    /**
     * Cached project instance
     *
     * @var Project
     */
    var $project = false;

    /**
     * Return project instance
     *
     * @return Project
     */
    function getProject() {
      if($this->project === false) {
        $this->project = Projects::findById($this->getProjectId());
      } // if
      return $this->project;
    } // getProject

    
    function setNextTriggerOnDate($frequency, $start_on = false) {
      
      if($start_on) {
        $last_triggered_on = $start_on;
      } else {
       // $last_triggered_on = date("Y-m-d",$this->getNextTriggerOn()->getTimestamp());
        $last_triggered_on = date("Y-m-d");
      }//if
      
      switch ($frequency) {
        case self::FREQUENCY_DAILY:
          $next_trigger_on = date("Y-m-d",strtotime($last_triggered_on . " +1 day"));
          break;
        case self::FREQUENCY_WEEKLY:
          $next_trigger_on = date("Y-m-d",strtotime($last_triggered_on . " +1 week"));
          break;
        case self::FREQUENCY_TWO_WEEKS:
          $next_trigger_on = date("Y-m-d",strtotime($last_triggered_on . " +2 week"));
          break;
        case self::FREQUENCY_MONTHLY:
          $next_trigger_on = date("Y-m-d",strtotime($last_triggered_on . " +1 month"));
          break;
        case self::FREQUENCY_TWO_MONTHS:
          $next_trigger_on = date("Y-m-d",strtotime($last_triggered_on . " +2 month"));
          break;
        case self::FREQUENCY_THREE_MONTHS:
          $next_trigger_on = date("Y-m-d",strtotime($last_triggered_on . " +3 month"));
          break;
        case self::FREQUENCY_SIX_MONTHS:
          $next_trigger_on = date("Y-m-d",strtotime($last_triggered_on . " +6 month"));
          break;
        case self::FREQUENCY_YEARLY:
          $next_trigger_on = date("Y-m-d",strtotime($last_triggered_on . " +1 year"));
          break;
      }//switch
      
      parent::setNextTriggerOn($next_trigger_on);
     }//setNextTriggerOn
     
     /**
      * Check to see if this profile is archived
      * 
      * @return boolean
      */
     function isArchived() {
       return $this->getState() == STATE_ARCHIVED ? true : false;
     }//isArchived
     
     
     /**
      * Check to see if this recurring profile skip to trigger (If administrator forget to approve it or similar)
      * 
      * @return boolean
      */
     function isSkippedToTrigger() {
       $today = new DateValue();
       if(strtotime($today) > strtotime($this->getNextTriggerOn()) && $this->getState() == STATE_VISIBLE) {
         return true;
       }//if
       return false;
     }//isSkippedToTrigger
    
     /**
      * Return next occurrence number
      */
     function getNextOccurrenceNumber() {
       return $this->getTriggeredNumber() + 1;
     }//getNextOccurrenceNumber
     
     /**
      * Return is last occurrence 
      */
     function isLastOccurrence() {
       return $this->getNextOccurrenceNumber() == $this->getOccurrences(); 
     }//isLastOccurrence
     
     /**
      * Increase triggered number on trigger
      */
     function increaseTriggeredNumber() {
       $number = $this->getTriggeredNumber() + 1;
       $this->setTriggeredNumber($number);
     }//increaseTriggeredNumber
     
     /**
      * Reset triggered number to 0
      */
     function resetTriggeredNumber() {
       $this->setTriggeredNumber(0);
       return $this->save();
     }//resetTriggeredNumber
     
    /**
     * Add items
     * 
     * @param $items
     */
    function addItems($items, $delete_items = false) {
      if($delete_items) {
        $this->deleteItems();
      }//if
      $counter = 0;
      if(is_foreachable($items)) {
      	foreach($items as $item_data) {
      		$item = new RecurringProfileItem();
      		$item->setAttributes($item_data);
      		$item->setRecurringProfileId($this->getId());
      		$item->setPosition($counter + 1);
      		$item->save();
      		$counter++;
      	} // foreach
      } // if
      if($counter == 0) {
      	throw new ValidationErrors(array('recurring_profile' => lang('Recurring profile items data is not valid. All descriptions are required and there need to be at least one unit with cost set per item!')));
      } // if
    }//addItems
    
    /**
     * Delete items in this profile
     */
    function deleteItems() {
      if(is_foreachable($this->getItems())) {
        foreach($this->getItems() as $item) {
          if($item instanceof RecurringProfileItem) {
            $item->delete();
          }//if
        }//foreach
      }//if
    }//deleteItems
       
    /**
     * Cached company value
     *
     * @var Company
     */
    protected $company = false;
  	
  	/**
  	 * Return quote client
  	 *
  	 * @return Company
  	 */
  	function getCompany() {
  		if($this->company === false) {
  			$this->company = Companies::findById($this->getCompanyId());
  		} // if
  		return $this->company;
  	} // getCompany
  	
  	/**
  	 * Return allow payments display text
  	 * 
  	 * @return string
  	 */
  	function getAllowPaymentsText() {
  	  if (!$this->getAllowPayments()) {
  	    return lang('Do not allow payments');
  	  } else if ($this->getAllowPayments() == 1) {
  	    return lang('Allow full payments');
  	  } else if ($this->getAllowPayments() == 2) {
  	    return lang('Allow partial payments');
 	    } else if ($this->getAllowPayments() == -1) {
 	    	return lang('Use system default');
  	  } // if
  	} // getAllowPaymentsText
  	
  	/**
     * Return currency code
     *
     * @return string
     */
    function getCurrencyCode() {
      return $this->getCurrency() instanceof Currency ? $this->getCurrency()->getCode() : lang('Unknown Currency');
    } // getCurrencyCode
    
    /**
     * Cached language value
     *
     * @var Language
     */
    protected $language = false;

    /**
      * Return quote language
      *
      * @return Language
      */
    function getLanguage() {
      if($this->language === false) {
        $this->language = Languages::findById($this->getLanguageId());
      } // if
      return $this->language;
    } // getLanguage
    
    /**
     * Cached currency instance
     *
     * @var Currency
     */
    protected $currency = false;

    /**
     * Return quote currency
     *
     * @return Currency
     */
    function getCurrency() {
      if($this->currency === false) {
        $this->currency = Currencies::findById($this->getCurrencyId());
      } // if
      return $this->currency;
    } // getCurrency
    
    /**
     * Cached quote total
     *
     * @var float
     */
    protected $total = false;

    /**
     * Cached tax value
     *
     * @var float
     */
    protected $tax = false;
  	
  	/**
     * Cached taxed total
     *
     * @var float
     */
    protected $taxed_total = false;
    
    /**
     * Return quote total
     *
     * @param boolean $cache
     * @return float
     */
    function getTotal($cache = true) {
      $this->calculateTotal($cache);
      return $this->total;
    } // getTotal
    
    /**
     * Return calculated tax
     *
     * @param boolean $cache
     * @return float
     */
    function getTax($cache = true) {
      $this->calculateTotal($cache);
      return $this->tax;
    } // getTax
  	
  	/**
     * Returned taxed total
     *
     * @param boolean $cache
     * @return float
     */
    function getTaxedTotal($cache = true) {
      $this->calculateTotal($cache);
      return $this->taxed_total;
    } // getTaxedTotal
    
    /**
     * Calculate total by walking through list of items
     */
    function calculateTotal($cache = true) {
      if($cache === false || $this->total === false || $this->tax === false || $this->taxed_total === false) {
        $this->total = 0;
        $this->tax = 0;

        if(is_foreachable($this->getItems())) {
          foreach($this->getItems() as $item) {
            $this->total += $item->getSubTotal();
            $this->tax   += $item->getTax();
          } // foreach
        } // if
        
        // now we have total tax for quote, we need to round it up to 2 decimals
        $this->tax = round($this->tax, 2);
        $this->taxed_total = $this->total + $this->tax;
      } // if
    } // calculateTotal
    
    // ---------------------------------------------------
    //  Interface implementations
    // ---------------------------------------------------
    
    /**
     * Return email notification context ID
     *
     * @return string
     */
    function getNotifierContextId() {
      return 'RECURRING_PROFILE/' . $this->getId();
    } // getNotifierContextId
    
    
    /**
     * State helper instance
     *
     * @var IStateImplementation
     */
    private $state = false;
    
    /**
     * Return state helper instance
     *
     * @return IStateImplementation
     */
    function state() {
      if($this->state === false) {
        $this->state = new IStateImplementation($this);
      } // if
      
      return $this->state;
    } // state
    
    
    /**
     * Routing context name
     *
     * @var string
     */
    private $routing_context = false;
    
    /**
     * Return routing context name
     *
     * @return string
     */
    function getRoutingContext() {
      if($this->routing_context === false) {
        $this->routing_context = 'recurring_profile';
      } // if
      
      return $this->routing_context;
    } // getRoutingContext
    
    /**
     * Routing context parameters
     *
     * @var array
     */
    private $routing_context_params = false;
    
    /**
     * Return routing context parameters
     *
     * @return array
     */
    function getRoutingContextParams() {
      if($this->routing_context_params === false) {
        $this->routing_context_params = array(
          'recurring_profile_id' => $this->getId(),
        );
      } // if
      
      return $this->routing_context_params;
    } // getRoutingContextParams
        
    /**
     * Return main page url
     */
    function getMainPageUrl() {
      return Router::assemble('recurring_profiles');
    }//getMainPageUrl
    
    /**
     * Return view url
     */
    function getViewUrl() {
      return Router::assemble('recurring_profile', array('recurring_profile_id' => $this->getId()));
    }//getViewUrl
    
    /**
     * Return add new recurring profile url
     */
    function getAddUrl() {
      return Router::assemble('recurring_profile_add');
    }//getAddUrl
    
    /**
     * Return edit recurring profile url
     */
    function getEditUrl() {
      return Router::assemble('recurring_profile_edit', array('recurring_profile_id' => $this->getId()));
    }//getAddUrl
    
    /**
     * Return trigger recurring profile url
     */
    function getTriggerUrl() {
      return Router::assemble('recurring_profile_trigger', array('recurring_profile_id' => $this->getId()));
    }//getTriggerUrl

    
     /**
     * Return duplicate recurring profile url
     */
    function getDuplicateUrl() {
      return Router::assemble('recurring_profile_duplicate', array('recurring_profile_id' => $this->getId()));
    }//getDuplicateUrl
    
    
    /**
     * Get Items for this profile
     */
    function getItems() {
      return RecurringProfileItems::findByProfileId($this->getId());
    }//getItems

    /**
     * Prepare list of options that $user can use
     *
     * @param IUser $user
     * @param NamedList $options
     * @param string $interface
     * @return NamedList
     */
    protected function prepareOptionsFor(IUser $user, NamedList $options, $interface = AngieApplication::INTERFACE_DEFAULT) {
      
      parent::prepareOptionsFor($user, $options, $interface);
      
      // mark archive as important
      if ($options->exists('archive')) {
      	$archive = $options->get('archive');
      	$archive['important'] = true;
      	$options->add('archive', $archive);
      } // if
      
      if($this->isSkippedToTrigger()) {
        $options->add('trigger', array(
          'text' => lang('Trigger'),
          'url'  => $this->getTriggerUrl(),
          'important' => true,
          'onclick' => new AsyncLinkCallback(array(
            'confirmation' => lang('Are you sure that you want to trigger this :object_name profile?', array("object_name" => $this->getName())),
            'success_message' => lang(':object_name profile has been successfully triggered', array("object_name" => $this->getName())),
            'success_event' => $this->getUpdatedEventName(),
          ))
        ));
      }//if
      
      if(RecurringProfiles::canAdd($user)) {
        $options->add('duplicate', array(
          'text' => lang('Duplicate'),
          'url'  => $this->getDuplicateUrl(),
          'onclick' => new FlyoutFormCallback($this->getCreatedEventName()),
        	'icon' => AngieApplication::getImageUrl('icons/12x12/duplicate-invoice.png', INVOICING_MODULE)
        ));
      }//if
      

      return $options;
      
    } // prepareOptionsFor

    /**
     * Returns true if $user can view
     *
     * @param User $user
     * @return boolean
     */
    function canView($user) {
      return $user->isFinancialManager();
    } // canView

    /**
     * Returns true if $user can edit this recurring profile
     *
     * @param User $user
     * @return boolean
     */
    function canEdit($user) {
      return $user->isFinancialManager();
    } // canEdit

    /**
     * Returns true if $user can delete this recurring profile
     *
     * @param User $user
     * @return boolean
     */
    function canDelete($user) {
      return $user->isFinancialManager();
    } // canDelete
    
		/**
     * Return array or property => value pairs that describes this object
     *
     * $user is an instance of user who requested description - it's used to get
     * only the data this user can see
     *
     * @param IUser $user
     * @param boolean $detailed
     * @param boolean $for_interface
     * @return array
     */
    function describe(IUser $user, $detailed = false, $for_interface = false) {
      $result = parent::describe($user, $detailed, $for_interface);
      
      $result['start_on'] = $this->getStartOn();
      $result['frequency'] = $this->getFrequency();
      $result['occurrence'] = $this->getOccurrences();
      $result['auto_issue'] = $this->getAutoIssue();
      $result['invoice_due_after'] = $this->getInvoiceDueAfterText();
      
 			$result['next_trigger_on'] = $this->getNextTriggerOn();
 			$result['next_occurrence_number'] = $this->getNextOccurrenceNumber();
      $result['triggered_number'] = $this->getTriggeredNumber();
      $result['is_skipped'] = $this->isSkippedToTrigger();
      
      $result['occurrence_left'] = $this->getTriggeredNumber() . '/' . $this->getOccurrences();
      
      $result['currency'] = $this->getCurrency() instanceof Currency ? $this->getCurrency()->describe($user, false, $for_interface) : null;
      $result['project'] = $this->getProject() instanceof Project ? $this->getProject()->describe($user, false, $for_interface) : null;
      $result['client'] = $this->getCompany() instanceof Company ? $this->getCompany()->describe($user, false, $for_interface) : null;
      $result['client_address'] = $this->getCompanyAddress();
      
      $result['allow_payments'] = $this->getAllowPayments();
      $result['allow_payments_text'] = $this->getAllowPaymentsText();
      
      $result['note'] = nl2br($this->getNote());
      $result['our_comment'] = $this->getOurComment();

      $result['subtotal'] = $this->getTotal(false);
      $result['tax'] = $this->getTax(false);
      $result['total'] = $this->getTaxedTotal(false);
      
      $result['items'] = array();
      
      $items = $this->getItems();
      if (is_foreachable($items)) {
      	foreach ($items as $item) {
      		$result['items'][] = $item->describe($user, false, $for_interface);
      	} // foreach
      } // if
            
      $result['permissions'] = array(
      	'can_view' => $this->canView($user), 
      );
      
      return $result;
    } // describe

    /**
     * Return array or property => value pairs that describes this object
     *
     * @param IUser $user
     * @param boolean $detailed
     * @return array
     */
    function describeForApi(IUser $user, $detailed = false) {
      throw new NotImplementedError(__METHOD__);
    } // describeForApi
    
    /**
     * Validate before save
     *
     * @param ValidationErrors $errors
     */
    function validate(ValidationErrors &$errors) {
      
      if(!$this->validatePresenceOf('name')) {
        $errors->addError(lang('Please enter recurring profile name.'), 'name');
      } // if
      
      if(!$this->validatePresenceOf('occurrences', 1)) {
        $errors->addError(lang('Occurrence has to be numeric value larget than 0'), 'occurrence');
      } // if
      
      if(!$this->validatePresenceOf('start_on') || !$this->getStartOn() instanceof DateValue) {
        $errors->addError(lang('Start date has to be valid date value.'), 'start_on');
      } // if
            
      parent::validate($errors, true);
    } // validate
    
  }