<?php
/*
* 2007-2017 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
*  @author PrestaShop SA <contact@prestashop.com>
*  @copyright  2007-2017 PrestaShop SA
*  @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
*  International Registered Trademark & Property of PrestaShop SA
*/

class ProductControllerCore extends FrontController
{
    public $php_self = 'product';

    /** @var Product */
    protected $product;

    /** @var Category */
    protected $category;

    public function setMedia()
    {
        parent::setMedia();
        if (count($this->errors)) {
            return;
        }

        if (!$this->useMobileTheme()) {
            $this->addCSS(_THEME_CSS_DIR_.'product.css');
            $this->addCSS(_THEME_CSS_DIR_.'print.css', 'print');
            $this->addJqueryPlugin(array('fancybox', 'idTabs', 'scrollTo', 'serialScroll', 'bxslider'));

            $this->addJS(array(
                _THEME_JS_DIR_.'tools.js',  // retro compat themes 1.5
                _THEME_JS_DIR_.'product.js',
            ));
        } else {
            $this->addJqueryPlugin(array('scrollTo', 'serialScroll'));
            $this->addJS(array(
                _THEME_JS_DIR_.'tools.js',  // retro compat themes 1.5
                _THEME_MOBILE_JS_DIR_.'product.js',
                _THEME_MOBILE_JS_DIR_.'jquery.touch-gallery.js'
            ));
        }

        $this->addCSS(_THEME_CSS_DIR_.'occupancy.css');
        $this->addJS(_THEME_JS_DIR_.'occupancy.js');

        if (Configuration::get('PS_DISPLAY_JQZOOM') == 1) {
            $this->addJqueryPlugin('jqzoom');
        }

        if (($PS_API_KEY = Configuration::get('PS_API_KEY'))) {
            $objHotelRoomType = new HotelRoomType();
            $roomTypeInfo = $objHotelRoomType->getRoomTypeInfoByIdProduct($this->product->id);
            if ($roomTypeInfo) {
                $objHotelBranchInformation = new HotelBranchInformation($roomTypeInfo['id_hotel']);
                if (floatval($objHotelBranchInformation->latitude) != 0
                    && floatval($objHotelBranchInformation->longitude) != 0
                ) {
                    Media::addJsDef(array(
                        'hotel_location' => array(
                            'latitude' => $objHotelBranchInformation->latitude,
                            'longitude' => $objHotelBranchInformation->longitude,
                        )
                    ));

                    $this->addJS(
                        'https://maps.googleapis.com/maps/api/js?key='.$PS_API_KEY.'&libraries=places&language='.
                        $this->context->language->iso_code.'&region='.$this->context->country->iso_code
                    );
                }
            }
        }
    }

    public function canonicalRedirection($canonical_url = '')
    {
        if (Tools::getValue('live_edit')) {
            return;
        }
        if (Validate::isLoadedObject($this->product)) {
            parent::canonicalRedirection($this->context->link->getProductLink($this->product));
        }
    }

    /**
     * Initialize product controller
     * @see FrontController::init()
     */
    public function init()
    {
        // validate dates if available
        $dateFrom = Tools::getValue('date_from');
        $dateTo = Tools::getValue('date_to');

        $currentTimestamp = strtotime(date('Y-m-d'));
        $dateFromTimestamp = strtotime($dateFrom);
        $dateToTimestamp = strtotime($dateTo);

        if ($dateFrom != '' && ($dateFromTimestamp === false || ($dateFromTimestamp < $currentTimestamp))) {
            Tools::redirect($this->context->link->getPageLink('pagenotfound'));
        }

        if ($dateTo != '' && ($dateToTimestamp === false || ($dateToTimestamp < $currentTimestamp))) {
            Tools::redirect($this->context->link->getPageLink('pagenotfound'));
        }

        parent::init();

        if ($id_product = (int) Tools::getValue('id_product')) {
            $this->product = new Product($id_product, true, $this->context->language->id, $this->context->shop->id);
        }

        if (!$this->product->booking_product || ($this->product->booking_product && !$this->product->show_at_front)) {
            Tools::redirect($this->context->link->getPageLink('pagenotfound'));
        }

        if (!Validate::isLoadedObject($this->product)) {
            header('HTTP/1.1 404 Not Found');
            header('Status: 404 Not Found');
            $this->errors[] = Tools::displayError('Product not found');
        } else {
            $this->canonicalRedirection();
            /*
            * If the product is associated to the shop
            * and is active or not active but preview mode (need token + file_exists)
             * allow showing the product
             * In all the others cases => 404 "Product is no longer available"
             */
            if (!$this->product->isAssociatedToShop() || !$this->product->active) {
                if (Tools::getValue('adtoken') == Tools::getAdminToken('AdminProducts'.(int)Tab::getIdFromClassName('AdminProducts').(int)Tools::getValue('id_employee')) && $this->product->isAssociatedToShop()) {
                    // If the product is not active, it's the admin preview mode
                    $this->context->smarty->assign('adminActionDisplay', true);
                } else {
                    $this->context->smarty->assign('adminActionDisplay', false);
                    if (!$this->product->id_product_redirected || $this->product->id_product_redirected == $this->product->id) {
                        $this->product->redirect_type = '404';
                    }

                    switch ($this->product->redirect_type) {
                        case '301':
                            header('HTTP/1.1 301 Moved Permanently');
                            header('Location: '.$this->context->link->getProductLink($this->product->id_product_redirected));
                            exit;
                        break;
                        case '302':
                            header('HTTP/1.1 302 Moved Temporarily');
                            header('Cache-Control: no-cache');
                            header('Location: '.$this->context->link->getProductLink($this->product->id_product_redirected));
                            exit;
                        break;
                        case '404':
                        default:
                            header('HTTP/1.1 404 Not Found');
                            header('Status: 404 Not Found');
                            $this->errors[] = Tools::displayError('This Room Type is no longer available.');
                        break;
                    }
                }
            } elseif (!$this->product->checkAccess(
                isset($this->context->customer->id)
                && $this->context->customer->id ? (int)$this->context->customer->id : 0)
            ) {
                header('HTTP/1.1 403 Forbidden');
                header('Status: 403 Forbidden');
                $this->errors[] = Tools::displayError('You do not have access to this Room Type.');
            } else {
                // Load category
                $id_category = false;
                if (isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER'] == Tools::secureReferrer($_SERVER['HTTP_REFERER']) // Assure us the previous page was one of the shop
                    && preg_match('~^.*(?<!\/content)\/([0-9]+)\-(.*[^\.])|(.*)id_(category|product)=([0-9]+)(.*)$~', $_SERVER['HTTP_REFERER'], $regs)) {
                    // If the previous page was a category and is a parent category of the product use this category as parent category
                    $id_object = false;
                    if (isset($regs[1]) && is_numeric($regs[1])) {
                        $id_object = (int)$regs[1];
                    } elseif (isset($regs[5]) && is_numeric($regs[5])) {
                        $id_object = (int)$regs[5];
                    }
                    if ($id_object) {
                        $referers = array($_SERVER['HTTP_REFERER'],urldecode($_SERVER['HTTP_REFERER']));
                        if (in_array($this->context->link->getCategoryLink($id_object), $referers)) {
                            $id_category = (int)$id_object;
                        } elseif (isset($this->context->cookie->last_visited_category) && (int)$this->context->cookie->last_visited_category && in_array($this->context->link->getProductLink($id_object), $referers)) {
                            $id_category = (int)$this->context->cookie->last_visited_category;
                        }
                    }
                }
                if (!$id_category || !Category::inShopStatic($id_category, $this->context->shop) || !Product::idIsOnCategoryId((int)$this->product->id, array('0' => array('id_category' => $id_category)))) {
                    $id_category = (int)$this->product->id_category_default;
                }
                $this->category = new Category((int)$id_category, (int)$this->context->cookie->id_lang);
                if (isset($this->context->cookie) && isset($this->category->id_category)) {
                    $this->context->cookie->last_visited_category = (int)$this->category->id_category;
                }
            }
        }
    }

