<?php
/**
 * @package         ReReplacer
 * @version         12.4.0PRO
 * 
 * @author          Peter van Westen <info@regularlabs.com>
 * @link            http://regularlabs.com
 * @copyright       Copyright © 2022 Regular Labs All Rights Reserved
 * @license         http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
 */

namespace RegularLabs\Component\ReReplacer\Administrator\Model;

use Joomla\CMS\Factory as JFactory;
use Joomla\CMS\Form\Form as JForm;
use Joomla\CMS\Language\Text as JText;
use Joomla\CMS\MVC\Model\ListModel;
use RegularLabs\Library\Parameters as RL_Parameters;
use RegularLabs\Library\RegEx as RL_RegEx;
use RegularLabs\Library\StringHelper as RL_String;

defined('_JEXEC') or die;

class ItemsModel extends ListModel
{
    protected $config;

    /**
     * @var     string    The prefix to use with controller messages.
     */
    protected $text_prefix = 'RL';

    /**
     * Constructor.
     *
     * @param array    An optional associative array of configuration settings.
     *
     * @see        JController
     */
    public function __construct($config = [])
    {
        if (empty($config['filter_fields']))
        {
            $config['filter_fields'] = [
                'area', 'a.area',
                'casesensitive', 'a.casesensitive',
                'category', 'a.category',
                'description', 'a.description',
                'enable_in_admin', 'a.enable_in_admin',
                'id', 'a.id',
                'name', 'a.name',
                'ordering', 'a.ordering',
                'regex', 'a.regex',
                'replace', 'a.replace',
                'search', 'a.search',
                'state', 'a.published',
            ];
        }

        parent::__construct($config);

        $this->config = RL_Parameters::getComponent('rereplacer');
    }

    /**
     * Duplicate Method
     * Duplicate all items specified by array id
     */
    public function duplicate($ids, $model)
    {
        foreach ($ids as $id)
        {
            $model->duplicate($id);
        }

        $msg = JText::sprintf('%d items duplicated', count($ids));
        JFactory::getApplication()->enqueueMessage($msg);
    }

    /**
     * Export Method
     * Export the selected items specified by id
     */
    public function export($ids)
    {
        $db    = $this->getDbo();
        $query = $db->getQuery(true)
            ->select('r.name')
            ->select('r.description')
            ->select('r.category')
            ->select('r.search')
            ->select('r.replace')
            ->select('r.area')
            ->select('r.params')
            ->select('r.published')
            ->select('r.ordering')
            ->from('#__rereplacer as r')
            ->where('r.id IN ( ' . implode(', ', $ids) . ' )');
        $db->setQuery($query);
        $rows = $db->loadObjectList();

        $format = $this->config->export_format;

        $this->exportDataToFile($rows, $format);
    }

    public function exportDataToFile($rows, $format = 'json')
    {
        $filename = 'ReReplacer Items';

        if (count($rows) == 1)
        {
            $name = RL_String::strtolower(RL_String::html_entity_decoder($rows[0]->name));
            $name = RL_RegEx::replace('[^a-z0-9_-]', '_', $name);
            $name = trim(RL_RegEx::replace('__+', '_', $name), '_-');

            $filename = 'ReReplacer Item (' . $name . ')';
        }

        $string = json_encode($rows);

        $this->exportStringToFile($string, $filename);
    }

    public function exportStringToFile($string, $filename)
    {
        // SET DOCUMENT HEADER
        if (RL_RegEx::match('Opera(/| )([0-9].[0-9]{1,2})', $_SERVER['HTTP_USER_AGENT']))
        {
            $UserBrowser = "Opera";
        }
        elseif (RL_RegEx::match('MSIE ([0-9].[0-9]{1,2})', $_SERVER['HTTP_USER_AGENT']))
        {
            $UserBrowser = "IE";
        }
        else
        {
            $UserBrowser = '';
        }

        $mime_type = ($UserBrowser == 'IE' || $UserBrowser == 'Opera') ? 'application/octetstream' : 'application/octet-stream';
        @ob_end_clean();
        ob_start();

        header('Content-Type: ' . $mime_type);
        header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT');

        if ($UserBrowser == 'IE')
        {
            header('Content-Disposition: inline; filename="' . $filename . '.json"');
            header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
            header('Pragma: public');
            echo $string;
            die;
        }

        header('Content-Disposition: attachment; filename="' . $filename . '.json"');
        header('Pragma: no-cache');
        echo $string;
        die;
    }

