<?php
/**
 *    This file is part of Mobile Assistant Connector.
 *
 *   Mobile Assistant Connector is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   Mobile Assistant Connector is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with Mobile Assistant Connector.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  @author    eMagicOne <contact@emagicone.com>
 *  @copyright 2014-2017 eMagicOne
 *  @license   http://www.gnu.org/licenses   GNU General Public License
 */

class Mobassistantconnector extends Module
{
    private $file_logger;
//    private $context_id_shop_prev;
    private $pushes_unlinked = '';
    private $cart_version;

    const HOOK_NEW_ORDER = 1;
    const HOOK_ACTION_ORDER_STATUS_POST_UPDATE = 2;
    const TRACKING_NUMBER_TEMPLATE_COMMON_MIDDLE =
        '/modules/mobassistantconnector/mail/common/tracking_number_middle.txt';

    public function __construct()
    {
        $this->name            = 'mobassistantconnector';
        $this->tab             = 'mobile';
        $this->version         = '1.4.0';
        $this->author          = 'eMagicOne';
        $this->module_key      = 'c0b8b5438c3e54ae5f47df2d8da43907';
        $this->need_instance   = 0;
        $this->is_configurable = 1;
        $this->bootstrap       = true;
        $this->cart_version    = Configuration::get('PS_INSTALL_VERSION');

        include_once _PS_MODULE_DIR_.'/mobassistantconnector/classes/FileLoggerMob.php';
        include_once _PS_MODULE_DIR_.'/mobassistantconnector/functions/functions.php';
        include_once _PS_MODULE_DIR_.'/mobassistantconnector/functions/for_ajax.php';
        include_once _PS_MODULE_DIR_.'/mobassistantconnector/classes/Constants.php';

        if (Tools::getIsset('auth_key')) {
            include_once _PS_MODULE_DIR_.'/mobassistantconnector/functions/ajax.php';
            die();
        }

        // Get current id_shop context
//        $this->context_id_shop_prev = Shop::getContextShopID();

        // Set id_shop context to 'CONTEXT_ALL'
//        $this->setContext();

        // Initialize logger
        $this->file_logger = new FileLoggerMob();
        $this->file_logger->setFilename(_PS_MODULE_DIR_.$this->name.'/log/'.Constants::LOG_FILENAME);

        if (version_compare($this->cart_version, '1.7.0.1', '!=')) {
            $this->ps_versions_compliancy = array('min' => '1.5.1', 'max' => '1.7');
        }

        parent::__construct();

        $this->displayName = $this->l('Mobile Assistant Connector');
        $this->description = $this->l(
            'Feel stressed leaving your PrestaShop store off-hand during your vacation, meetings or conferences? Are
            you carrying laptop everywhere you go, repeatedly searching the answer for bothering question in your mind
            - What is going on at my store? With PrestaShop Mobile Assistant on you can be on the move and have access
            to the real-time store data reports at your fingertips. Using it you can monitor key details on your
            products, customers and orders from your Android device wherever you are.'
        );

        $this->checkAndUpdateToNewVersion();
        $this->confirmUninstall = $this->l('Are you sure you want to uninstall?');
    }

    public function install()
    {
//        $this->setContext();

        return (
            parent::install()
            && $this->registerHook('actionValidateOrder')
            && $this->registerHook('actionOrderStatusPostUpdate')
            && $this->registerHook('createAccount')
            && $this->updateTrackingNumberTemplate()
            && Configuration::updateGlobalValue('MOBASSISTANTCONNECTOR_TN_LNG', 0)
            && $this->createTables()
            && $this->initiateTableUsersDataByInstallation()
        );
    }

    public function uninstall()
    {
//        $this->setContext();

        if (
            !parent::uninstall()
            || !Configuration::deleteByName('MOBASSISTANTCONNECTOR_API_KEY')
            || !Configuration::deleteByName('MOBASSISTANTCONNECTOR_TN_TEXT')
            || !Configuration::deleteByName('MOBASSISTANTCONNECTOR_TN_LNG')
            || !Configuration::deleteByName('MOBASSISTANTCONNECTOR_CL_DATE')
            || !$this->dropTables()
        ) {
            return false;
        }

        return true;
    }

    public function getContent()
    {
//        $this->setContext();
        $output = null;

        if (Tools::isSubmit('submit'.$this->name)) {
            $my_module_name = (string)Tools::getValue('submitmobassistantconnector');

            if (!$my_module_name || empty($my_module_name) || !Validate::isGenericName($my_module_name)) {
                $output .= $this->displayError($this->l('Invalid Configuration value'));
            } else {
                $languages = Language::getLanguages(false);

                // Update 'MOBASSISTANTCONNECTOR_TN_TEXT'
                $text = array();
                foreach ($languages as $lang) {
                    $text[$lang['id_lang']] = Tools::getValue(
                        'mobassistantconnector_tracknum_template_text_'.$lang['id_lang']
                    );
                }
                Configuration::updateGlobalValue('MOBASSISTANTCONNECTOR_TN_TEXT', $text);

                // Update 'MOBASSISTANTCONNECTOR_TN_LNG'
                Configuration::updateGlobalValue(
                    'MOBASSISTANTCONNECTOR_TN_LNG',
                    $this->getMobassistantconnectorTracknumMessageLngAllValue()
                );

                $output .= $this->displayConfirmation($this->l('Settings updated'));
            }
        }

        if ($this->isDefaultUserExists()) {
            $output .= $this->displayError(
                $this->l(
                    'Mobile Assistant Connector: Some user has default login and password "1". Change them because
                    of security reasons, please!'
                )
            );
        }

        $form = "<script type='text/javascript' src='{$this->_path}views/js/common.js?v={$this->version}'></script>"
            .$this->displayForm();
        return $output.$form;
    }