    /**
     * Assign template vars related to page content
     * @see FrontController::initContent()
     */
    public function initContent()
    {
        parent::initContent();

        if (!$this->errors) {
            if (Pack::isPack((int)$this->product->id) && !Pack::isInStock((int)$this->product->id)) {
                $this->product->quantity = 0;
            }

            $this->product->description = $this->transformDescriptionWithImg($this->product->description);

            // Assign to the template the id of the virtual product. "0" if the product is not downloadable.
            $this->context->smarty->assign('virtual', ProductDownload::getIdFromIdProduct((int)$this->product->id));

            $this->context->smarty->assign('customizationFormTarget', Tools::safeOutput(urldecode($_SERVER['REQUEST_URI'])));

            if (Tools::isSubmit('submitCustomizedDatas')) {
                // If cart has not been saved, we need to do it so that customization fields can have an id_cart
                // We check that the cookie exists first to avoid ghost carts
                if (!$this->context->cart->id && isset($_COOKIE[$this->context->cookie->getName()])) {
                    $this->context->cart->add();
                    $this->context->cookie->id_cart = (int)$this->context->cart->id;
                }
                $this->pictureUpload();
                $this->textRecord();
                $this->formTargetFormat();
            } elseif (Tools::getIsset('deletePicture') && !$this->context->cart->deleteCustomizationToProduct($this->product->id, Tools::getValue('deletePicture'))) {
                $this->errors[] = Tools::displayError('An error occurred while deleting the selected picture.');
            }

            $pictures = array();
            $text_fields = array();
            if ($this->product->customizable) {
                $files = $this->context->cart->getProductCustomization($this->product->id, Product::CUSTOMIZE_FILE, true);
                foreach ($files as $file) {
                    $pictures['pictures_'.$this->product->id.'_'.$file['index']] = $file['value'];
                }

                $texts = $this->context->cart->getProductCustomization($this->product->id, Product::CUSTOMIZE_TEXTFIELD, true);

                foreach ($texts as $text_field) {
                    $text_fields['textFields_'.$this->product->id.'_'.$text_field['index']] = str_replace('<br />', "\n", $text_field['value']);
                }
            }

            $this->context->smarty->assign(array(
                'pictures' => $pictures,
                'textFields' => $text_fields));

            $this->product->customization_required = false;
            $customization_fields = $this->product->customizable ? $this->product->getCustomizationFields($this->context->language->id) : false;
            if (is_array($customization_fields)) {
                foreach ($customization_fields as $customization_field) {
                    if ($this->product->customization_required = $customization_field['required']) {
                        break;
                    }
                }
            }

            // Assign template vars related to the category + execute hooks related to the category
            $this->assignCategory();
            // Assign template vars related to the price and tax
            $this->assignPriceAndTax();

            // Assign template vars related to the images
            $this->assignImages();
            // Assign attribute groups to the template
            $this->assignAttributesGroups();

            // Assign attributes combinations to the template
            $this->assignAttributesCombinations();

            // Pack management
            $pack_items = Pack::isPack($this->product->id) ? Pack::getItemTable($this->product->id, $this->context->language->id, true) : array();
            $this->context->smarty->assign('packItems', $pack_items);
            $this->context->smarty->assign('packs', Pack::getPacksTable($this->product->id, $this->context->language->id, true, 1));

            if (isset($this->category->id) && $this->category->id) {
                $return_link = Tools::safeOutput($this->context->link->getCategoryLink($this->category));
            } else {
                $return_link = 'javascript: history.back();';
            }

            $accessories = $this->product->getAccessories($this->context->language->id);
            if ($this->product->cache_is_pack || (is_array($accessories) && count($accessories))) {
                $this->context->controller->addCSS(_THEME_CSS_DIR_.'product_list.css');
            }
            if ($this->product->customizable) {
                $customization_datas = $this->context->cart->getProductCustomization($this->product->id, null, true);
            }

            $useTax = HotelBookingDetail::useTax();
            // Check if product is a booking product or a service product
            // Virtual product is booking product
            if ($this->product->booking_product) {
                #####################################################################
                /*By webkul To send All needed Hotel Information on product.tpl*/
                #####################################################################

                    $htl_features = array();
                $obj_hotel_room_type = new HotelRoomType();
                $room_info_by_product_id = $obj_hotel_room_type->getRoomTypeInfoByIdProduct($this->product->id);
                if ($hotel_id = $room_info_by_product_id['id_hotel']) {
                    $obj_hotel_branch = new HotelBranchInformation();
                    $hotel_info_by_id = $obj_hotel_branch->hotelBranchesInfo(false, 2, 1, $hotel_id);
                    $hotel_policies = $hotel_info_by_id['policies'];
                    $hotel_name = $hotel_info_by_id['hotel_name'];

                    $addressInfo = HotelBranchInformation::getAddress($room_info_by_product_id['id_hotel']);
                    $hotel_location = $addressInfo['city'].
                    ($addressInfo['id_state']?', '.$addressInfo['state']:'').', '.$addressInfo['country'];

                    $obj_hotel_feaures_ids = $obj_hotel_branch->getFeaturesOfHotelByHotelId($hotel_id);

                    if (isset($obj_hotel_feaures_ids) && $obj_hotel_feaures_ids) {
                        foreach ($obj_hotel_feaures_ids as $key => $value) {
                            $obj_htl_ftr = new HotelFeatures();
                            $htl_info = $obj_htl_ftr->getFeatureInfoById($value['feature_id']);
                            $htl_features[] = $htl_info['name'];
                        }
                    }
                    $date_from = Tools::getValue('date_from');
                    $date_to = Tools::getValue('date_to');

                    if (!($date_from = Tools::getValue('date_from'))) {
                        $date_from = date('Y-m-d');
                        // set date to according to los
                        $objHotelRoomTypeRestrictionDateRange = new HotelRoomTypeRestrictionDateRange();
                        $los = $objHotelRoomTypeRestrictionDateRange->getRoomTypeLengthOfStay($this->product->id, $date_from);
                        $date_to = date('Y-m-d', strtotime('+'.$los['min_los'].' day', strtotime($date_from)));
                    }
                    if (!($date_to = Tools::getValue('date_to'))) {
                        // set date to according to los
                        $objHotelRoomTypeRestrictionDateRange = new HotelRoomTypeRestrictionDateRange();
                        $los = $objHotelRoomTypeRestrictionDateRange->getRoomTypeLengthOfStay($this->product->id, $date_from);
                        $date_to = date('Y-m-d', strtotime('+'.$los['min_los'].' day', strtotime($date_from)));
                    }

                    $hotel_branch_obj = new HotelBranchInformation($hotel_id);
                    /*Max date of ordering for order restrict*/
                    $order_date_restrict = false;
                    $max_order_date = HotelOrderRestrictDate::getMaxOrderDate($hotel_id);
                    if ($max_order_date) {
                        $max_order_date = date('Y-m-d', strtotime($max_order_date));
                        if (strtotime('-1 day', strtotime($max_order_date)) < strtotime($date_from)
                            || strtotime($max_order_date) < strtotime($date_to)
                        ) {
                            $order_date_restrict = true;
                        }
                    }
                    /*End*/
                    // booking preparation time
                    $preparationTime = (int) HotelOrderRestrictDate::getPreparationTime($hotel_id);

                    $objHotelImage = new HotelImage();
                    $hotelImageLink = null;
                    if ($coverImage = HotelImage::getCover($hotel_id)) {
                        $hotelImagesBaseDir = _MODULE_DIR_.'hotelreservationsystem/views/img/hotel_img/';
                        $hotelImageLink = $this->context->link->getMediaLink(
                            $objHotelImage->getImageLink($coverImage['id'], ImageType::getFormatedName('large'))
                        );
                    }

                    // get room type additional demands
                    $objRoomDemands = new HotelRoomTypeDemand();
                    if ($roomTypeDemands = $objRoomDemands->getRoomTypeDemands($this->product->id)) {
                        foreach ($roomTypeDemands as &$demand) {
                            // if demand has advance options then set demand price as first advance option price.
                            if (isset($demand['adv_option']) && $demand['adv_option']) {
                                $demand['price'] = current($demand['adv_option'])['price'];
                            }
                        }
                    }

                    $this->context->smarty->assign(
                        array(
                            'id_hotel' => $hotel_id,
                            'room_type_demands' => $roomTypeDemands,
                            'room_type_info' => $room_info_by_product_id,
                            'isHotelRefundable' => $hotel_branch_obj->isRefundable(),
                            'max_order_date' => $max_order_date,
                            'preparation_time' => $preparationTime,
                            'warning_num' => Configuration::get('WK_ROOM_LEFT_WARNING_NUMBER'),
                            'ratting_img_path' => _MODULE_DIR_.'hotelreservationsystem/views/img/Slices/icons-sprite.png',
                            'product_controller_url' => $this->context->link->getPageLink('product'),
                            'date_from' => $date_from,
                            'date_to' => $date_to,
                            'hotel_check_in' => date('h:i a', strtotime($hotel_branch_obj->check_in)),
                            'hotel_check_out' => date('h:i a', strtotime($hotel_branch_obj->check_out)),
                            'hotel_location' => $hotel_location,
                            'hotel_latitude' => $hotel_branch_obj->latitude,
                            'hotel_longitude' => $hotel_branch_obj->longitude,
                            'hotel_map_input_text' => $hotel_branch_obj->map_input_text,
                            'hotel_address1' => $addressInfo['address1'],
                            'hotel_phone' => $addressInfo['phone'],
                            'hotel_name' => $hotel_name,
                            'hotel_policies' => $hotel_policies,
                            'hotel_features' => $htl_features,
                            'hotel_image_link' => $hotelImageLink,
                            'hotel_has_images' => (bool) HotelImage::getCover($hotel_id),
                            'ftr_img_src' => _PS_IMG_.'rf/',
                            'order_date_restrict' => $order_date_restrict,
                            'PS_SERVICE_PRODUCT_CATEGORY_FILTER' => Configuration::get('PS_SERVICE_PRODUCT_CATEGORY_FILTER'),
                        )
                    );

                    $occupancy_value = array();
                    if (Configuration::get('PS_FRONT_ROOM_UNIT_SELECTION_TYPE') == HotelBookingDetail::PS_ROOM_UNIT_SELECTION_TYPE_QUANTITY) {
                        $occupancy_value = 1;
                    } else {
                        $occupancy_value = array(
                            array(
                                'adults' => $room_info_by_product_id['adults'],
                                'children' => 0,
                                'child_ages' => array(),
                            ),
                        );
                    }
                    $this->assignBookingFormVars($this->product->id, $date_from, $date_to, $occupancy_value);
                    $this->assignServiceProductVars();

                    // product price after imposing feature prices...
                    if ($useTax) {
                        $priceProduct = Product::getPriceStatic($this->product->id, true);
                        $feature_price = HotelRoomTypeFeaturePricing::getRoomTypeFeaturePricesPerDay($this->product->id, $date_from, $date_to, true);
                    } else {
                        $priceProduct = Product::getPriceStatic($this->product->id, false);
                        $feature_price = HotelRoomTypeFeaturePricing::getRoomTypeFeaturePricesPerDay($this->product->id, $date_from, $date_to, false);
                    }
                    $productPriceWithoutReduction = $this->product->getPriceWithoutReduct(!$useTax);
                    $feature_price_diff = (float)($productPriceWithoutReduction - $feature_price);
                    $this->context->smarty->assign('feature_price', $feature_price);
                    $this->context->smarty->assign('feature_price_diff', $feature_price_diff);

                    // send hotel refund rules
                    if ($hotel_branch_obj->active_refund) {
                        $objBranchRefundRules = new HotelBranchRefundRules();
                        if ($hotelRefundRules = $objBranchRefundRules->getHotelRefundRules($hotel_id, 0, 1, 0, 1)) {
                            $this->context->smarty->assign('hotelRefundRules', $hotelRefundRules);
                        }
                    }
                } else {
                    // when using service product we will not need any new paramaters, will update interface by checking is_virtual
                }

                if (Tools::getValue('error')) {
                    $this->context->smarty->assign('error', Tools::getValue('error'));
                }
            }

            // send all the common variables for all type of products
            $this->context->smarty->assign(
                array(
                    'product_controller_url' => $this->context->link->getPageLink('product'),
                    'ratting_img_path' => _MODULE_DIR_.'hotelreservationsystem/views/img/Slices/icons-sprite.png',
                    'WK_PRICE_CALC_METHOD_EACH_DAY' => HotelRoomTypeGlobalDemand::WK_PRICE_CALC_METHOD_EACH_DAY,
                    'stock_management' => Configuration::get('PS_STOCK_MANAGEMENT'),
                    'customizationFields' => $customization_fields,
                    'id_customization' => empty($customization_datas) ? null : $customization_datas[0]['id_customization'],
                    'accessories' => $accessories,
                    'return_link' => $return_link,
                    'product' => $this->product,
                    'product_manufacturer' => new Manufacturer((int) $this->product->id_manufacturer, $this->context->language->id),
                    'token' => Tools::getToken(false),
                    'features' => $this->product->getFrontFeatures($this->context->language->id),
                    'attachments' => (($this->product->cache_has_attachments) ? $this->product->getAttachments($this->context->language->id) : array()),
                    'allow_oosp' => $this->product->isAvailableWhenOutOfStock((int) $this->product->out_of_stock),
                    'last_qties' => (int) Configuration::get('PS_LAST_QTIES'),
                    'HOOK_EXTRA_LEFT' => Hook::exec('displayLeftColumnProduct'),
                    'HOOK_EXTRA_RIGHT' => Hook::exec('displayRightColumnProduct'),
                    'HOOK_PRODUCT_OOS' => Hook::exec('actionProductOutOfStock', array('product' => $this->product)),
                    'HOOK_PRODUCT_ACTIONS' => Hook::exec('displayProductButtons', array('product' => $this->product)),
                    'HOOK_PRODUCT_TAB' => Hook::exec('displayProductTab', array('product' => $this->product)),
                    'HOOK_PRODUCT_TAB_CONTENT' => Hook::exec(
                        'displayProductTabContent',
                        array('product' => $this->product)
                    ),
                    'HOOK_PRODUCT_CONTENT' => Hook::exec('displayProductContent', array('product' => $this->product)),
                    'display_qties' => (int) Configuration::get('PS_DISPLAY_QTIES'),
                    'display_ht' => !Tax::excludeTaxeOption(),
                    'jqZoomEnabled' => Configuration::get('PS_DISPLAY_JQZOOM'),
                    'ENT_NOQUOTES' => ENT_NOQUOTES,
                    'outOfStockAllowed' => (int) Configuration::get('PS_ORDER_OUT_OF_STOCK'),
                    'errors' => $this->errors,
                    'body_classes' => array(
                        $this->php_self.'-'.$this->product->id,
                        $this->php_self.'-'.$this->product->link_rewrite,
                        'category-'.(isset($this->category) ? $this->category->id : ''),
                        'category-'.(isset($this->category) ? $this->category->getFieldByLang('link_rewrite') : ''),
                    ),
                    'display_discount_price' => Configuration::get('PS_DISPLAY_DISCOUNT_PRICE'),
                    'display_google_maps' => Configuration::get('WK_GOOGLE_ACTIVE_MAP'),
                )
            );
        }
        $this->setTemplate(_PS_THEME_DIR_.'product.tpl');
    }

