<?php
class annualService 
{
    public $users;
    public $dao;

    /**
     * Init Dao. 
     *
     * @access public
     * @return void
     */
    public function __construct()
    {
        global $app;
        $this->dao = $app->loadClass('dao');
    }

    /**
     * Init user list.
     *
     * @param  string $account
     * @param  int    $year
     * @param  object $pager
     * @access public
     * @return void
     */
    public function initUsers($account = null, $year = null, $pager = null)
    {
        $users = $this->dao->select('t1.account, realname')->from(TABLE_USER)->alias('t1')
            ->leftJoin(TABLE_ANNUAL)->alias('t2')->on('t1.account = t2.account')
            ->where('t1.deleted')->eq('0')
            ->andWhere('t1.locked')->lt('2199-12-31')
            ->andWhere('t2.id')->ne('NULL')
            ->beginIF($account)->andWhere('t1.account')->eq($account)->fi()
            ->beginIF($year)->andWhere('t2.year')->eq($year)->fi()
            ->orderBy('t1.id')
            ->page($pager)
            ->fetchAll('account');

        $userIDs = array_keys($users);
        $annuals = $this->dao->select('*')->from(TABLE_ANNUAL)->where('account')->in($userIDs)->fetchGroup('account');
        foreach($users as $user)
        {
            $userAnnuals           = zget($annuals, $user->account, array());
            $user->annualLeaves    = array();
            $user->oldAnnualLeaves = array();
            foreach($userAnnuals as $annual)
            {
                $user->annualLeaves[$annual->year]    = object2Array($annual);
                $user->oldAnnualLeaves[$annual->year] = object2Array($annual);
            }
        }

        $this->users = array_values($users);
        return $this;
    }

    /**
     * Deduct the time in wait leave. 
     *
     * @param  int    $leaveID
     * @access public
     * @return void
     */
    public function deductionWait($leaveID = null)
    {
        $waitAnnuals = $this->dao->select('begin, hours, createdBy')->from(TABLE_LEAVE)
            ->where('type')->eq('annual')
            ->andWhere('status')->in(array('wait', 'doing'))
            ->beginIF($leaveID)->andWhere('id')->ne($leaveID)->fi()
            ->orderBy('id')
            ->fetchGroup('createdBy');
        $users = $this->users;
        foreach($users as $user)
        {
            $userWaitAnnuals = zget($waitAnnuals, $user->account, array());
            foreach($userWaitAnnuals as $userWaitAnnual)
            {
                $user->annualLeaves = $this->deductionInOrder($user->annualLeaves, $userWaitAnnual->begin, $userWaitAnnual->hours);
            }
        }
        $this->users = $users;
        return $this;
    }

    /**
     * Deduct the time in wait conversion.
     *
     * @param  int    $conversionID
     * @access public
     * @return void
     */
    public function deductionConversion($conversionID = null)
    {
        $conversions = $this->dao->select('year, days, createdBy')->from(TABLE_CONVERSION)
            ->where('status')->eq('wait')
            ->beginIF($conversionID)->andWhere('id')->ne($conversionID)->fi()
            ->orderBy('id')
            ->fetchGroup('createdBy');
        $users = $this->users;
        foreach($users as $user)
        {
            $userConversions = zget($conversions, $user->account, array());
            foreach($userConversions as $conversion)
            {
                $user->annualLeaves = $this->deductionInYear($user->annualLeaves, $conversion->year, $conversion->days);
            }
        }
        $this->users = $users;
        return $this;
    }

    /**
     * Deduct the balance in sequence for someone.
     *
     * @param  string $date 
     * @param  int    $days 
     * @access public
     * @return void
     */
    public function deductionInOrderForOne($date, $days)
    {
        $this->users[0]->annualLeaves = $this->deductionInOrder($this->users[0]->annualLeaves, $date, $days);
        return $this;
    }

    /**
     * Deduct the balance in year for someone.
     *
     * @param  int    $year
     * @param  int    $days
     * @access public
     * @return void
     */
    public function deductionInYearForOne($year, $days)
    {
        $this->users[0]->annualLeaves = $this->deductionInYear($this->users[0]->annualLeaves, $year, $days);
        return $this;
    }

    /**
     * Hide out of validity data.
     *
     * @param  string $date
     * @access public
     * @return void
     */
    public function hideOutDate($date = '')
    {
        if(!$date) $date = date('Y-m-d');
        $year  = date('Y', strtotime($date));
        $day   = date('md', strtotime($date));
        $users = $this->users;

        foreach($users as $user)
        {
            foreach($user->annualLeaves as $annualYear => $annualLeave)
            {
                $availableYears = array($year);
                if($day < '0301') $availableYears[] = $year - 1;

                if(!in_array($annualYear, $availableYears))
                {
                    unset($user->annualLeaves[$annualYear]);
                    continue;
                }
            }
        }
        $this->users = $users;
        return $this;
    }