    /**
     * @param array   $data     Data for the form.
     * @param boolean $loadData True if the form is to load its own data (default case), false if not.
     *
     * @return  JForm   A Form object on success, false on failure
     */
    public function getForm($data = [], $loadData = true)
    {
        return $this->loadForm('com_rereplacer.items', 'items');
    }

    public function getHasCategories()
    {
        $db = $this->getDbo();

        $query = $db->getQuery(true)
            ->select('COUNT(*)')
            ->from($db->quoteName('#__rereplacer'))
            ->where($db->quoteName('category') . ' != ' . $db->quote(''));

        $db->setQuery($query);

        return $db->loadResult();
    }

    public function getItems($getall = false)
    {
        // Get a storage key.
        $store = $this->getStoreId('', $getall);

        // Try to load the data from internal storage.
        if (isset($this->cache[$store]))
        {
            return $this->cache[$store];
        }

        // Load the list items.
        if ($getall)
        {
            $db    = $this->getDbo();
            $query = $db->getQuery(true)
                // Select the required fields from the table.
                ->select('a.*')
                ->from($db->quoteName('#__rereplacer', 'a'))
                ->where($this->_db->quoteName('a.published') . ' = 1')
                ->order('a.ordering asc');
            $this->_db->setQuery($query);
            $items = $this->_db->loadObjectList();
        }
        else
        {
            $query = $this->_getListQuery();
            $items = $this->_getList($query, $this->getStart(), $this->getState('list.limit'));
        }

        foreach ($items as $i => $item)
        {
            $params = RL_Parameters::getObjectFromRegistry($item->params, JPATH_ADMINISTRATOR . '/components/com_rereplacer/item_params.xml');

            foreach ($params as $key => $val)
            {
                if ( ! isset($item->{$key}) && ! is_object($val))
                {
                    $items[$i]->{$key} = $val;
                }
            }

            unset($items[$i]->params);
        }

        // Add the items to the internal cache.
        $this->cache[$store] = $items;

        return $items;
    }

    public function getItemsFromImportData($data)
    {
        $items = json_decode($data, true);

        if (is_null($items))
        {
            return [];
        }

        return $items;
    }

    /**
     * Import Method
     * Import the selected items specified by id
     * and set Redirection to the list of items
     */
    public function import($file, $model)
    {
        $msg = JText::_('RR_PLEASE_CHOOSE_A_VALID_FILE');

        if (empty($file) || ! is_array($file) || ! isset($file['name']))
        {
            JFactory::getApplication()->enqueueMessage($msg, 'warning');

            return;
        }

        $file_format = pathinfo($file['name'], PATHINFO_EXTENSION);

        if ($file_format !== 'json')
        {
            JFactory::getApplication()->enqueueMessage($msg, 'warning');

            return;
        }

        $publish_all = JFactory::getApplication()->input->getInt('publish_all', 0);

        $data = file_get_contents($file['tmp_name']);

        if (empty($data))
        {
            JFactory::getApplication()->enqueueMessage($msg, 'warning');

            return;
        }

        $items = $this->getItemsFromImportData($data);

        if (empty($items))
        {
            JFactory::getApplication()->enqueueMessage($msg, 'warning');

            return;
        }

        foreach ($items as $item)
        {
            $item['id'] = 0;

            if ($publish_all == 0)
            {
                unset($item['published']);
            }
            else if ($publish_all == 1)
            {
                $item['published'] = 1;
            }

            $saved = $model->save($item);

            if ($saved != 1)
            {
                $error = JText::_('Error Saving Item') . ' ( ' . $saved . ' )';
                JFactory::getApplication()->enqueueMessage($error, 'error');
            }
        }

        JFactory::getApplication()->enqueueMessage(JText::_('Items saved'));
    }