    public function displayForm()
    {
//        $this->setContext();

        // Get default language
        $default_lang = (int)Configuration::get('PS_LANG_DEFAULT');

        // Init Fields form array
        $fields_form = array();
        $fields_form[0]['form'] = array(
            'legend' => array(
                'title' => $this->l('Access Settings'),
            ),
            'input' => array(
                array(
                    'type'  => 'text',
                    'label' => $this->l('Users'),
                    'name'  => 'mobassistantconnector_users',
                    'id'    => 'mobassistantconnector_users',
                    'class' => 'mobassistantconnector_invisible',
                ),
                array(
                    'type'   => 'checkbox',
                    'name'   => 'mobassistantconnector',
                    'label'  => $this->l('Use current tracking number message for all languages'),
                    'values' => array(
                        'query' => array(
                            array(
                                'id'   => 'tracknum_message_lng_all',
                                'name' => $this->l('Use current tracking number message for all languages'),
                                'val'  => (int)Configuration::get('MOBASSISTANTCONNECTOR_TN_LNG'),
                            ),
                        ),
                        'id'   => 'id',
                        'name' => 'name'
                    ),
                ),
                array(
                    'type'  => 'textarea',
                    'label' => $this->l('Tracking Number Change Message'),
                    'name'  => 'mobassistantconnector_tracknum_template_text',
                    'id'    => 'mobassistantconnector_tracknum_template_text',
                    'desc'  => $this->l(
                        "If 'Notify Customer by Email' option is enabled in PrestaShop Mobile Assistant application,
                        this message will be sent to a customer. You can set different messages for different
                        languages if needed or use the same one for all the languages. {firstname}, {lastname},
                        {order_name}, {tracking_number} will be substituted by ones inherent to corresponding order"
                    ),
                    'lang'  => true,
                ),
                array(
                    'type' => 'hidden',
                    'name' => 'mobassistantconnector_base_url',
                    'id'   => 'mobassistantconnector_base_url',
                ),
                array(
                    'type' => 'hidden',
                    'name' => 'mobassistantconnector_admin_module_url',
                    'id'   => 'mobassistantconnector_admin_module_url',
                ),
                array(
                    'type' => 'hidden',
                    'name' => 'mobassistantconnector_key',
                    'id'   => 'mobassistantconnector_key',
                ),
            ),
            'submit' => array(
                'title' => (Tools::substr($this->cart_version, 0, 3) == '1.5')
                    ? $this->l('Update settings')
                    : $this->l('Save'),
                'class' => 'btn btn-default pull-right'
            )
        );

        $fields_form[1]['form'] = array(
            'legend' => array(
                'title' => $this->l('Get the Prestashop Mobile Assistant App from Google Play'),
            ),
            'input' => array(
                array(
                    'type'  => 'text',
                    'label' => $this->l('QR-code for Prestashop Mobile Assistant App'),
                    'name'  => 'mobassistantconnector_qrcode_app',
                    'id'    => 'mobassistantconnector_qrcode_app',
                    'desc'  => 'Use your device camera to read the QR-code',
                    'class' => 'mobassistantconnector_invisible',
                ),
            )
        );

        // Show unlinked push notification devices
        $this->pushes_unlinked = getUnlinkedPushes();
        if (!empty($this->pushes_unlinked)) {
            array_splice(
                $fields_form[0]['form']['input'],
                1,
                0,
                array(
                    array(
                        'type'  => 'text',
                        'label' => $this->l('Unlinked Push Notification Devices'),
                        'name'  => 'mobassistantconnector_unlinked_pushes',
                        'id'    => 'mobassistantconnector_unlinked_pushes',
                        'class' => 'mobassistantconnector_invisible',
                        'desc'  => 'This table contains push notification devices which are not linked to any
                            user',
                    )
                )
            );
        }

        $helper = new HelperForm();

        // Module, token and currentIndex
        $helper->module          = $this;
        $helper->name_controller = $this->name;
        $helper->token           = Tools::getAdminTokenLite('AdminModules');
        $helper->currentIndex    = AdminController::$currentIndex.'&configure='.$this->name;

        // Language
        $helper->default_form_language    = $default_lang;
        $helper->allow_employee_form_lang = $default_lang;

        // Title and toolbar
        $helper->title = $this->displayName;
        (version_compare($this->cart_version, '1.6.0.0', '<') && version_compare($this->cart_version, '1.5.0.0', '>='))
            ? $helper->show_toolbar = false
            : $helper->show_toolbar = true;        // false -> remove toolbar
        $helper->toolbar_scroll = true;      // yes - > Toolbar is always visible on the top of the screen.
        $helper->submit_action = 'submit'.$this->name;
        $helper->toolbar_btn = array(
            'save' =>
            array(
                'desc' => $this->l('Save'),
                'href' => AdminController::$currentIndex.'&configure='.$this->name.'&save'.$this->name.
                '&token='.Tools::getAdminTokenLite('AdminModules'),
            ),
            'back' => array(
                'href' => AdminController::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminModules'),
                'desc' => $this->l('Back to list')
            )
        );

        $helper->tpl_vars = array(
            'fields_value' => $this->getConfigFieldsValues(),
            'languages'    => $this->context->controller->getLanguages(),
            'id_language'  => $this->context->language->id
        );

        if (version_compare($this->cart_version, '1.6.0.0', '<')) {
            $root_path = '';
            $this->context->controller->addJS($root_path.'/modules/mobassistantconnector/views/js/ps_1.5.js');
        } else {
            $root_path = _PS_ROOT_DIR_;
            $this->context->controller->addJS($root_path.'/modules/mobassistantconnector/views/js/ps_1.6.js');
        }

        $this->context->controller->addCSS(
            array(
                $this->_path.'views/css/jquery-ui.min.css',
                $this->_path.'views/css/mobassistantconnector.css'
            )
        );
        $this->context->controller->addJS(
            array(
                $this->_path.'views/js/jquery-ui.min.js',
                $this->_path.'views/js/qrcode.min.js',
            )
        );

//        $this->context->controller->addJS(
//            $root_path.'/modules/mobassistantconnector/views/js/jquery-ui.min.js'
//        );
//        $this->context->controller->addCSS($root_path.'/modules/mobassistantconnector/views/css/jquery-ui.min.css');
//        $this->context->controller->addCSS(
//            $root_path.'/modules/mobassistantconnector/views/css/mobassistantconnector.css'
//        );
//        $this->context->controller->addJS($root_path.'/modules/mobassistantconnector/views/js/qrcode.min.js');
//        $this->context->controller->addJS('//dev.com'.$this->_path.'views/js/common.js?v='.$this->version);
        /*$this->context->controller->addJS(
            str_replace('http:', '', str_replace('https:', '', $this->context->shop->getBaseURL()))
            .'modules/mobassistantconnector/views/js/common.js?v='.$this->version
        );*/
//        $this->context->controller->addJS($root_path.'/modules/mobassistantconnector/views/js/common.js');

        if (version_compare($this->cart_version, '1.6', '>=')) {
            $this->context->controller->addCSS($root_path.'/modules/mobassistantconnector/views/css/1.5_more.css');
        }

        return $helper->generateForm($fields_form);
    }