    public function assignServiceProductVars()
    {
        // get service products for room type
        $p = 1;
        $n = RoomTypeServiceProduct::WK_NUM_RESULTS;
        $objRoomTypeServiceProduct = new RoomTypeServiceProduct();
        $smartyVars = array();
        if (Configuration::get('PS_SERVICE_PRODUCT_CATEGORY_FILTER')) {
            $serviceProductsByCategory = $objRoomTypeServiceProduct->getServiceProductsGroupByCategory(
                $this->product->id,
                $p,
                $n,
                true
            );
            if ($serviceProductsByCategory) {
                $smartyVars['service_products_exists'] = 1;
            }
            $smartyVars['service_products_by_category'] = $serviceProductsByCategory;
        } else {
            $roomTypeServiceProducts = $objRoomTypeServiceProduct->getServiceProductsData(
                $this->product->id,
                $p,
                $n,
                true
            );
            $numTotalServiceProducts = $this->product->getProductServiceProducts(
                $this->context->language->id,
                $p,
                $n,
                true,
                2,
                0,
                true
            );
            if ($roomTypeServiceProducts) {
                $smartyVars['service_products_exists'] = 1;
            }
            $smartyVars['service_products'] = $roomTypeServiceProducts;
            $smartyVars['num_total_service_products'] = $numTotalServiceProducts;
        }

        $this->context->smarty->assign($smartyVars);
    }