    /**
     * Build an SQL query to load the list data.
     *
     * @return    JDatabaseQuery
     */
    protected function getListQuery()
    {
        $db = $this->getDbo();

        $query = $db->getQuery(true)
            // Select the required fields from the table.
            ->select(
                $this->getState(
                    'list.select',
                    'a.*'
                )
            )
            ->from($db->quoteName('#__rereplacer', 'a'));

        $state = $this->getState('filter.state');
        if (is_numeric($state))
        {
            $query->where($db->quoteName('a.published') . ' = ' . ( int ) $state);
        }
        else if ($state == '')
        {
            $query->where('( ' . $db->quoteName('a.published') . ' IN ( 0,1,2 ) )');
        }

        $category = $this->getState('filter.category');
        if ($category != '')
        {
            $query->where($db->quoteName('a.category') . ' = ' . $db->quote($category));
        }

        $casesensitive = $this->getState('filter.casesensitive');
        if ($casesensitive != '')
        {
            $query->where($db->quoteName('a.params') . ' LIKE ' . $db->quote('%"casesensitive":"' . $casesensitive . '"%'));
        }

        $regex = $this->getState('filter.regex');
        if ($regex != '')
        {
            $query->where($db->quoteName('a.params') . ' LIKE ' . $db->quote('%"regex":"' . $regex . '"%'));
        }

        $enable_in_admin = $this->getState('filter.enable_in_admin');
        if ($enable_in_admin != '')
        {
            $query->where($db->quoteName('a.params') . ' LIKE ' . $db->quote('%"enable_in_admin":"' . $enable_in_admin . '"%'));
        }

        $area = $this->getState('filter.area');
        if ($area != '')
        {
            $query->where($db->quoteName('a.area') . ' = ' . $db->quote($area));
        }

        // Filter the list over the search string if set.
        $search = $this->getState('filter.search');
        if ( ! empty($search))
        {
            if (stripos($search, 'id:') === 0)
            {
                $query->where($db->quoteName('a.id') . ' = ' . ( int ) substr($search, 3));
            }
            else
            {
                $search = $db->quote('%' . $db->escape($search, true) . '%');
                $query->where(
                    '( ' . $db->quoteName('a.name') . ' LIKE ' . $search .
                    ' OR ' . $db->quoteName('a.description') . ' LIKE ' . $search .
                    ' OR ' . $db->quoteName('a.category') . ' LIKE ' . $search .
                    ' OR ' . $db->quoteName('a.search') . ' LIKE ' . $search .
                    ' OR ' . $db->quoteName('a.replace') . ' LIKE ' . $search . ' )'
                );
            }
        }

        $query->select($db->quoteName('uc.name', 'editor'))
            ->join('LEFT', $db->quoteName('#__users', 'uc'), $db->quoteName('uc.id') . ' = ' . $db->quoteName('a.checked_out'));

        // Add the list ordering clause.
        $ordering = $this->getState('list.ordering', 'a.ordering');

        if ($ordering == 'a.ordering')
        {
            $query->order('( ' . $db->quoteName('a.area') . ' != ' . $db->quote('articles') . ' )')
                ->order('( ' . $db->quoteName('a.area') . ' != ' . $db->quote('component') . ' )')
                ->order('( ' . $db->quoteName('a.area') . ' != ' . $db->quote('body') . ' AND ' . $db->quoteName('a.area') . ' != ' . $db->quote('') . ')')
                ->order('( ' . $db->quoteName('a.area') . ' != ' . $db->quote('everywhere') . ' )');
        }

        $query->order($db->quoteName($db->escape($ordering)) . ' ' . $db->escape($this->getState('list.direction', 'ASC')));

        return $query;
    }

    /**
     * Method to get a store id based on model configuration state.
     *
     * This is necessary because the model is used by the component and
     * different modules that might need different sets of data or different
     * ordering requirements.
     *
     * @param string    A prefix for the store id.
     *
     * @return    string    A store id.
     */
    protected function getStoreId($id = '', $getall = 0)
    {
        // Compile the store id.
        $id .= ':' . $this->getState('filter.search');
        $id .= ':' . $this->getState('filter.state');
        $id .= ':' . $getall;

        return parent::getStoreId($id);
    }

    /**
     * Method to auto-populate the model state.
     *
     * Note. Calling getState in this method will result in recursion.
     *
     */
    protected function populateState($ordering = null, $direction = null)
    {
        // List state information.
        parent::populateState('a.ordering', 'asc');
    }
}