    /**
     * Hide data without balance.
     *
     * @access public
     * @return void
     */
    public function hideNoBalance()
    {
        $users = $this->users;

        foreach($users as $user)
        {
            foreach($user->annualLeaves as $annualYear => $annualLeave)
            {
                if($annualLeave['times'] <= 0 || $annualLeave['balance'] <= 0)
                {
                    unset($user->annualLeaves[$annualYear]);
                    continue;
                }
                
            }
        }
        $this->users = $users;
        return $this;
    }

    /**
     * Get the list.
     *
     * @access public
     * @return void
     */
    public function getList()
    {
        return $this->users;
    }

    /**
     * Get the first one.
     *
     * @access public
     * @return void
     */
    public function getFirst()
    {
        return isset($this->users[0]) ? $this->users[0] : array();
    }

    /**
     * Save balance.
     *
     * @access public
     * @return array
     */
    public function saveBalance()
    {
        $user    = $this->getFirst();
        $annuals = $user ? $user->annualLeaves : array();
        if(empty($annuals)) return false;

        $oldAnnuals = $user->oldAnnualLeaves; 
        $changes    = array();
        foreach($annuals as $year => $annual)
        {
            $change = commonModel::createChanges(array2Object($oldAnnuals[$year]), array2Object($annual));
            $this->dao->update(TABLE_ANNUAL)->set('balance')->eq($annual['balance'])->set('times')->eq($annual['times'])->where('id')->eq($annual['id'])->exec();
            $changes[$annual['id']] = $change;
        }
        return $changes;
    }

    /**
     * Check the first one balance.
     * 
     * @access public
     * @return bool
     */
    public function checkBalance()
    {
        $user = $this->users[0];
        if(!$user) return false;
        $balance = $user->annualLeaves;
        if(!$balance) return false;
        if(end($balance)['balance'] < 0 || end($balance)['times'] < 0) return false;
        return true;
    }

    /**
     * Check the first one balance in year.
     *
     * @param  int    $year
     * @access public
     * @return void
     */
    public function checkBalanceYear($year)
    {
        $user = $this->users[0];
        if(!$user) return false;
        $balance = $user->annualLeaves;
        if(!$balance) return false;
        if(!isset($balance[$year])) return false;
        if($balance[$year]['balance'] < 0 || $balance[$year]['times'] < 0) return false;                                                                               
        return true;   
    }

    /**
     * Deduct the balance in sequence.
     *
     * @param  array  $balance
     * @param  string $date
     * @param  int    $days
     * @access private
     * @return array
     */
    private function deductionInOrder($balance, $date, $days)
    {
        $year  = substr($date, 0, 4);
        $month = substr($date, 5, 2);
        $day   = substr($date, 8, 2);

        if($month . $day < '0301')
        {
            $balanceDiff = 0;
            if(isset($balance[$year - 1]) && $balance[$year - 1]['balance'] > 0 && $balance[$year - 1]['times'] > 0)
            {
                $balance[$year - 1]['times']--;
                $balance[$year - 1]['balance'] -= $days;
                if($balance[$year - 1]['balance'] < 0)
                {
                    $balanceDiff = -$balance[$year - 1]['balance'];
                    $balance[$year - 1]['balance'] = 0;
                    if($balanceDiff)
                    {
                        if(!isset($balance[$year]))
                        {
                            $balance[$year]['times']   = 0;
                            $balance[$year]['balance'] = -$balanceDiff;
                        }
                        else
                        {
                            $balance[$year]['times']--;
                            $balance[$year]['balance'] -= $balanceDiff;
                        }
                    }
                }
            }
            else
            {
                if(isset($balance[$year]))
                {
                    $balance[$year]['times']--;
                    $balance[$year]['balance'] -= $days;
                }
            }
        }
        else
        {
            if(isset($balance[$year]))
            {
                $balance[$year]['times']--;
                $balance[$year]['balance'] -= $days;
            }
        }
        ksort($balance);
        return $balance;
    }

    /**
     * Deduct the balance of the specified year.
     *
     * @param  array  $balance
     * @param  int    $year
     * @param  int    $days
     * @access private
     * @return array
     */
    private function deductionInYear($balance, $year, $days)
    {
        if(isset($balance[$year]))
        {
            $balance[$year]['balance'] -= $days;
        }
        else
        {
            $balance[$year]['balance'] = -$days;
        }

        ksort($balance);
        return $balance;
    }
}
