<?php

  /**
   * Quotes class
   *
   * @package activeCollab.modules.invoicing
   * @subpackage models
   */
  class Quotes extends BaseQuotes {
    
    // Project steps
    const STEP_IMPORT_DISCUSSION = 'import-quote-discussion';
    const STEP_IMPORT_MILESTONES = 'import-quote-items';
    
    // ---------------------------------------------------
    //  Permissions
    // ---------------------------------------------------
    
    /**
     * Returns true if $user can create new quote
     * 
     * Optonally, $based_on can be provided, so we can check if user can create 
     * a new quote based on a given project request
     * 
     * @param IUser $user
     * @param ProjectRequest $based_on
     * @throws InvalidInstanceError
     * @return bool
     */
    static function canAdd(IUser $user, $based_on = null) {
      if(Quotes::canManage($user)) {
        if($based_on === null) {
          return true;
        } else {
          if($based_on instanceof ProjectRequest) {
            return DB::executeFirstCell('SELECT COUNT(id) FROM ' . TABLE_PREFIX . 'quotes WHERE based_on_type = ? AND based_on_id = ?', get_class($based_on), $based_on->getId()) == 0;
          } else {
            throw new InvalidInstanceError('based_on', $based_on, 'ProjectRequest', 'Quotes can be based on project requests only');
          } // if
        } // if
      } else {
        return false;
      } // if
    } // canAdd
    
    /**
     * Returns true if $user can manage quotes
     * 
     * @param IUser $user
     * @return boolean
     */
    static function canManage(IUser $user) {
      return $user->getSystemPermission('can_manage_quotes') || $user->isFinancialManager();
    } // canManage
    
    // ---------------------------------------------------
    //  Finders
    // ---------------------------------------------------
    
    /**
     * Find quotes by ID-s
     * 
     * @param array $ids
     * @return DBResult
     */
    static function findByIds($ids) {
      return Quotes::find(array(
        'conditions' => array('id IN (?)', $ids),
      ));
    } // findByIds

    /**
     * Find quote by public id
     *
     * @param string $public_id
     * @return Quote 
     */
    function findByPublicId($public_id) {
      return Quotes::find(array(
        'conditions' => array('public_id = ?', $public_id),
        'one' => true
      ));
    } // findByPublicId

    /**
     * Get status map for quotes
     *
     * @return array
     */
    static function getStatusMap() {
      return array(
        QUOTE_STATUS_DRAFT => lang('Draft'),
        QUOTE_STATUS_LOST => lang('Lost'),
        QUOTE_STATUS_SENT => lang('Sent'),
        QUOTE_STATUS_WON => lang('Won')
      );
    } // getStatusMap

    /**
     * Get quote statuses that $user can see
     *
     * @param User $user
     * @return array
     */
    function getVisibleStatuses(User $user) {
      $statuses = array(QUOTE_STATUS_SENT, QUOTE_STATUS_WON, QUOTE_STATUS_LOST);
      if (Quotes::canManage($user)) {
        $statuses[] = QUOTE_STATUS_DRAFT;
      } // if

      return $statuses;
    } // getVisibleStatuses

    /**
     * Get id-name map of companies. This method is specific because it also adds client information
     * from companies that haven't been saved yet
     */
    static function getCompaniesIdNameMap(User $user) {
      $companies = Companies::getIdNameMap($user->visibleCompanyIds());
      $nonexisting_companies = DB::execute("SELECT DISTINCT company_name FROM ".TABLE_PREFIX."quotes WHERE company_id = 0");
      if (is_foreachable($nonexisting_companies)) {
        foreach ($nonexisting_companies as $key => $value) {
          if (trim($value['company_name'] !== "" && !is_null($value['company_name']))) {
            $companies[Inflector::slug($value['company_name'])] = $value['company_name'];
          } // if
        } // foreach
      } // if

      return $companies;
    } // getCompaniesIdNameMap
    
    /**
     * Return number of quote drafts
     *
     * @param void
     * @return integer
     */
    function countDrafts() {
      return Quotes::count(array('status = ?', QUOTE_STATUS_DRAFT));
    } // countDrafts

    /**
     * Count quotes by company
     *
     * @param Company $company
     * @param User $user
     * @param array $statuses
     * @return integer
     */
    function countByCompany(&$company, User $user, $statuses = null) {
      if (is_null($statuses)) {
        $statuses = self::getVisibleStatuses($user);
      } // if

      return Quotes::count(array('company_id = ? AND status IN (?)', $company->getId(), $statuses));
    } // countByCompany

    /**
     * Return quotes by company
     *
     * @param Company $company
     * @param User $user
     * @param array $statuses
     * @Param string $order_by
     * @return array
     */
    static function findByCompany(Company $company, User $user, $statuses = null, $order_by = 'created_on') {
      if (is_null($statuses)) {
        $statuses = self::getVisibleStatuses($user);
      } // if

      return Quotes::find(array(
        'conditions' => array('company_id = ? AND status IN (?)', $company->getId(), $statuses),
        'order' => $order_by,
      ));
    } // findByCompany

    /**
     * Find quotes for object list
     *
     * @param User $user
     * @param Company $company
     * @return array
     */
    function findForObjectsList(User $user, $company = null) {
      $statuses = self::getVisibleStatuses($user);

      if ($company instanceof Company) {
        $quote_url = Router::assemble('people_company_quote', array('company_id' => '--COMPANYID--', 'quote_id' => '--QUOTEID--'));
        $quotes = DB::execute('SELECT id, name, status, company_name FROM ' . TABLE_PREFIX . 'quotes WHERE company_id = ? AND status IN (?)', $company->getId(), $statuses);
      } else {
        $quote_url = Router::assemble('quote', array('quote_id' => '--QUOTEID--'));
        $quotes = DB::execute('SELECT id, name, status, company_id, company_name FROM ' . TABLE_PREFIX . 'quotes WHERE status IN (?)', $statuses);
      } // if

      $result = array();

      if (is_foreachable($quotes)) {
        foreach ($quotes as $quote) {
          $result[] = array(
            'id' => $quote['id'],
            'name' => $quote['name'],
            'status' => $quote['status'],
            'company_id' => isset($quote['company_id']) && $quote['company_id'] > 0 ? $quote['company_id'] : Inflector::slug($quote['company_name']),
            'permalink' => $company instanceof Company ? str_replace(array('--COMPANYID--', '--QUOTEID--'), array($company->getId(), $quote['id']), $quote_url) : str_replace('--QUOTEID--', $quote['id'], $quote_url)
          );
        } // foreach
      } // if

      return $result;
    } // findForObjectsList
    
    /**
     * Find quotes for phone list view
     *
     * @param User $user
     * @param Company $company
     * @return array
     */
    function findForPhoneList(User $user, $company = null) {
      $statuses = self::getVisibleStatuses($user);
      
      $quotes_table = TABLE_PREFIX . 'quotes';

      // exclude draft quotes from company quotes
      if ($company instanceof Company) {
        $quotes = DB::execute("SELECT id, name, status FROM $quotes_table WHERE company_id = ? AND status IN (?) ORDER BY status, id", $company->getId(), $statuses);
        $view_quote_url_template = Router::assemble('people_company_quote', array('company_id' => '--COMPANYID--', 'quote_id' => '--QUOTEID--'));
      } else {
        $quotes = DB::execute("SELECT id, name, status FROM $quotes_table WHERE status IN (?) ORDER BY status, id", $statuses);
        $view_quote_url_template = Router::assemble('quote', array('quote_id' => '--QUOTEID--'));
      } // if
      
      $result = array();

      if(is_foreachable($quotes)) {
        foreach($quotes as $quote) {
          $result[$quote['status']][] = array(
            'name' => $quote['name'],
            'permalink' => $company instanceof Company ? str_replace(array('--COMPANYID--', '--QUOTEID--'), array($company->getId(), $quote['id']), $view_quote_url_template) : str_replace('--QUOTEID--', $quote['id'], $view_quote_url_template)
          );
        } // foreach
      } // if

      return $result;
    } // findForPhoneList
  
    /**
     * Find quotes for printing by grouping and filtering criteria
     *
     * @param User $user
     * @param Company $company
     * @param string $group_by
     * @param array $filter_by
     * @return DBResult
     */
    public function findForPrint(User $user, $company = null, $group_by = null, $filter_by = null) {
      
      if (!in_array($group_by, array('company_id', 'status'))) {
        $group_by = null;
      } // if

      // filter by status
      $filter_type = array_var($filter_by, 'status', null);
      if ($filter_type === '0' && Quotes::canManage($user)) {
        $conditions[] = DB::prepare('(status=?)', array(QUOTE_STATUS_DRAFT));
      } else if ($filter_type === '1') {
        $conditions[] = DB::prepare('(status=?)', array(QUOTE_STATUS_SENT));
      } else if ($filter_type === '2') {
        $conditions[] = DB::prepare('(status=?)', array(QUOTE_STATUS_WON));
      } else if ($filter_type === '3') {
        $conditions[] = DB::prepare('(status=?)', array(QUOTE_STATUS_LOST));
      } else {
        $conditions[] = DB::prepare('status IN (?)', array(self::getVisibleStatuses($user)));
      } // if

      if ($company instanceof Company) {
        $conditions[] = DB::prepare('company_id = ?', array($company->getId()));
      } // if
      
      // do find tasks
      $quotes = self::find(array(
        'conditions' => implode(' AND ', $conditions),
        'order' => $group_by ? $group_by : 'id DESC'
      ));

      return $quotes;
    } // findForPrint
    
  }