<?php

/**
 * @package     ARTIO Booking
 * @subpackage  models
 * @copyright   Copyright (C) 2010 ARTIO s.r.o.. All rights reserved.
 * @author      ARTIO s.r.o., http://www.artio.net
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 * @link        http://www.artio.net Official website
 */

defined('_JEXEC') or die('Restricted access');

class BookingModelDiscounts extends JModelLegacy {
    /**
     * @var int
     */
    protected $capacity;
    /**
     * @var array
     */
    protected $firstPrice;
    /**
     * @var TablePrice
     */
    protected $firstPriceDepositPercent;
    /**
     * @var int
     */
    protected $firstPriceDepositType;
    /**
     * @var string
     */
    protected $from;
    /**
     * @var BookingInterval
     */
    protected $interval;
    /**
     * @var TablePrice[]
     */
    protected $prices;
    /**
     * @var TableSubject
     */
    protected $subject;
    /**
     * @var TableSupplement[]
     */
    protected $supplements;
    /**
     * @var int
     */
    protected $units;
    /**
     * @var int[]
     */
    protected $usedPrices;

    public function __construct($config = array()) {
        $this->setProperties($config);
        parent::__construct($config);
    }

    /**
     * Get discount model instance.
     * @param array $discount
     * @return BookingModelDiscount
     */
    public function getDiscount($discount) {
        return JModelLegacy::getInstance('Discount', 'BookingModel', $discount);
    }

    /**
     * Apply discounts to reservation items.
     */
    public function apply() {
        $this->interval->supplementsDiscount = 0;
        foreach ($this->supplements as $supplement) {
            $this->interval->supplementsDiscount += $supplement->discount;
        }
        foreach ($this->interval->calendar->prices as $type) {
            foreach ($type->prices as $price) {
                if (!empty($this->subject->volume_discount)) {
                    $this->applySingle($price);
                } else {
                    $this->applyPrice($price);
                }
            }
        }
        $this->applyEarly();
        // prevent negative discounts
        if ($this->interval->discount > $this->interval->price) {
            $this->interval->discount = $this->interval->price;
        }
        if ($this->interval->supplementsDiscount > $this->interval->supplementsFullPrice) {
            $this->interval->supplementsDiscount = $this->interval->supplementsFullPrice;
        }
        foreach ($this->supplements as $supplement) {
            if ($supplement->discount > $supplement->fullPrice) {
                $supplement->discount = $supplement->fullPrice;
            }
        }
        if ($this->interval->fullDiscount > $this->interval->fullPrice) {
            $this->interval->fullDiscount = $this->interval->fullPrice;
        }
        // calculate discount totals
        $this->interval->fullDiscount += $this->interval->supplementsDiscount;
        $this->interval->fullPrice -= $this->interval->fullDiscount;
        $this->interval->fullPriceSupplements = $this->interval->fullPrice + $this->interval->supplementsFullPrice;
    }

    /**
     * Apply volume discount to single supplement.
     * @param $supplement
     */
    public function applySupplement($supplement) {
        $discount = $this->getAvailable($supplement->volume_discount);
        $supplement->discount = $discount ? $discount->getDiscount($supplement->fullPrice) : 0;
    }

    /**
     * Apply permanent volume discount to reservation.
     */
    public function applySingle($price) {
        $price->volume_discount = $this->subject->volume_discount;
        $price->discount_include_supplements = $this->subject->discount_include_supplements;
        $price->discount_all_units = $this->subject->discount_all_units;
        $this->applyPrice($price);
    }

    /**
     * Apply volume discount to price.
     * @param $price
     */
    public function applyPrice($price) {
        if (isset($this->usedPrices[$price->id])) {

            $units = $this->usedPrices[$price->id];
            $quantity = $this->getQuantity($price);
            $discount = $this->getAvailable($price->volume_discount, $units);

            if ($discount) {
                $total = $price->discount_all_units ? $units : $units - $discount->count + 1;
                if ($discount->isValueType()) { // fixed discount amount
                    if ($discount->isPerUnit()) { // discount per item
                        $this->interval->discount += $total * $discount->value;
                        $this->interval->fullDiscount += $total * $discount->value * $quantity;
                    } else { // discount per reservation
                        $this->interval->discount += $discount->value;
                        $this->interval->fullDiscount += $discount->value;
                    }
                } else { // percentage discount amount
                    if ($discount->isPerUnit()) { // discount per item
                        $this->interval->discount += $total * $discount->getPercentage($price->value);
                        $this->interval->fullDiscount += $total * $discount->getPercentage($price->value) * $quantity;
                    } else { // discount per reservation
                        $this->interval->discount += $discount->getPercentage($this->interval->price);
                        $this->interval->fullDiscount += $discount->getPercentage($this->interval->fullPrice);

                        $this->includeSupplements($price, $discount);
                    }
                }
            }
        }
    }

    /**
     * Apply early booking discount.
     */
    public function applyEarly() {
        if ($this->subject->early_booking_discount) {
            $available = null;
            $diff = JFactory::getDate()->diff(JFactory::getDate($this->from));
            /* @var $diff DateInterval */
            foreach ($this->subject->early_booking_discount as $discount) {
                $discount = $this->getDiscount($discount);
                if ($diff->days + 1 >= $discount->count) {
                    $available = $discount;
                }
            }
            if ($available) {
                $this->interval->discount += $discount->getDiscount($this->interval->price);
                $this->interval->fullDiscount += $discount->getDiscount($this->interval->fullPrice);
            }
        }
    }

    /**
     * Apply member discount.
     * @param $price
     */
    public function applyMember(&$price) {
        $groups = JFactory::getUser()->getAuthorisedGroups();
        $value = 0;
        foreach ((array)$this->subject->member_discount as $group => $discount) {
            $discount = $this->getDiscount($discount);
            if (in_array($group, $groups)) { // for all customer groups
                $value += $discount->getDiscount($price);
            }
        }
        $price -= $value;
    }

    /**
     * Get quantity according to configuration.
     * @param $price
     * @return int
     */
    public function getQuantity($price) {
        return $price->price_capacity_multiply ? $this->capacity : 1;
    }

    /**
     * Get available volume discount for number of units or number of quantity.
     * @param array[] $discounts
     * @param int $units
     * @return BookingModelDiscount
     */
    public function getAvailable($discounts, $units = null) {
        $available = null;
        $units = $units === null ? $this->units : $units;
        foreach ($discounts as $discount) {
            $discount = $this->getDiscount($discount);
            if ($discount->isAvailable($units, $this->capacity)) {
                $available = $discount;
            }
        }
        return $available;
    }

    /**
     * Include supplements price into discount. Apply item percentage discount to discounted supplement price.
     * @param TablePrice $item
     * @param BookingModelDiscount $discount
     */
    public function includeSupplements($item, $discount) {
        if ($item->discount_include_supplements && $this->supplements) {
            $this->interval->supplementsDiscount += $discount->getPercentage($this->interval->supplementsFullPrice - $this->interval->supplementsDiscount);
            foreach ($this->supplements as $supplement) {
                if ($supplement->value) {
                    $supplement->discount += $discount->getPercentage($supplement->fullPrice - $supplement->discount);
                }
            }
        }
    }
}