    public function assignBookingFormVars(
        $idProduct,
        $dateFrom,
        $dateTo,
        $occupancy = array(),
        $jsonDemands = '',
        $roomServiceProducts = null
    ) {
        $objProduct = new Product($idProduct, true, $this->context->language->id, $this->context->shop->id);
        if (!Validate::isLoadedObject($objProduct)) {
            return false;
        }

        $smartyVars = array();
        $objHotelRoomType = new HotelRoomType();
        $objHotel = new HotelBranchInformation();
        $objHotelCartBookingData = new HotelCartBookingData();
        $objBookingDetail = new HotelBookingDetail();
        $objHRTDemand = new HotelRoomTypeDemand();
        $objHRTDemandPrice = new HotelRoomTypeDemandPrice();

        $idCart = (int) $this->context->cart->id;
        $idGuest = (int) $this->context->cart->id_guest;
        $warningCount = (int) Configuration::get('WK_ROOM_LEFT_WARNING_NUMBER');

        $roomType = $objHotelRoomType->getRoomTypeInfoByIdProduct($idProduct);
        $idHotel = $roomType['id_hotel'];
        $hotel = $objHotel->hotelBranchesInfo(false, 2, 1, $idHotel);
        $hotelLocation = $hotel['city'].', '.(isset($hotel['state_name']) ? ' '.$hotel['state_name'].', ' : '').
        ' '.$hotel['country_name'];

        $orderDateRestrict = false;
        $maxOrderDate = HotelOrderRestrictDate::getMaxOrderDate($idHotel);
        if ($maxOrderDate) {
            $maxOrderDate = date('Y-m-d', strtotime($maxOrderDate));
            if (strtotime('-1 day', strtotime($maxOrderDate)) < strtotime($dateFrom)
                || strtotime($maxOrderDate) < strtotime($dateTo)
            ) {
                $orderDateRestrict = true;
            }
        }

        $numDays = $objBookingDetail->getNumberOfDays($dateFrom, $dateTo);
        $bookingParams = array(
            'date_from' => $dateFrom,
            'date_to' => $dateTo,
            'hotel_id' => $idHotel,
            'id_room_type' => $idProduct,
            'id_cart' => $idCart,
            'id_guest' => $idGuest,
        );
        if (Configuration::get('PS_FRONT_ROOM_UNIT_SELECTION_TYPE') == HotelBookingDetail::PS_ROOM_UNIT_SELECTION_TYPE_OCCUPANCY) {
            $bookingParams['occupancy'] = $occupancy;
            $quantity = count($occupancy);
        } else {
            // when room selection is done using qty, occupancy parameter will contain the number of rooms.
            if (is_array($occupancy)) {
                $quantity = 1;
            } else {
                $quantity = $occupancy;
            }
        }

        if ($quantity <= 0) {
            $quantity = 1;
        }

        $totalAvailableRooms = 0;
        if ($hotelRoomData = $objBookingDetail->dataForFrontSearch($bookingParams)) {
            $totalAvailableRooms = $hotelRoomData['stats']['num_avail'];
            $quantity = ($quantity > $totalAvailableRooms) ? $totalAvailableRooms : $quantity;
        }

        // calculate room type price first
        $useTax = HotelBookingDetail::useTax();
        $totalPrice = 0;
        $productPriceWithoutReduction = $objProduct->getPriceWithoutReduct(!$useTax);
        $roomTypeDateRangePrice = HotelRoomTypeFeaturePricing::getRoomTypeTotalPrice(
            $idProduct,
            $dateFrom,
            $dateTo
        );

        $featurePrice = 0;
        if ($useTax) {
            $featurePrice = HotelRoomTypeFeaturePricing::getRoomTypeFeaturePricesPerDay(
                $idProduct,
                $dateFrom,
                $dateTo,
                true
            );
            $roomTypeDateRangePrice = $roomTypeDateRangePrice['total_price_tax_incl'];
        } else {
            $featurePrice = HotelRoomTypeFeaturePricing::getRoomTypeFeaturePricesPerDay(
                $idProduct,
                $dateFrom,
                $dateTo,
                false
            );
            $roomTypeDateRangePrice = $roomTypeDateRangePrice['total_price_tax_excl'];
        }
        $featurePriceDiff = (float) ($productPriceWithoutReduction - $featurePrice);

        $totalRoomPrice = $roomTypeDateRangePrice * $quantity;
        // calculate demand price now
        $demandsPricePerRoom = 0;
        $roomTypeDemands = $objHRTDemand->getRoomTypeDemands($idProduct);
        if ($jsonDemands !== '') {
            $cartDemands = json_decode($jsonDemands, true);
            $demandsPricePerRoom = $objHRTDemandPrice->getRoomTypeDemandsTotalPrice(
                $idProduct,
                $cartDemands,
                $useTax,
                $dateFrom,
                $dateTo
            );

            // send demand info to booking form
            foreach($cartDemands as &$demand) {
                if (Validate::isLoadedObject(
                    $objRoomTypeGlobalDemand = new HotelRoomTypeGlobalDemand($demand['id_global_demand'], $this->context->language->id)
                )) {
                    $demand['name'] = $objRoomTypeGlobalDemand->name;
                    if ($demand['id_option']) {
                        if (Validate::isLoadedObject($objDemandAdvanceOption = new HotelRoomTypeGlobalDemandAdvanceOption($demand['id_option'], $this->context->language->id))) {
                            if ($objDemandAdvanceOption->id_global_demand != $objRoomTypeGlobalDemand->id) {
                                unset($demand);
                                continue;
                            }
                            $demand['advance_option'] = array(
                                'id_option' => $objDemandAdvanceOption->id,
                                'name' => $objDemandAdvanceOption->name
                            );
                        } else {
                            unset($demand);
                            continue;
                        }
                    }

                    $demand['price'] = HotelRoomTypeDemand::getPriceStatic(
                        $idProduct,
                        $objRoomTypeGlobalDemand->id,
                        $demand['id_option'],
                        $useTax
                    );
                }
            }
            $smartyVars['selected_demands'] = $cartDemands;
        }

        if ($roomServiceProducts) {
            $serviceProductsPrice = 0;
            if ($roomServiceProducts = json_decode($roomServiceProducts, true)) {
                $objRoomTypeServiceProductPrice = new RoomTypeServiceProductPrice();
                $objRoomTypeServiceProduct = new RoomTypeServiceProduct();
                foreach ($roomServiceProducts as &$product) {
                    if (!$objRoomTypeServiceProduct->isRoomTypeLinkedWithProduct($idProduct, $product['id_product'])) {
                        unset($product);
                        continue;
                    } else {
                        $objServiceProduct = new ProductCore($product['id_product'], false, $this->context->language->id);
                        if (!$objServiceProduct->allow_multiple_quantity && $product['quantity'] > 1) {
                            $product['quantity'] = 1;
                        } else if ($objServiceProduct->max_quantity && $objServiceProduct->max_quantity < $product['quantity'] ) {
                            $product['quantity'] = $objServiceProduct->max_quantity;
                        }

                    }
                    $product['name'] = $objServiceProduct->name;
                    $product['allow_multiple_quantity'] = $objServiceProduct->allow_multiple_quantity;
                    $productPrice = $objRoomTypeServiceProductPrice->getServicePrice(
                        $product['id_product'],
                        $idProduct,
                        $product['quantity'],
                        $dateFrom,
                        $dateTo,
                        $useTax
                    );
                    $product['price'] = $productPrice;
                    $serviceProductsPrice += $productPrice;
                }
                $smartyVars['selected_service_product'] = $roomServiceProducts;
            }
            $demandsPricePerRoom += $serviceProductsPrice;
            $totalPrice += $serviceProductsPrice;
        }
        // multiply price by number of room required
        $demandsPrice = $demandsPricePerRoom * $quantity;
        // calculate total price
        $totalPrice = $totalRoomPrice + $demandsPrice;
        // send occupancy information searched by the user
        if ($occupancy && is_array($occupancy)) {
            $smartyVars['occupancies'] = $occupancy;
            $smartyVars['occupancy_adults'] = array_sum(array_column($occupancy, 'adults'));
            $smartyVars['occupancy_children'] = array_sum(array_column($occupancy, 'children'));
            $smartyVars['occupancy_child_ages'] = array_sum(array_column($occupancy, 'child_ages'));
        }

        $smartyVars['hotel_location'] = $hotelLocation;
        $smartyVars['order_date_restrict'] = $orderDateRestrict;
        $smartyVars['max_order_date'] = $maxOrderDate;
        $smartyVars['date_from'] = $dateFrom;
        $smartyVars['date_to'] = $dateTo;
        $smartyVars['allow_oosp'] = $objProduct->isAvailableWhenOutOfStock((int) $objProduct->out_of_stock);
        $smartyVars['quantity'] = $quantity;
        $smartyVars['num_days'] = $numDays;
        $smartyVars['warning_count'] = $warningCount;
        $smartyVars['total_available_rooms'] = $totalAvailableRooms;
        $smartyVars['has_room_type_demands'] = $roomTypeDemands ? true : false; // whether to show price breakup
        $smartyVars['rooms_price'] = $totalRoomPrice;
        $smartyVars['demands_price_per_room'] = $demandsPricePerRoom;
        $smartyVars['demands_price'] = $demandsPrice;
        $smartyVars['total_price'] = $totalPrice;
        $this->context->smarty->assign($smartyVars);
        return true;
    }