    public function getConfigFieldsValues()
    {
//        $this->setContext();
        $languages = Language::getLanguages(false);

        $shop_url = new ShopUrl((int)Configuration::get('PS_SHOP_DEFAULT'));
        $base_url = $this->prepareUrlProtocol($shop_url->getURL(Configuration::get('PS_SSL_ENABLED')));
        $admin_module_url = AdminController::$currentIndex."&configure=$this->name&token="
            .Tools::getAdminTokenLite('AdminModules')."&tab_module=$this->tab&module_name=$this->name";
        /*$admin_module_url = $this->prepareUrlProtocol(
            Tools::getAdminUrl(
                basename(_PS_ADMIN_DIR_).'/'.Context::getContext()->link->getAdminLink('AdminModules', true)
                .'&configure='.urlencode($this->name).'&tab_module='.$this->tab.'&module_name='.urlencode($this->name)
            )
        );*/

        $data = array(
            'mobassistantconnector_tracknum_message_lng_all' => Configuration::get('MOBASSISTANTCONNECTOR_TN_LNG'),
            'mobassistantconnector_qrcode_app' => '',
            'mobassistantconnector_users' => '',
            'mobassistantconnector_base_url' => $base_url,
            'mobassistantconnector_admin_module_url' => $admin_module_url,
            'mobassistantconnector_key' => hash('sha256', _COOKIE_KEY_),
        );

        if (!empty($this->pushes_unlinked)) {
            $data['mobassistantconnector_unlinked_pushes'] = Tools::jsonEncode($this->pushes_unlinked);
        }

        foreach ($languages as $lang) {
            $data['mobassistantconnector_tracknum_template_text'][$lang['id_lang']] = Configuration::get(
                'MOBASSISTANTCONNECTOR_TN_TEXT',
                $lang['id_lang']
            );
        }

        return $data;
    }

    public function hookActionValidateOrder($params)
    {
//        $this->setContext();

        if (Module::isEnabled($this->name)) {
            $this->hookOrderProcess(self::HOOK_NEW_ORDER, $params);
        }
    }

    public function hookActionOrderStatusPostUpdate($params)
    {
//        $this->setContext();

        if (Module::isEnabled($this->name) && (!isset($GLOBALS['hookNewOrder']) || $GLOBALS['hookNewOrder'] != 1)) {
            $this->hookOrderProcess(self::HOOK_ACTION_ORDER_STATUS_POST_UPDATE, $params);
        }
    }

    public function hookCreateAccount($params)
    {
//        $this->setContext();

        if (!Module::isEnabled($this->name)) {
            return;
        }

        $type      = 'new_customer';
        $id_shop   = $params['newCustomer']->id_shop;
        $devices   = $this->getActiveDevices();
        $count_all = count($devices);

        for ($i = 0; $i < $count_all; $i++) {
            $devices[$i] = $this->preparePushSettings($devices[$i]);

            if (
                !empty($devices[$i]['device_id'])
                && $devices[$i]['new_customer'] == 1
                && $devices[$i]['app_connection_id'] > 0
                && ((int)$devices[$i]['id_shop'] == -1 || (int)$devices[$i]['id_shop'] == $id_shop)
            ) {
//                $store_url = $this->getStoreUrl($id_shop);
                $store_url = $this->getStoreUrl();
                $fields = array(
                    'registration_ids' => array($devices[$i]['device_id']),
                    'data' => array(
                        'message' => array(
                            'push_notif_type' => $type,
                            'email' => $params['newCustomer']->email,
                            'customer_name' => $params['newCustomer']->firstname.' '.$params['newCustomer']->lastname,
                            'customer_id' => $params['newCustomer']->id,
                            'store_url' => $store_url,
                            'id_shop' => $id_shop,
                            'app_connection_id' => $devices[$i]['app_connection_id']
                        )
                    )
                );

                $fields = Tools::jsonEncode($fields);
                $response = $this->sendPushMessage($fields);
                $this->proceedGoogleResponse($response, $devices[$i]['device_id']);
            }
        }

        // Set id_shop context to previous
//        Shop::setContext(Shop::CONTEXT_SHOP, $this->context_id_shop_prev);
    }

    /* Common function for hookNewOrder, hookActionOrderStatusPostUpdate */
    private function hookOrderProcess($hook, $params)
    {
//        $this->setContext();

        $devices   = $this->getActiveDevices();
        $count_all = count($devices);

        if ($count_all < 1) {
            return;
        }

        if ($hook == self::HOOK_NEW_ORDER) {
            $type      = 'new_order';
            $total     = $params['order']->total_paid;
            $id_shop   = $params['order']->id_shop;
            $shop_info = new Shop($id_shop);
            $shop_name = $shop_info->name;
            $status    = $params['orderStatus']->name;
            $GLOBALS['hookNewOrder'] = 1;

            for ($i = 0; $i < $count_all; $i++) {
                $devices[$i] = $this->preparePushSettings($devices[$i]);

                if (
                    !empty($devices[$i]['device_id'])
                    && $devices[$i]['new_order'] == 1
                    && $devices[$i]['app_connection_id'] > 0
                    && ((int)$devices[$i]['id_shop'] == -1 || (int)$devices[$i]['id_shop'] == $id_shop)
                    && $devices[$i]['status'] == 1
                ) {
                    // Send data to Google
                    $this->sendData(
                        $devices[$i]['app_connection_id'],
                        $params['order']->id_shop,
                        $shop_name,
                        $devices[$i]['device_id'],
                        $type,
                        $params['customer']->email,
                        $params['customer']->firstname.' '.$params['customer']->lastname,
                        $params['order']->id,
                        $this->getTotal($devices[$i]['currency_code'], $params['order']->id_currency, $total),
                        $status
                    );
                }
            }
        } elseif ($hook == self::HOOK_ACTION_ORDER_STATUS_POST_UPDATE) {
            $type              = 'order_changed';
            $order_info        = new Order($params['id_order']);
            $customer_info     = new Customer($order_info->id_customer);
            $id_shop           = $order_info->id_shop;
            $shop_info         = new Shop($id_shop);
            $shop_name         = $shop_info->name;
            $order_currency_id = $order_info->id_currency;
            $total             = $order_info->total_paid;
            $status_id         = $params['newOrderStatus']->id;
            $status_name       = $params['newOrderStatus']->name;

            for ($i = 0; $i < $count_all; $i++) {
                $devices[$i]   = $this->preparePushSettings($devices[$i]);
                $push_statuses = $this->getPushStatuses($devices[$i]['order_statuses']);

                if (!empty($devices[$i]['device_id'])
                    && !empty($push_statuses)
                    && (in_array($status_id, $push_statuses) || $push_statuses[0] == -1)
                    && $devices[$i]['app_connection_id'] > 0
                    && ((int)$devices[$i]['id_shop'] == -1 || (int)$devices[$i]['id_shop'] == $id_shop)
                    && $devices[$i]['status'] == 1
                ) {
                    // Send data to Google
                    $this->sendData(
                        $devices[$i]['app_connection_id'],
                        $order_info->id_shop,
                        $shop_name,
                        $devices[$i]['device_id'],
                        $type,
                        $customer_info->email,
                        $customer_info->firstname.' '.$customer_info->lastname,
                        $params['id_order'],
                        $this->getTotal($devices[$i]['currency_code'], $order_currency_id, $total),
                        $status_name,
                        $status_id
                    );
                }
            }
        }

        // Set id_shop context to previous
//        Shop::setContext(Shop::CONTEXT_SHOP, $this->context_id_shop_prev);
    }