    /**
     * Assign price and tax to the template
     */
    protected function assignPriceAndTax()
    {
        $id_customer = (isset($this->context->customer) ? (int)$this->context->customer->id : 0);
        $id_group = (int)Group::getCurrent()->id;
        $id_country = $id_customer ? (int)Customer::getCurrentCountry($id_customer) : (int)Tools::getCountry();

        $group_reduction = GroupReduction::getValueForProduct($this->product->id, $id_group);
        if ($group_reduction === false) {
            $group_reduction = Group::getReduction((int)$this->context->cookie->id_customer) / 100;
        }

        // Tax
        $tax = (float)$this->product->getTaxesRate();
        $this->context->smarty->assign('tax_rate', $tax);

        $product_price_with_tax = Product::getPriceStatic($this->product->id, true, null, 6);
        if (Product::$_taxCalculationMethod == PS_TAX_INC) {
            $product_price_with_tax = Tools::ps_round($product_price_with_tax, 2);
        }
        $product_price_without_eco_tax = (float)$product_price_with_tax - $this->product->ecotax;

        $ecotax_rate = (float)Tax::getProductEcotaxRate(Cart::getIdAddressForTaxCalculation($this->product->id));
        if (Product::$_taxCalculationMethod == PS_TAX_INC && (int)Configuration::get('PS_TAX')) {
            $ecotax_tax_amount = Tools::ps_round($this->product->ecotax * (1 + $ecotax_rate / 100), 2);
        } else {
            $ecotax_tax_amount = Tools::ps_round($this->product->ecotax, 2);
        }

        $id_currency = (int)$this->context->cookie->id_currency;
        $id_product = (int)$this->product->id;
        $id_shop = $this->context->shop->id;

        $quantity_discounts = SpecificPrice::getQuantityDiscounts($id_product, $id_shop, $id_currency, $id_country, $id_group, null, true, (int)$this->context->customer->id);
        foreach ($quantity_discounts as &$quantity_discount) {
            if (!isset($quantity_discount['base_price'])) {
                $quantity_discount['base_price'] = 0;
            }
            if ($quantity_discount['id_product_attribute']) {
                $quantity_discount['base_price'] = $this->product->getPrice(Product::$_taxCalculationMethod == PS_TAX_INC, $quantity_discount['id_product_attribute']);

                $combination = new Combination((int)$quantity_discount['id_product_attribute']);
                $attributes = $combination->getAttributesName((int)$this->context->language->id);
                foreach ($attributes as $attribute) {
                    $quantity_discount['attributes'] = $attribute['name'].' - ';
                }
                $quantity_discount['attributes'] = rtrim($quantity_discount['attributes'], ' - ');
            }
            if ((int)$quantity_discount['id_currency'] == 0 && $quantity_discount['reduction_type'] == 'amount') {
                $quantity_discount['reduction'] = Tools::convertPriceFull($quantity_discount['reduction'], null, Context::getContext()->currency);
            }
        }

        $address = new Address(Cart::getIdAddressForTaxCalculation($this->product->id));
        $this->context->smarty->assign(
            array(
                'quantity_discounts' => $this->formatQuantityDiscounts($quantity_discounts, null, (float)$tax, $ecotax_tax_amount),
                'ecotax_tax_inc' => $ecotax_tax_amount,
                'ecotax_tax_exc' => Tools::ps_round($this->product->ecotax, 2),
                'ecotaxTax_rate' => $ecotax_rate,
                'productPriceWithoutEcoTax' => (float)$product_price_without_eco_tax,
                'group_reduction' => $group_reduction,
                'no_tax' => Tax::excludeTaxeOption() || !$this->product->getTaxesRate($address),
                'ecotax' => (!count($this->errors) && $this->product->ecotax > 0 ? Tools::convertPrice((float)$this->product->ecotax) : 0),
                'tax_enabled' => Configuration::get('PS_TAX') && !Configuration::get('AEUC_LABEL_TAX_INC_EXC'),
                'customer_group_without_tax' => Group::getPriceDisplayMethod($this->context->customer->id_default_group),
            )
        );
    }

    /**
     * Assign template vars related to images
     */
    protected function assignImages()
    {
        $images = $this->product->getImages((int)$this->context->cookie->id_lang);
        $product_images = array();

        if (isset($images[0])) {
            $this->context->smarty->assign('mainImage', $images[0]);
        }
        foreach ($images as $k => $image) {
            if ($image['cover']) {
                $this->context->smarty->assign('mainImage', $image);
                $cover = $image;
                $cover['id_image'] = (Configuration::get('PS_LEGACY_IMAGES') ? ($this->product->id.'-'.$image['id_image']) : $image['id_image']);
                $cover['id_image_only'] = (int)$image['id_image'];
            }
            $product_images[(int)$image['id_image']] = $image;
        }

        if (!isset($cover)) {
            if (isset($images[0])) {
                $cover = $images[0];
                $cover['id_image'] = (Configuration::get('PS_LEGACY_IMAGES') ? ($this->product->id.'-'.$images[0]['id_image']) : $images[0]['id_image']);
                $cover['id_image_only'] = (int)$images[0]['id_image'];
            } else {
                $cover = array(
                    'id_image' => $this->context->language->iso_code.'-default',
                    'legend' => 'No picture',
                    'title' => 'No picture'
                );
            }
        }
        $size = Image::getSize(ImageType::getFormatedName('large'));
        $this->context->smarty->assign(array(
            'have_image' => (isset($cover['id_image']) && (int)$cover['id_image'])? array((int)$cover['id_image']) : Product::getCover((int)Tools::getValue('id_product')),
            'cover' => $cover,
            'imgWidth' => (int)$size['width'],
            'mediumSize' => Image::getSize(ImageType::getFormatedName('medium')),
            'largeSize' => Image::getSize(ImageType::getFormatedName('large')),
            'homeSize' => Image::getSize(ImageType::getFormatedName('home')),
            'cartSize' => Image::getSize(ImageType::getFormatedName('cart')),
            'col_img_dir' => _PS_COL_IMG_DIR_));
        if (count($product_images)) {
            $this->context->smarty->assign('images', $product_images);
        }
    }

    /**
     * Assign template vars related to attribute groups and colors
     */
    protected function assignAttributesGroups()
    {
        $colors = array();
        $groups = array();

        // @todo (RM) should only get groups and not all declination ?
        $attributes_groups = $this->product->getAttributesGroups($this->context->language->id);
        if (is_array($attributes_groups) && $attributes_groups) {
            $combination_images = $this->product->getCombinationImages($this->context->language->id);
            $combination_prices_set = array();
            foreach ($attributes_groups as $k => $row) {
                // Color management
                if (isset($row['is_color_group']) && $row['is_color_group'] && (isset($row['attribute_color']) && $row['attribute_color']) || (file_exists(_PS_COL_IMG_DIR_.$row['id_attribute'].'.jpg'))) {
                    $colors[$row['id_attribute']]['value'] = $row['attribute_color'];
                    $colors[$row['id_attribute']]['name'] = $row['attribute_name'];
                    if (!isset($colors[$row['id_attribute']]['attributes_quantity'])) {
                        $colors[$row['id_attribute']]['attributes_quantity'] = 0;
                    }
                    $colors[$row['id_attribute']]['attributes_quantity'] += (int)$row['quantity'];
                }
                if (!isset($groups[$row['id_attribute_group']])) {
                    $groups[$row['id_attribute_group']] = array(
                        'group_name' => $row['group_name'],
                        'name' => $row['public_group_name'],
                        'group_type' => $row['group_type'],
                        'default' => -1,
                    );
                }

                $groups[$row['id_attribute_group']]['attributes'][$row['id_attribute']] = $row['attribute_name'];
                if ($row['default_on'] && $groups[$row['id_attribute_group']]['default'] == -1) {
                    $groups[$row['id_attribute_group']]['default'] = (int)$row['id_attribute'];
                }
                if (!isset($groups[$row['id_attribute_group']]['attributes_quantity'][$row['id_attribute']])) {
                    $groups[$row['id_attribute_group']]['attributes_quantity'][$row['id_attribute']] = 0;
                }
                $groups[$row['id_attribute_group']]['attributes_quantity'][$row['id_attribute']] += (int)$row['quantity'];

                $combinations[$row['id_product_attribute']]['attributes_values'][$row['id_attribute_group']] = $row['attribute_name'];
                $combinations[$row['id_product_attribute']]['attributes'][] = (int)$row['id_attribute'];
                $combinations[$row['id_product_attribute']]['price'] = (float)Tools::convertPriceFull($row['price'], null, Context::getContext()->currency, false);

                // Call getPriceStatic in order to set $combination_specific_price
                if (!isset($combination_prices_set[(int)$row['id_product_attribute']])) {
                    Product::getPriceStatic((int)$this->product->id, false, $row['id_product_attribute'], 6, null, false, false, 1, false, null, null, null, $combination_specific_price);
                    $combination_prices_set[(int)$row['id_product_attribute']] = true;
                    $combinations[$row['id_product_attribute']]['specific_price'] = $combination_specific_price;
                }
                $combinations[$row['id_product_attribute']]['ecotax'] = (float)$row['ecotax'];
                $combinations[$row['id_product_attribute']]['weight'] = (float)$row['weight'];
                $combinations[$row['id_product_attribute']]['quantity'] = (int)$row['quantity'];
                $combinations[$row['id_product_attribute']]['reference'] = $row['reference'];
                $combinations[$row['id_product_attribute']]['unit_impact'] = Tools::convertPriceFull($row['unit_price_impact'], null, Context::getContext()->currency, false);
                $combinations[$row['id_product_attribute']]['minimal_quantity'] = $row['minimal_quantity'];
                if ($row['available_date'] != '0000-00-00' && Validate::isDate($row['available_date'])) {
                    $combinations[$row['id_product_attribute']]['available_date'] = $row['available_date'];
                    $combinations[$row['id_product_attribute']]['date_formatted'] = Tools::displayDate($row['available_date']);
                } else {
                    $combinations[$row['id_product_attribute']]['available_date'] = $combinations[$row['id_product_attribute']]['date_formatted'] = '';
                }

                if (!isset($combination_images[$row['id_product_attribute']][0]['id_image'])) {
                    $combinations[$row['id_product_attribute']]['id_image'] = -1;
                } else {
                    $combinations[$row['id_product_attribute']]['id_image'] = $id_image = (int)$combination_images[$row['id_product_attribute']][0]['id_image'];
                    if ($row['default_on']) {
                        if (isset($this->context->smarty->tpl_vars['cover']->value)) {
                            $current_cover = $this->context->smarty->tpl_vars['cover']->value;
                        }

                        if (is_array($combination_images[$row['id_product_attribute']])) {
                            foreach ($combination_images[$row['id_product_attribute']] as $tmp) {
                                if ($tmp['id_image'] == $current_cover['id_image']) {
                                    $combinations[$row['id_product_attribute']]['id_image'] = $id_image = (int)$tmp['id_image'];
                                    break;
                                }
                            }
                        }

                        if ($id_image > 0) {
                            if (isset($this->context->smarty->tpl_vars['images']->value)) {
                                $product_images = $this->context->smarty->tpl_vars['images']->value;
                            }
                            if (isset($product_images) && is_array($product_images) && isset($product_images[$id_image])) {
                                $product_images[$id_image]['cover'] = 1;
                                $this->context->smarty->assign('mainImage', $product_images[$id_image]);
                                if (count($product_images)) {
                                    $this->context->smarty->assign('images', $product_images);
                                }
                            }
                            if (isset($this->context->smarty->tpl_vars['cover']->value)) {
                                $cover = $this->context->smarty->tpl_vars['cover']->value;
                            }
                            if (isset($cover) && is_array($cover) && isset($product_images) && is_array($product_images)) {
                                $product_images[$cover['id_image']]['cover'] = 0;
                                if (isset($product_images[$id_image])) {
                                    $cover = $product_images[$id_image];
                                }
                                $cover['id_image'] = (Configuration::get('PS_LEGACY_IMAGES') ? ($this->product->id.'-'.$id_image) : (int)$id_image);
                                $cover['id_image_only'] = (int)$id_image;
                                $this->context->smarty->assign('cover', $cover);
                            }
                        }
                    }
                }
            }

            // wash attributes list (if some attributes are unavailables and if allowed to wash it)
            if (!Product::isAvailableWhenOutOfStock($this->product->out_of_stock) && Configuration::get('PS_DISP_UNAVAILABLE_ATTR') == 0) {
                foreach ($groups as &$group) {
                    foreach ($group['attributes_quantity'] as $key => &$quantity) {
                        if ($quantity <= 0) {
                            unset($group['attributes'][$key]);
                        }
                    }
                }

                foreach ($colors as $key => $color) {
                    if ($color['attributes_quantity'] <= 0) {
                        unset($colors[$key]);
                    }
                }
            }
            foreach ($combinations as $id_product_attribute => $comb) {
                $attribute_list = '';
                foreach ($comb['attributes'] as $id_attribute) {
                    $attribute_list .= '\''.(int)$id_attribute.'\',';
                }
                $attribute_list = rtrim($attribute_list, ',');
                $combinations[$id_product_attribute]['list'] = $attribute_list;
            }

            $this->context->smarty->assign(array(
                'groups' => $groups,
                'colors' => (count($colors)) ? $colors : false,
                'combinations' => $combinations,
                'combinationImages' => $combination_images
            ));
        }
    }