    private function getTotal($device_currency_code, $order_currency_id, $total)
    {
//        $this->setContext();

        $device_currency = Currency::getCurrencyInstance($device_currency_code);

        if ($device_currency_code != $order_currency_id) {
            $currency_from = Currency::getCurrencyInstance($order_currency_id);
            $total = Tools::convertPriceFull($total, $currency_from, $device_currency);
        }

        $total = Tools::displayPrice($total, $device_currency);

        return $total;
    }

    private function getStoreUrl()
    {
//        $this->setContext();

//        $store_info = Shop::getShop($id_shop);
        $store_info = Shop::getShop((int)Configuration::get('PS_SHOP_DEFAULT'));
        $store_url  = $store_info['domain'].$store_info['uri'];
        $store_url  = str_replace('http://', '', $store_url);
        $store_url  = str_replace('https://', '', $store_url);
        preg_replace('/\/*$/i', '', $store_url);

        return $store_url;
    }

    private function sendData(
        $app_connection_id,
        $id_shop,
        $shop_name,
        $device_id,
        $type,
        $customer_email,
        $customer_name,
        $id_order,
        $total,
        $status_label,
        $status_code = false
    ) {
//        $store_url = $this->getStoreUrl($id_shop);
        $store_url = $this->getStoreUrl();
        $fields = array(
            'registration_ids' => array($device_id),
            'data' => array(
                'message' => array(
                    'push_notif_type'   => $type,
                    'email'             => $customer_email,
                    'customer_name'     => $customer_name,
                    'order_id'          => $id_order,
                    'total'             => $total,
                    'store_url'         => $store_url,
                    'new_status'        => $status_label,
                    'id_shop'           => $id_shop,
                    'shop_name'         => $shop_name,
                    'app_connection_id' => $app_connection_id
                )
            ),
        );

        if ($status_code) {
            $fields['data']['message']['new_status_code'] = $status_code;
        }

        $fields = Tools::jsonEncode($fields);
        $response = $this->sendPushMessage($fields);
        $this->proceedGoogleResponse($response, $device_id);
    }

    private function sendPushMessage($message_content)
    {
//        $this->setContext();
        $api_key = Configuration::get('MOBASSISTANTCONNECTOR_API_KEY');

        if (!$api_key) {
            return false;
        }

        $headers = array("Authorization: key=$api_key", 'Content-Type: application/json');
        $result = false;

        if (is_callable('curl_init')) {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, 'https://android.googleapis.com/gcm/send');
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $message_content);
            $result = curl_exec($ch);

            if (curl_errno($ch)) {
                $this->file_logger->logMessageCall(
                    "Push message error while sending CURL request: {$result}",
                    $this->file_logger->level
                );
            }