    /**
     * Get and assign attributes combinations informations
     */
    protected function assignAttributesCombinations()
    {
        $attributes_combinations = Product::getAttributesInformationsByProduct($this->product->id);
        if (is_array($attributes_combinations) && count($attributes_combinations)) {
            foreach ($attributes_combinations as &$ac) {
                foreach ($ac as &$val) {
                    $val = str_replace(Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR'), '_', Tools::link_rewrite(str_replace(array(',', '.'), '-', $val)));
                }
            }
        } else {
            $attributes_combinations = array();
        }
        $this->context->smarty->assign(
            array(
            'attributesCombinations' =>  $attributes_combinations,
            'attribute_anchor_separator' => Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR')
            )
        );
    }

    /**
     * Assign template vars related to category
     */
    protected function assignCategory()
    {
        // Assign category to the template
        if ($this->category !== false && Validate::isLoadedObject($this->category) && $this->category->inShop() && $this->category->isAssociatedToShop()) {
            $path = Tools::getPath($this->category->id, $this->product->name, true);
        } elseif (Category::inShopStatic($this->product->id_category_default, $this->context->shop)) {
            $this->category = new Category((int)$this->product->id_category_default, (int)$this->context->language->id);
            if (Validate::isLoadedObject($this->category) && $this->category->active && $this->category->isAssociatedToShop()) {
                $path = Tools::getPath((int)$this->product->id_category_default, $this->product->name);
            }
        }
        if (!isset($path) || !$path) {
            $path = Tools::getPath((int)$this->context->shop->id_category, $this->product->name);
        }

        $sub_categories = array();
        if (Validate::isLoadedObject($this->category)) {
            $sub_categories = $this->category->getSubCategories($this->context->language->id, true);

            // various assignements before Hook::exec
            $this->context->smarty->assign(array(
                'path' => $path,
                'category' => $this->category,
                'subCategories' => $sub_categories,
                'id_category_current' => (int)$this->category->id,
                'id_category_parent' => (int)$this->category->id_parent,
                'return_category_name' => Tools::safeOutput($this->category->getFieldByLang('name')),
                'categories' => Category::getHomeCategories($this->context->language->id, true, (int)$this->context->shop->id)
            ));
        }
        $this->context->smarty->assign(array('HOOK_PRODUCT_FOOTER' => Hook::exec('displayFooterProduct', array('product' => $this->product, 'category' => $this->category))));
    }

    protected function transformDescriptionWithImg($desc)
    {
        $reg = '/\[img\-([0-9]+)\-(left|right)\-([a-zA-Z0-9-_]+)\]/';
        while (preg_match($reg, $desc, $matches)) {
            $link_lmg = $this->context->link->getImageLink($this->product->link_rewrite, $this->product->id.'-'.$matches[1], $matches[3]);
            $class = $matches[2] == 'left' ? 'class="imageFloatLeft"' : 'class="imageFloatRight"';
            $html_img = '<img src="'.$link_lmg.'" alt="" '.$class.'/>';
            $desc = str_replace($matches[0], $html_img, $desc);
        }

        return $desc;
    }

    protected function pictureUpload()
    {
        if (!$field_ids = $this->product->getCustomizationFieldIds()) {
            return false;
        }
        $authorized_file_fields = array();
        foreach ($field_ids as $field_id) {
            if ($field_id['type'] == Product::CUSTOMIZE_FILE) {
                $authorized_file_fields[(int)$field_id['id_customization_field']] = 'file'.(int)$field_id['id_customization_field'];
            }
        }
        $indexes = array_flip($authorized_file_fields);
        foreach ($_FILES as $field_name => $file) {
            if (in_array($field_name, $authorized_file_fields) && isset($file['tmp_name']) && !empty($file['tmp_name'])) {
                $file_name = md5(uniqid(rand(), true));
                if ($error = ImageManager::validateUpload($file, (int)Configuration::get('PS_PRODUCT_PICTURE_MAX_SIZE'))) {
                    $this->errors[] = $error;
                }

                $product_picture_width = (int)Configuration::get('PS_PRODUCT_PICTURE_WIDTH');
                $product_picture_height = (int)Configuration::get('PS_PRODUCT_PICTURE_HEIGHT');
                $tmp_name = tempnam(_PS_TMP_IMG_DIR_, 'PS');
                if ($error || (!$tmp_name || !move_uploaded_file($file['tmp_name'], $tmp_name))) {
                    return false;
                }
                /* Original file */
                if (!ImageManager::resize($tmp_name, _PS_UPLOAD_DIR_.$file_name)) {
                    $this->errors[] = Tools::displayError('An error occurred during the image upload process.');
                }
                /* A smaller one */
                elseif (!ImageManager::resize($tmp_name, _PS_UPLOAD_DIR_.$file_name.'_small', $product_picture_width, $product_picture_height)) {
                    $this->errors[] = Tools::displayError('An error occurred during the image upload process.');
                } elseif (!chmod(_PS_UPLOAD_DIR_.$file_name, 0777) || !chmod(_PS_UPLOAD_DIR_.$file_name.'_small', 0777)) {
                    $this->errors[] = Tools::displayError('An error occurred during the image upload process.');
                } else {
                    $this->context->cart->addPictureToProduct($this->product->id, $indexes[$field_name], Product::CUSTOMIZE_FILE, $file_name);
                }
                unlink($tmp_name);
            }
        }
        return true;
    }

    protected function textRecord()
    {
        if (!$field_ids = $this->product->getCustomizationFieldIds()) {
            return false;
        }

        $authorized_text_fields = array();
        foreach ($field_ids as $field_id) {
            if ($field_id['type'] == Product::CUSTOMIZE_TEXTFIELD) {
                $authorized_text_fields[(int)$field_id['id_customization_field']] = 'textField'.(int)$field_id['id_customization_field'];
            }
        }

        $indexes = array_flip($authorized_text_fields);
        foreach ($_POST as $field_name => $value) {
            if (in_array($field_name, $authorized_text_fields) && $value != '') {
                if (!Validate::isMessage($value)) {
                    $this->errors[] = Tools::displayError('Invalid message');
                } else {
                    $this->context->cart->addTextFieldToProduct($this->product->id, $indexes[$field_name], Product::CUSTOMIZE_TEXTFIELD, $value);
                }
            } elseif (in_array($field_name, $authorized_text_fields) && $value == '') {
                $this->context->cart->deleteCustomizationToProduct((int)$this->product->id, $indexes[$field_name]);
            }
        }
    }

    protected function formTargetFormat()
    {
        $customization_form_target = Tools::safeOutput(urldecode($_SERVER['REQUEST_URI']));
        foreach ($_GET as $field => $value) {
            if (strncmp($field, 'group_', 6) == 0) {
                $customization_form_target = preg_replace('/&group_([[:digit:]]+)=([[:digit:]]+)/', '', $customization_form_target);
            }
        }
        if (isset($_POST['quantityBackup'])) {
            $this->context->smarty->assign('quantityBackup', (int)$_POST['quantityBackup']);
        }
        $this->context->smarty->assign('customizationFormTarget', $customization_form_target);
    }

    protected function formatQuantityDiscounts($specific_prices, $price, $tax_rate, $ecotax_amount)
    {
        foreach ($specific_prices as $key => &$row) {
            $row['quantity'] = &$row['from_quantity'];
            if ($row['price'] >= 0) {
                // The price may be directly set

                $cur_price = (!$row['reduction_tax'] ? $row['price'] : $row['price'] * (1 + $tax_rate / 100)) + (float)$ecotax_amount;

                if ($row['reduction_type'] == 'amount') {
                    $cur_price -= ($row['reduction_tax'] ? $row['reduction'] : $row['reduction'] / (1 + $tax_rate / 100));
                    $row['reduction_with_tax'] = $row['reduction_tax'] ? $row['reduction'] : $row['reduction'] / (1 + $tax_rate / 100);
                } else {
                    $cur_price *= 1 - $row['reduction'];
                }

                $row['real_value'] = $row['base_price'] > 0 ? $row['base_price'] - $cur_price : $cur_price;
            } else {
                if ($row['reduction_type'] == 'amount') {
                    if (Product::$_taxCalculationMethod == PS_TAX_INC) {
                        $row['real_value'] = $row['reduction_tax'] == 1 ? $row['reduction'] : $row['reduction'] * (1 + $tax_rate / 100);
                    } else {
                        $row['real_value'] = $row['reduction_tax'] == 0 ? $row['reduction'] : $row['reduction'] / (1 + $tax_rate / 100);
                    }
                    $row['reduction_with_tax'] = $row['reduction_tax'] ? $row['reduction'] : $row['reduction'] +  ($row['reduction'] *$tax_rate) / 100;
                } else {
                    $row['real_value'] = $row['reduction'] * 100;
                }
            }
            $row['nextQuantity'] = (isset($specific_prices[$key + 1]) ? (int)$specific_prices[$key + 1]['from_quantity'] : - 1);
        }
        return $specific_prices;
    }

    public function getProduct()
    {
        return $this->product;
    }

    public function getCategory()
    {
        return $this->category;
    }

    public function displayAjaxRefreshBookingForm()
    {
        $response = array('status' => false);
        $idProduct = (int) Tools::getValue('id_product');
        $dateFrom = Tools::getValue('date_from');
        $dateTo = Tools::getValue('date_to');
        $occupancy = Tools::getValue('occupancy');
        if (Configuration::get('PS_FRONT_ROOM_UNIT_SELECTION_TYPE') == HotelBookingDetail::PS_ROOM_UNIT_SELECTION_TYPE_OCCUPANCY) {
            if (!Validate::isOccupancy($occupancy)) {
                $occupancy = array();
            }
        }
        $roomTypeDemands = Tools::getValue('room_type_demands');
        $roomServiceProducts = Tools::getValue('room_service_products');

        $dateFrom = date('Y-m-d', strtotime($dateFrom));
        $dateTo = date('Y-m-d', strtotime($dateTo));

        $this->assignServiceProductVars();
        if ($this->assignBookingFormVars(
            $idProduct,
            $dateFrom,
            $dateTo,
            $occupancy,
            $roomTypeDemands,
            $roomServiceProducts
        )) {
            $html = $this->context->smarty->fetch('_partials/booking-form.tpl');
            $response['status'] = true;
            $response['html_booking_form'] = $html;
        }

        $this->ajaxDie(json_encode($response));
    }

    public function displayAjaxGetHotelImages()
    {
        $response = array('status' => false);
        $idProduct = (int) Tools::getValue('id_product');
        $page = (int) Tools::getValue('page');
        $imagesPerPage = (int) Configuration::get('PS_HOTEL_IMAGES_PER_PAGE');

        $objHotelRoomType = new HotelRoomType();
        $objHotelImage = new HotelImage();

        $roomTypeInfo = $objHotelRoomType->getRoomTypeInfoByIdProduct($idProduct);
        $idHotel = $roomTypeInfo['id_hotel'];
        $hotelImages = $objHotelImage->getImagesByHotelId($idHotel, $page, $imagesPerPage);
        $hasNextPage = ($objHotelImage->getImagesByHotelId($idHotel, $page + 1, $imagesPerPage)) ? true : false;
        $hotelImagesBaseDir = _MODULE_DIR_.'hotelreservationsystem/views/img/hotel_img/';
        foreach ($hotelImages as &$hotelImage) {
            $hotelImage['link'] = $this->context->link->getMediaLink(
                $objHotelImage->getImageLink($hotelImage['id'],
                ImageType::getFormatedName('large'))
            );
        }

        if (is_array($hotelImages) && count($hotelImages)) {
            $this->context->smarty->assign(array('hotel_images' => $hotelImages));
            $html = $this->context->smarty->fetch(
                $this->getTemplatePath('_partials/hotel_images.tpl')
            );

            $response['html'] = $html;
            $response['status'] = true;
            $response['has_next_page'] = $hasNextPage;
            $response['message'] = 'HTML_OK';
        }

        die(json_encode($response));
    }

    public function displayAjaxGetServiceProducts()
    {
        $response = array(
            'status' => false
        );

        $p = Tools::getValue('p');
        $id_product = Tools::getValue('id_product');
        $id_category = Tools::getValue('id_category');

        if ($this->isTokenValid()) {
            if ($p && $id_product) {
                if (Validate::isLoadedObject($objProduct = new Product($id_product))) {
                    $objRoomTypeServiceProduct = new RoomTypeServiceProduct();
                    $n = RoomTypeServiceProduct::WK_NUM_RESULTS;
                    if (Configuration::get('PS_SERVICE_PRODUCT_CATEGORY_FILTER') && !$id_category) {
                        $response['error'] = Tools::displayError('Invaild Request, please try again');
                    }
                    if (empty($response['error'])) {
                        $response['status'] = true;
                        if ($roomTypeServiceProducts = $objRoomTypeServiceProduct->getServiceProductsData(
                            $objProduct->id,
                            $p,
                            $n,
                            true,
                            2,
                            0,
                            $id_category
                        )) {
                            $this->context->smarty->assign('service_products', $roomTypeServiceProducts);
                            if (Configuration::get('PS_SERVICE_PRODUCT_CATEGORY_FILTER')) {
                                $this->context->smarty->assign('group', $id_category);
                            } else {
                                $this->context->smarty->assign('group', 'all');
                            }
                            $response['service_products'] = $this->context->smarty->fetch(_PS_THEME_DIR_.'_partials/service-products-list.tpl');
                        }
                    }
                } else {
                    $response['error'] = Tools::displayError('Room Type not Found');
                }
            } else {
                $response['error'] = Tools::displayError('Invaild Request, please try again');
            }
        } else {
            $response['error'] = Tools::displayError('Unable to process request, please try again');
        }


        $this->ajaxDie(json_encode($response));
    }

    public function displayAjaxCheckServiceProductWithRoomType()
    {
        $response = array(
            'success' => false
        );

        if ($this->isTokenValid()) {
            $idProduct = Tools::getValue('id_product');
            $idServiceProduct = Tools::getValue('service_product');
            $addedServiceProduct = Tools::getValue('added_service_product');
            $qty = Tools::getValue('qty');
            if (Validate::isLoadedObject($objProduct = new Product($idProduct))) {
                if (!Product::isBookingProduct($idProduct)) {
                    $response['error'] = Tools::displayError('Room Type info not Found');
                    $this->ajaxDie(json_encode($response));
                }

                $objRoomTypeServiceProduct = new RoomTypeServiceProduct();
                if (!$objRoomTypeServiceProduct->isRoomTypeLinkedWithProduct($idProduct, $idServiceProduct)) {
                    $response['error'] = Tools::displayError('Selected product is not available with current room type');
                } else {
                    if (Validate::isLoadedObject($objServiceProduct = new Product($idServiceProduct))) {
                        $response['success'] = true;
                        if (!$objServiceProduct->allow_multiple_quantity) {
                            if (!empty($addedServiceProduct)) {
                                if (!in_array($idServiceProduct, array_column($addedServiceProduct, 'id_product'))) {
                                    $response['add'] = true;
                                    $response['msg'] = 'Service is added to cart.';
                                } else {
                                    $response['msg'] = 'Service already added.';
                                }
                            } else {
                                $response['add'] = true;
                                $response['msg'] = 'Service is added to cart.';
                            }
                        } else {
                            $totalQty = $qty;
                            if (!empty($addedServiceProduct)) {
                                if (($key = array_search($idServiceProduct, array_column($addedServiceProduct, 'id_product')))!== false) {
                                    $totalQty += $addedServiceProduct[$key]['quantity'];
                                }
                            }
                            if ($objServiceProduct->max_quantity && $objServiceProduct->max_quantity < $totalQty ) {
                                $response['msg'] = Tools::displayError(sprintf('Cannot add more than %d quantity.', $objServiceProduct->max_quantity));
                            } else {
                                $response['add'] = true;
                                $response['msg'] = Tools::displayError('Service is added to cart.');
                            }
                        }
                    } else {
                        $response['error'] = Tools::displayError('Service not Found');
                    }
                }
            } else {
                $response['error'] = Tools::displayError('Room Type not Found');
            }
        } else {
            $response['error'] = Tools::displayError('Unable to process request, please try again');
        }


        $this->ajaxDie(json_encode($response));
    }
}