            curl_close($ch);
        }

        return $result;
    }

    private function getActiveDevices()
    {
        $devices_obj = new DbQuery();
        $devices_obj->select('
            mpn.`id`,
            mpn.`device_id`,
            mpn.`app_connection_id`,
            mpn.`currency_code`,
            mpn.`new_order`,
            mpn.`new_customer`,
            mpn.`order_statuses`,
            mpn.`id_shop`
        ');
        $devices_obj->from(Constants::T_PUSH_NOTIFICATIONS, 'mpn');
        $devices_obj->leftJoin(Constants::T_DEVICES, 'md', 'md.`device_unique_id` = mpn.`device_unique_id`');
        $devices_obj->leftJoin(Constants::T_ACCOUNTS, 'ma', 'ma.`id` = md.`account_id`');
        $devices_obj->leftJoin(Constants::T_USERS, 'mu', 'mu.`user_id` = mpn.`user_id`');
        $devices_obj->where(
            'ma.`status` = 1 AND mu.`status` = 1 OR mpn.`user_id` IS NULL OR md.`account_id` IS NULL
            OR md.`account_id` = 0'
        );
//        $devices_obj->where('mu.`status` = 1 OR mu.`status` IS NULL');
        $devices_sql = $devices_obj->build();
        $devices = Db::getInstance()->executeS($devices_sql);

        if (!$devices) {
            $devices = array();
        }

        return $devices;
    }

    private function preparePushSettings($data)
    {
        if (!is_array($data)) {
            $data = array();
        }

        $data['device_id'] = (!isset($data['device_id']) || is_null($data['device_id']))
            ? ''
            : (string)$data['device_id'];
        $data['app_connection_id'] = (!isset($data['app_connection_id']) || (int)$data['app_connection_id'] < 1)
            ? 0
            : (int)$data['app_connection_id'];
        $data['currency_code'] = (!isset($data['currency_code']) || (int)$data['currency_code'] < 1)
            ? (int)Configuration::get('PS_CURRENCY_DEFAULT')
            : (int)$data['currency_code'];
        $data['id_shop'] = (!isset($data['app_connection_id']) || (int)$data['id_shop'] < 1)
            ? '-1'
            : (int)$data['id_shop'];
        $data['status'] = !isset($data['status'])
            ? 1
            : (int)$data['status'];
        $data['new_order'] = !isset($data['new_order'])
            ? 1
            : (int)$data['new_order'];
        $data['new_customer'] = !isset($data['new_customer'])
            ? 1
            : (int)$data['new_customer'];
        $data['order_statuses'] = !isset($data['order_statuses'])
            ? ''
            : (string)$data['order_statuses'];

        return $data;
    }

    private function getPushStatuses($data)
    {
        $statuses = array();

        if (!$data || !is_string($data)) {
            return $statuses;
        }

        $statuses = explode('|', $data);
        $count    = count($statuses);

        for ($i = 0; $i < $count; $i++) {
            $statuses[$i] = (int)trim($statuses[$i]);
        }

        return $statuses;
    }

    private function proceedGoogleResponse($response, $device_registration_id)
    {
        $json = array();

        if ($response && strpos($response, '{') === 0) {
            $json = Tools::jsonDecode($response, true);

            if (!is_array($json)) {
                $json = array();
            }
        }

        if (empty($json) || !isset($json['results'])) {
            return;
        }

        $results = is_array($json['results']) ? $json['results'] : array();

        foreach ($results as $result) {
            if (
                isset($result['registration_id']) && isset($json['canonical_ids']) && (int)$json['canonical_ids'] > 0 ||
                isset($result['error']) &&
                ($result['error'] == 'NotRegistered' || $result['error'] == 'InvalidRegistration')
            ) {
                if (
                    isset($result['registration_id'])
                    && isset($json['canonical_ids'])
                    && (int)$json['canonical_ids'] > 0
                ) {
                    // Delete old records if new push settings are stored
                    Db::getInstance()->execute(
                        'DELETE mp FROM `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS.'` mp
                        LEFT JOIN `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS
                        ."` pn1 ON pn1.`app_connection_id` = pn.`app_connection_id` AND pn1.`user_id` = pn.`user_id`
                        WHERE pn.`device_id` = '$device_registration_id'
                        AND pn1.`device_id` = '{$result['registration_id']}'"
                    );

                    // Replace old registration ids with new
                    Db::getInstance()->update(
                        Constants::T_PUSH_NOTIFICATIONS,
                        array('device_id' => $result['registration_id']),
                        "device_id = '$device_registration_id'"
                    );
                } else {
                    Db::getInstance()->delete(Constants::T_PUSH_NOTIFICATIONS, "device_id = '$device_registration_id'");
                    deleteEmptyDevices();
                    deleteEmptyAccounts();
                    $this->file_logger->logMessageCall("Google error response: {$response}", $this->file_logger->level);
                }
            }
        }
    }

    private function getDatabaseVersion()
    {
        $db_version_obj = new DbQuery();
        $db_version_obj->select('version');
        $db_version_obj->from('module');
        $db_version_obj->where('id_module = '.(int)$this->id);
        $db_version_sql = $db_version_obj->build();
        $db_version = Db::getInstance()->executeS($db_version_sql);

        if (is_array($db_version)) {
            $db_version = array_shift($db_version);
        }

        return $db_version['version'];
    }

    private function updateTrackingNumberTemplate()
    {
//        $this->setContext();
        $languages = Language::getLanguages(false);
        $fp = fopen(_PS_ROOT_DIR_.self::TRACKING_NUMBER_TEMPLATE_COMMON_MIDDLE, 'r');

        if ($fp) {
            $file_content = fread($fp, filesize(_PS_ROOT_DIR_.self::TRACKING_NUMBER_TEMPLATE_COMMON_MIDDLE));
            $data = array();

            foreach ($languages as $lang) {
                $data[$lang['id_lang']] = $file_content;
            }

            return Configuration::updateGlobalValue('MOBASSISTANTCONNECTOR_TN_TEXT', $data);
        }

        return false;
    }

    private function getMobassistantconnectorTracknumMessageLngAllValue()
    {
        return isset($_REQUEST['mobassistantconnector_tracknum_message_lng_all']) ?
            $_REQUEST['mobassistantconnector_tracknum_message_lng_all'] : 0;
    }

    /*private function setContext()
    {
        if (Shop::isFeatureActive()) {
            Shop::setContext(Shop::CONTEXT_ALL);
        }
    }*/

    private function createTables()
    {
        return $this->createTableSessionKeys()
            && $this->createTableFailedLogin()
            && $this->createTablePushNotifications()
            && $this->createTableDevices()
            && $this->createTableUsers()
            && $this->createTableAccounts();
    }

    private function createTableSessionKeys()
    {
        $sql = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.Constants::T_SESSION_KEYS.'` (
            `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
            `session_key` VARCHAR(100) NOT NULL,
            `user_id` INT(10),
            `date_added` DATETIME NOT NULL,
            PRIMARY KEY (`id`),
            UNIQUE KEY UNQ_MOB_USER_ID (`user_id`))';

        return Db::getInstance()->execute($sql);
    }

    private function checkStructureTableSessionKeys()
    {
        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_SESSION_KEYS)." LIKE 'id'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_SESSION_KEYS
                .'` ADD `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_SESSION_KEYS)." LIKE 'session_key'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_SESSION_KEYS.'` ADD `session_key` VARCHAR(100) NOT NULL'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_SESSION_KEYS)." LIKE 'user_id'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_SESSION_KEYS.'` ADD `user_id` INT(10)'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_SESSION_KEYS)." LIKE 'date_added'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_SESSION_KEYS.'` ADD `date_added` DATETIME NOT NULL'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW INDEX FROM '.pSQL(_DB_PREFIX_.Constants::T_SESSION_KEYS)." WHERE `key_name` = 'UNQ_MOB_USER_ID'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_SESSION_KEYS
                .'` ADD CONSTRAINT UNIQUE KEY UNQ_MOB_USER_ID (`user_id`)'
            );
        }
    }

    private function createTableFailedLogin()
    {
        $sql = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.Constants::T_FAILED_LOGIN.'` (
            `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
            `ip` VARCHAR(20) NOT NULL,
            `date_added` DATETIME NOT NULL,
            PRIMARY KEY (`id`))';

        return Db::getInstance()->execute($sql);
    }

    private function checkStructureTableFailedLogin()
    {
        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_FAILED_LOGIN)." LIKE 'id'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_FAILED_LOGIN
                .'` ADD `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_FAILED_LOGIN)." LIKE 'ip'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_FAILED_LOGIN.'` ADD `ip` VARCHAR(20) NOT NULL'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_FAILED_LOGIN)." LIKE 'date_added'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_FAILED_LOGIN.'` ADD `date_added` DATETIME NOT NULL'
            );
        }
    }

    private function createTablePushNotifications()
    {
        $sql = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS.'` (
            `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
            `device_unique_id` INT(10),
            `user_id` INT(10),
            `device_id` VARCHAR(200),
            `new_order` TINYINT,
            `new_customer` TINYINT,
            `order_statuses` VARCHAR(100),
            `app_connection_id` SMALLINT,
            `id_shop` SMALLINT DEFAULT -1,
            `currency_code` SMALLINT,
            PRIMARY KEY (`id`))';

        return Db::getInstance()->execute($sql);
    }

    private function checkStructureTablePushNotifications()
    {
        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS)." LIKE 'id'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS
                .'` ADD `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS)." LIKE 'device_unique_id'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS.'` ADD `device_unique_id` INT(10)'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS)." LIKE 'user_id'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS.'` ADD `user_id` INT(10)'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS)." LIKE 'new_order'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS.'` ADD `new_order` TINYINT'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS)." LIKE 'new_customer'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS.'` ADD `new_customer` TINYINT'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS)." LIKE 'order_statuses'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS.'` ADD `order_statuses` VARCHAR(100)'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS)." LIKE 'app_connection_id'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS.'` ADD `app_connection_id` SMALLINT'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS)." LIKE 'id_shop'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS.'` ADD `id_shop` SMALLINT DEFAULT -1'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS)." LIKE 'currency_code'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS.'` ADD `currency_code` SMALLINT'
            );
        }

        $this->dropStatusFromTablePushNotifications();
    }

    private function createTableDevices()
    {
        $sql = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.Constants::T_DEVICES.'` (
            `device_unique_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
            `device_unique` VARCHAR(100),
            `account_id` INT(10),
            `device_name` VARCHAR(150),
            `last_activity` DATETIME NOT NULL,
            PRIMARY KEY (`device_unique_id`),
            UNIQUE KEY UNQ_MOB_DEV_ID (`device_unique`, `account_id`))';

        return Db::getInstance()->execute($sql);
    }

    private function checkStructureTableDevices()
    {
        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_DEVICES)." LIKE 'device_unique_id'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_DEVICES
                .'` ADD `device_unique_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_DEVICES)." LIKE 'device_unique'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_DEVICES.'` ADD `device_unique` VARCHAR(100)'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_DEVICES)." LIKE 'account_email'"
        );
        if (!empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_DEVICES.'` CHANGE account_email account_id INT'
            );
        } else {
            $data = Db::getInstance()->executeS(
                'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_DEVICES)." LIKE 'account_id'"
            );
            if (empty($data)) {
                Db::getInstance()->execute(
                    'ALTER TABLE `'._DB_PREFIX_.Constants::T_DEVICES.'` ADD `account_id` INT(10)'
                );
            }
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_DEVICES)." LIKE 'device_name'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_DEVICES.'` ADD `device_name` VARCHAR(150)'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_DEVICES)." LIKE 'last_activity'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_DEVICES.'` ADD `last_activity` DATETIME NOT NULL'
            );
        }

        $is_exists = Db::getInstance()->executeS(
            'SHOW INDEX FROM '.pSQL(_DB_PREFIX_.Constants::T_DEVICES)." WHERE `key_name` = 'UNQ_MOB_DEV_UNQ'"
        );
        if (!empty($is_exists)) {
            Db::getInstance()->execute('ALTER TABLE `'._DB_PREFIX_.Constants::T_DEVICES.'` DROP INDEX UNQ_MOB_DEV_UNQ');
        }

        $is_exists = Db::getInstance()->executeS(
            'SHOW INDEX FROM '.pSQL(_DB_PREFIX_.Constants::T_DEVICES)." WHERE `key_name` = 'UNQ_MOB_DEV_ID'"
        );
        if (empty($is_exists)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_DEVICES
                .'` ADD CONSTRAINT UNIQUE KEY UNQ_MOB_DEV_ID (`device_unique`, `account_id`)'
            );
        }

        $is_exists = Db::getInstance()->executeS(
            'SHOW INDEX FROM '.pSQL(_DB_PREFIX_.Constants::T_DEVICES)." WHERE `key_name` = 'UNQ_MOB_DEV_EMAIL'"
        );
        if (!empty($is_exists)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_DEVICES.'` DROP INDEX UNQ_MOB_DEV_EMAIL'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_DEVICES)." LIKE 'device_unique'"
        );
        if (Tools::strtolower($data[0]['Type']) != 'varchar(100)') {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_DEVICES.'` MODIFY `device_unique` VARCHAR(100)'
            );
        }
    }

    private function createTableUsers()
    {
        $sql = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.Constants::T_USERS.'` (
            `user_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
            `username` VARCHAR(100) NOT NULL,
            `password` VARCHAR(35) NOT NULL,
            `allowed_actions` VARCHAR(1000),
            `qr_code_hash` VARCHAR(70),
            `status` TINYINT,
            PRIMARY KEY (`user_id`),
            UNIQUE KEY UNQ_MOB_USER (`username`))';

        return Db::getInstance()->execute($sql);
    }

    private function checkStructureTableUsers()
    {
        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_USERS)." LIKE 'user_id'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_USERS
                .'` ADD `user_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_USERS)." LIKE 'username'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_USERS.'` ADD `username` VARCHAR(100) NOT NULL'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_USERS)." LIKE 'password'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_USERS.'` ADD `password` VARCHAR(35) NOT NULL'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_USERS)." LIKE 'allowed_actions'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_USERS.'` ADD `allowed_actions` VARCHAR(1000)'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_USERS)." LIKE 'qr_code_hash'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_USERS.'` ADD `qr_code_hash` VARCHAR(70)'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_USERS)." LIKE 'status'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_USERS.'` ADD `status` TINYINT'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW INDEX FROM '.pSQL(_DB_PREFIX_.Constants::T_USERS)." WHERE `key_name` = 'UNQ_MOB_USER'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_USERS
                .'` ADD CONSTRAINT UNIQUE KEY UNQ_MOB_USER (`username`)'
            );
        }
    }

    private function createTableAccounts()
    {
        $sql = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.Constants::T_ACCOUNTS.'` (
            `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
            `account_email` VARCHAR(100) NOT NULL,
            `status` TINYINT,
            PRIMARY KEY (`id`),
            UNIQUE KEY UNQ_MOB_ACCOUNT (`account_email`))';

        return Db::getInstance()->execute($sql);
    }

    private function checkStructureTableAccounts()
    {
        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_ACCOUNTS)." LIKE 'id'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_ACCOUNTS.'` ADD `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_ACCOUNTS)." LIKE 'account_email'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_ACCOUNTS.'` ADD `account_email` INT(10) VARCHAR(100) NOT NULL'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW COLUMNS FROM '.pSQL(_DB_PREFIX_.Constants::T_ACCOUNTS)." LIKE 'status'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_ACCOUNTS.'` ADD `status` TINYINT'
            );
        }

        $data = Db::getInstance()->executeS(
            'SHOW INDEX FROM '.pSQL(_DB_PREFIX_.Constants::T_ACCOUNTS)." WHERE `key_name` = 'UNQ_MOB_ACCOUNT'"
        );
        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_ACCOUNTS
                .'` ADD CONSTRAINT UNIQUE KEY UNQ_MOB_ACCOUNT (`account_email`)'
            );
        }
    }

    private function createTableSessionKeysVersion1Point2Point12()
    {
        $sql = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.Constants::T_SESSION_KEYS.'` (
            `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
            `session_key` VARCHAR(100) NOT NULL,
            `date_added` DATETIME NOT NULL,
            PRIMARY KEY (`id`))';

        return Db::getInstance()->execute($sql);
    }

    private function createTablePushNotificationsVersion1Point2Point12()
    {
        $sql = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS.'` (
            `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
            `device_unique_id` INT(10),
            `device_id` VARCHAR(200),
            `new_order` TINYINT,
            `new_customer` TINYINT,
            `order_statuses` VARCHAR(100),
            `app_connection_id` SMALLINT,
            `id_shop` SMALLINT DEFAULT -1,
            `currency_code` SMALLINT,
            `status` TINYINT,
            PRIMARY KEY (`id`))';

        return Db::getInstance()->execute($sql);
    }

    private function createTableDevicesVersion1Point2Point12()
    {
        $sql = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.Constants::T_DEVICES.'` (
            `device_unique_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
            `device_unique` VARCHAR(100),
            `account_email` VARCHAR(150),
            `device_name` VARCHAR(150),
            `last_activity` DATETIME NOT NULL,
            PRIMARY KEY (`device_unique_id`),
            UNIQUE KEY UNQ_MOB_DEV_EMAIL (`device_unique`, `account_email`))';

        return Db::getInstance()->execute($sql);
    }

    private function dropTables()
    {
        // Drop table `mobassistantconnector_session_keys`
        $sql = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.Constants::T_SESSION_KEYS.'`';
        $td_session_keys = Db::getInstance()->execute($sql);

        // Drop table `mobassistantconnector_failed_login`
        $sql = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.Constants::T_FAILED_LOGIN.'`';
        $td_failed_attempts = Db::getInstance()->execute($sql);

        // Drop table `mobassistantconnector_push_notifications`
        $sql = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS.'`';
        $td_push_notifications = Db::getInstance()->execute($sql);

        // Drop table `mobassistantconnector_devices`
        $sql = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.Constants::T_DEVICES.'`';
        $td_devices = Db::getInstance()->execute($sql);

        // Drop table `mobassistantconnector_users`
        $sql = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.Constants::T_USERS.'`';
        $td_users = Db::getInstance()->execute($sql);

        // Drop table `mobassistantconnector_accounts`
        $sql = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.Constants::T_ACCOUNTS.'`';
        $td_accounts = Db::getInstance()->execute($sql);

        return $td_session_keys
            && $td_failed_attempts
            && $td_push_notifications
            && $td_devices
            && $td_users
            && $td_accounts;
    }

    private function dropOldTables()
    {
        // Drop table `mobassistantconnector_session_keys`
        $sql = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.Constants::T_SESSION_KEYS.'`';
        Db::getInstance()->execute($sql);

        // Drop table `mobassistantconnector_failed_attempts`
        $sql = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.'mobassistantconnector_failed_attempts`';
        Db::getInstance()->execute($sql);
    }

    private function deleteSessionKeys()
    {
        Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.Constants::T_SESSION_KEYS.'`');
    }

    private function movePushDevices()
    {
        $device_ids = Configuration::get('MOBASSISTANTCONNECTOR_GOOGLE_IDS');
        Configuration::deleteByName('MOBASSISTANTCONNECTOR_GOOGLE_IDS');

        if (!$device_ids) {
            return;
        }

        $device_ids = unserialize($device_ids);
        $count_ids  = count($device_ids);

        for ($i = 0; $i < $count_ids; $i++) {
            $is_exists = Db::getInstance()->getValue(
                'SELECT `id` FROM `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS."` WHERE `device_id` = '"
                .pSQL($device_ids[$i]['push_device_id'])."' AND `app_connection_id` = "
                .(int)$device_ids[$i]['app_connection_id']
            );

            if ($is_exists) {
                continue;
            }

            $values = array(
                'device_id'         => $device_ids[$i]['push_device_id'],
                'new_order'         => $device_ids[$i]['push_new_order'],
                'new_customer'      => $device_ids[$i]['push_new_customer'],
                'order_statuses'    => $device_ids[$i]['push_order_statuses'],
                'app_connection_id' => $device_ids[$i]['app_connection_id'],
                'id_shop'           => $device_ids[$i]['push_id_shop'],
                'currency_code'     => $device_ids[$i]['push_currency_code'],
                'status'            => 1
            );

            Db::getInstance()->insert(Constants::T_PUSH_NOTIFICATIONS, $values);
        }
    }

    private function changeTableDevices()
    {
        Db::getInstance()->execute(
            'ALTER TABLE `'._DB_PREFIX_.Constants::T_DEVICES.'` MODIFY `device_unique` VARCHAR(100)'
        );
    }

    private function addUserIdToTableSessionKeys()
    {
        $is_exists = Db::getInstance()->getValue(
            "SELECT `COLUMN_NAME` FROM information_schema.COLUMNS WHERE `TABLE_SCHEMA` = '".pSQL(_DB_NAME_)
            ."' AND `TABLE_NAME` = '".pSQL(_DB_PREFIX_.Constants::T_SESSION_KEYS)."' AND `COLUMN_NAME` = 'user_id'"
        );

        if (!$is_exists) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_SESSION_KEYS
                .'` ADD `user_id` INT(10), ADD CONSTRAINT UNIQUE KEY UNQ_MOB_USER_ID (`user_id`)'
            );
        }
    }

    private function addUserIdToTablePushNotifications()
    {
        $is_exists = Db::getInstance()->getValue(
            "SELECT `COLUMN_NAME` FROM information_schema.COLUMNS WHERE `TABLE_SCHEMA` = '".pSQL(_DB_NAME_)
            ."' AND `TABLE_NAME` = '".pSQL(_DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS)
            ."' AND `COLUMN_NAME` = 'user_id'"
        );

        if (!$is_exists) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS.'` ADD `user_id` INT(10)'
            );
        }
    }

    private function dropStatusFromTablePushNotifications()
    {
        $is_exists = Db::getInstance()->getValue(
            "SELECT `COLUMN_NAME` FROM information_schema.COLUMNS WHERE `TABLE_SCHEMA` = '".pSQL(_DB_NAME_)
            ."' AND `TABLE_NAME` = '".pSQL(_DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS)."' AND `COLUMN_NAME` = 'status'"
        );

        if ($is_exists) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_PUSH_NOTIFICATIONS.'` DROP COLUMN `status`'
            );
        }
    }

    private function changeTableDevicesToVersion1Point3Point0()
    {
        Db::getInstance()->execute(
            'ALTER TABLE `'._DB_PREFIX_.Constants::T_DEVICES.'` CHANGE account_email account_id INT'
        );

        $data = Db::getInstance()->executeS(
            'SHOW INDEX FROM '.pSQL(_DB_PREFIX_.Constants::T_DEVICES)." WHERE `key_name` = 'UNQ_MOB_DEV_UNQ'"
        );

        if (!empty($data)) {
            Db::getInstance()->execute('ALTER TABLE `'._DB_PREFIX_.Constants::T_DEVICES.'` DROP INDEX UNQ_MOB_DEV_UNQ');
        }

        $data = Db::getInstance()->executeS(
            'SHOW INDEX FROM '.pSQL(_DB_PREFIX_.Constants::T_DEVICES)." WHERE `key_name` = 'UNQ_MOB_DEV_ID'"
        );

        if (empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_DEVICES
                .'` ADD CONSTRAINT UNIQUE KEY UNQ_MOB_DEV_ID (`device_unique`, `account_id`)'
            );
        }
    }

    private function updateTo1Point3Point0()
    {
        $this->deleteSessionKeys();

        $this->addUserIdToTableSessionKeys();
        $this->addUserIdToTablePushNotifications();
        $this->dropStatusFromTablePushNotifications();
        $this->changeTableDevicesToVersion1Point3Point0();

        if (!$this->createTableUsers()) {
            return;
        }

        $this->createTableAccounts();
        $data = Configuration::get('MOBASSISTANTCONNECTOR');
        Configuration::deleteByName('MOBASSISTANTCONNECTOR');

        if ($data) {
            $data = unserialize($data);
        } else {
            $data = array('login' => '1', 'password' => md5('1'));
        }

        Db::getInstance()->insert(
            Constants::T_USERS,
            array(
                'username'        => $data['login'],
                'password'        => $data['password'],
                'allowed_actions' => implode(';', $this->getActionsCodes()),
                'qr_code_hash'    => hash('sha256', time()),
                'status'          => 1,
            )
        );
    }

    private function updateTo1Point3Point2()
    {
        $data = Db::getInstance()->executeS(
            'SHOW INDEX FROM '.pSQL(_DB_PREFIX_.Constants::T_DEVICES)." WHERE `key_name` = 'UNQ_MOB_DEV_EMAIL'"
        );

        if (!empty($data)) {
            Db::getInstance()->execute(
                'ALTER TABLE `'._DB_PREFIX_.Constants::T_DEVICES.'` DROP INDEX UNQ_MOB_DEV_EMAIL'
            );
        }
    }

    private function updateTo1Point4Point0()
    {
        $query = new DbQuery();
        $query->select('user_id, allowed_actions')->from(Constants::T_USERS);
        $users = Db::getInstance()->executeS($query->build());

        if (empty($users)) {
            return;
        }

        $action_codes_all = $this->getActionsCodes();
        $action_codes_all_str = implode(';', $action_codes_all);
        $count_actions_all = count($action_codes_all);
        $count_users = count($users);
        for ($i = 0; $i < $count_users; $i++) {
            if (!empty($users[$i]['allowed_actions'])) {
                $user_actions = explode(';', $users[$i]['allowed_actions']);
                $has_user_all_actions = true;

                for ($j = 0; $j < $count_actions_all; $j++) {
                    if (
                        !in_array($action_codes_all[$j], $user_actions)
                        && $action_codes_all[$j] != 'order_details_products_list_pickup'
                    ) {
                        $has_user_all_actions = false;
                        break;
                    }
                }

                if ($has_user_all_actions) {
                    Db::getInstance()->update(
                        Constants::T_USERS,
                        array('allowed_actions' => $action_codes_all_str),
                        "`user_id` = '".(int)$users[$i]['user_id']."'"
                    );
                }
            }
        }
    }

    private function getActionsCodes()
    {
        $actions = getRestrictedActions();
        $result = array();
        $groups_count = count($actions);

        for ($i = 0; $i < $groups_count; $i++) {
            $child_count = count($actions[$i]['child']);

            for ($j = 0; $j < $child_count; $j++) {
                $result[] = $actions[$i]['child'][$j]['code'];
            }
        }

        return $result;
    }

    private function initiateTableUsersDataByInstallation()
    {
        if (!$this->createTableUsers()) {
            return false;
        }

        $result = Db::getInstance()->insert(
            Constants::T_USERS,
            array(
                'username'        => '1',
                'password'        => md5('1'),
                'allowed_actions' => implode(';', $this->getActionsCodes()),
                'qr_code_hash'    => hash('sha256', time()),
                'status'          => 1,
            )
        );

        return $result;
    }

    private function isDefaultUserExists()
    {
        return Db::getInstance()->getValue(
            'SELECT `user_id` FROM `'._DB_PREFIX_.Constants::T_USERS
            ."` WHERE `username` = '1' AND `password` = 'c4ca4238a0b923820dcc509a6f75849b'"
        );
    }

    private function moveUsersFromConfigToTable()
    {
        $data = Configuration::get('MOBASSISTANTCONNECTOR');
        Configuration::deleteByName('MOBASSISTANTCONNECTOR');

        if (!$data) {
            return;
        }

        $data = unserialize($data);
        Db::getInstance()->insert(
            Constants::T_USERS,
            array(
                'username'        => $data['login'],
                'password'        => $data['password'],
                'allowed_actions' => implode(';', $this->getActionsCodes()),
                'qr_code_hash'    => hash('sha256', time()),
                'status'          => 1,
            )
        );
    }

    private function checkAndUpdateToNewVersion()
    {
//        $this->setContext();
        $database_version_cur = $this->getDatabaseVersion();

        if (!$database_version_cur) {
            return;
        }

        if (version_compare($database_version_cur, $this->version, '<')) {
            // Update module version in database
            self::upgradeModuleVersion($this->name, $this->version);
            $this->_generateConfigXml();

            if (version_compare($database_version_cur, '1.2.0', '=')) {
                $this->dropOldTables();
            }

            // Create additional tables
            if (version_compare($database_version_cur, '1.2.1', '<')) {
                $this->createTableDevicesVersion1Point2Point12();
                $this->createTablePushNotificationsVersion1Point2Point12();
                $this->createTableSessionKeysVersion1Point2Point12();
                $this->createTableFailedLogin();
                $this->movePushDevices();
            }

            if (
                version_compare($database_version_cur, '1.2.0', '>')
                && version_compare($database_version_cur, '1.2.5', '<')
            ) {
                $this->changeTableDevices();
            }

            if (version_compare($database_version_cur, '1.3.0', '<')) {
                $this->updateTo1Point3Point0();
            }

            if (version_compare($database_version_cur, '1.3.2', '<')) {
                $this->updateTo1Point3Point2();
            }
        }

        $this->createTableUsers();
        $this->checkStructureTableUsers();
        $this->moveUsersFromConfigToTable();

        $this->createTableSessionKeys();
        $this->checkStructureTableSessionKeys();

        $this->createTablePushNotifications();
        $this->checkStructureTablePushNotifications();

        $this->createTableFailedLogin();
        $this->checkStructureTableFailedLogin();

        $this->createTableDevices();
        $this->checkStructureTableDevices();

        $this->createTableAccounts();
        $this->checkStructureTableAccounts();

        if (version_compare($database_version_cur, '1.4.0', '<')) {
            $this->updateTo1Point4Point0();
        }

        self::removeMailsDir(_PS_MODULE_DIR_.'mobassistantconnector/mails/');
    }

    private static function getCurrentProtocol()
    {
        return (!empty($_SERVER['HTTPS']) && Tools::strtolower($_SERVER['HTTPS']) != 'off') ? 'https://' : 'http://';
    }

    private static function prepareUrlProtocol($url)
    {
        $url = str_replace('http://', '', $url);
        $url = str_replace('https://', '', $url);

        return self::getCurrentProtocol().$url;
    }

    private static function removeMailsDir($dir_path)
    {
        if (!file_exists($dir_path)) {
            return;
        }

        $files = glob("$dir_path*", GLOB_MARK);

        foreach ($files as $file) {
            if (is_dir($file)) {
                self::removeMailsDir($file);
            } else {
                unlink($file);
            }
        }

        rmdir($dir_path);
    }
}
