<?php

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

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

//import needed Joomla! libraries
jimport('joomla.application.component.model');

class AModel extends JModelLegacy //JModel
{
    /**
     * Loaded data
     *
     * @var array
     */
    var $_data = null;

    /**
     * Items total
     *
     * @var int
     */
    var $_total = null;

    /**
     * Pagination object
     *
     * @var JPagination
     */
    var $_pagination = null;

    /**
     * Filter criteria
     *
     * @var array
     */
    var $_lists = null;

    /**
     * Database connector
     *
     * @var JDatabaseMySQL
     */
    var $_db = null;

    /**
     * Object ID
     *
     * @var int
     */
    var $_id;

    public function __construct()
    {
        parent::__construct();
        $this->_db = JFactory::getDBO();
    }

    /**
     * Store more object from request where data is in arrays for each parameter one.
     *
     * @param JDatabaseMySQL $db database connector
     * @param JTable $table concrete table object
     * @param array $foreignKeys
     * @param string $dataPrefix
     * @param array $data
     */
    public function store(&$db, &$table, $foreignKeys, $dataPrefix, &$data)
    {
        $vars = get_object_vars($table);
        $items = array();
        foreach ($vars as $var => $value) {
            if (isset($data[$dataPrefix . $var]) && isset($_REQUEST[$dataPrefix . $var])) {
                $items[$var] = isset($_REQUEST[$dataPrefix . $var]) ? (array)$_REQUEST[$dataPrefix . $var] : array(); // Joomla request filters null value
            }
        }
        if (count($items)) {
            $where = array();
            foreach ($foreignKeys as $name => $value) {
                $where[] = $name . ' = ' . $db->Quote($value);
            }
            $query = 'SELECT `id` FROM ' . $table->getTableName() . (count($where) ? (' WHERE ' . implode(' AND ', $where)) : '');
            $db->setQuery($query);
            $ids = $db->loadAssocList("id","id");
            $firsts = reset($items);
            $added = array();
            foreach ($foreignKeys as $name => $value) {
                $table->$name = $value;
            }
            foreach ($firsts as $id => $value) {
                foreach ($items as $var => $item) {
                    $table->$var = isset($item[$id]) ? $item[$id] : null;
                }
                $table->id = in_array($id, $ids) ? $id : 0;
                if ($table->check()) {
                	//store data and write NULL values - it is NOT default settings
                    $table->store(true);
                    $added[] = $table->id;
                }
            }
            $diff = array_diff($ids, $added);

            //is first element of array - array?
            $diffValues = array_values($diff);
            if(is_array(array_shift($diffValues)))
            {
            	$dif = array();
            	foreach($diff as $arr)
            	{
            		$dif[] = $arr['id'];
            	}
            	$diff = $dif;
            }

            if (count($diff)) {
                $query = 'DELETE FROM ' . $table->getTableName() . ' WHERE id IN (' . implode(',', $diff) . ')';
                $db->setQuery($query);
                $db->query();
            }
        }
    }

    /**
     * Help function to generate where MySQL statement.
     *
     * @param array $where
     * @param mixed $properties
     */
    public function addStringProperty(&$where, $properties)
    {
        for ($i = 1; $i < func_num_args(); $i ++) {
            $property = func_get_arg($i);
            if (isset($this->_lists[$property]) && $this->_lists[$property]) {
                $where[] = 'LOWER(' . $this->rquoteProperty($property) . ') LIKE ' . $this->_db->Quote('%' . JString::strtolower($this->_lists[$property]) . '%');
            }
        }
    }

    /**
     * Help function to generate where MySQL statement.
     *
     * @param array $where
     * @param mixed $properties
     */
    public function addIntProperty(&$where, $properties)
    {
        for ($i = 1; $i < func_num_args(); $i ++) {
            $property = func_get_arg($i);
            if (isset($this->_lists[$property]) && $this->_lists[$property]) {
                $where[] = $this->rquoteProperty($property) . ' = ' . (int) $this->_lists[$property];
            }
        }
    }

    /**
     * Help function to generate where MySQL statement. Add statement for multiple property.
     *
     * @param array  $where       property to saving SQL where criterias
     * @param string $property    name of property in filter
     * @param string $sqlProperty name of property in SQL query, if empty is used name of propety in filter
     * @return void
     */
    public function addMultipleProperty(&$where, $property, $sqlProperty = null)
    {
        if (isset($this->_lists[$property])) {
            AModel::clean($this->_lists[$property]);
            if (count($this->_lists[$property]))
                $where[] = $this->rquoteProperty($sqlProperty ? $sqlProperty : $property) . ' IN (' . implode(',', $this->_lists[$property]) . ')';
            else
                $where[] = $this->rquoteProperty($sqlProperty ? $sqlProperty : $property) . ' IS NULL';
        }
    }

    /**
     * Init object by set limitstart and limit criteria and set array with complet filter data
     */
    public function init($lists)
    {
        $this->_lists = $lists;
        $this->_lists['limit'] = isset($this->_lists['limit']) ? (int) $this->_lists['limit'] : null;
        $this->_lists['limitstart'] = isset($this->_lists['limitstart']) ? (int) $this->_lists['limitstart'] : null;
        $this->_lists['limitstart'] = ARequest::getLimitstart((int) $this->_lists['limit'], (int) $this->_lists['limitstart']);
        $this->setState('limit', $this->_lists['limit']);
        $this->setState('limitstart', $this->_lists['limitstart']);
        unset($this->_data);
        return $this;
    }

    /**
     * Set object ID
     *
     * @param int $id
     * @return $this
     */
    public function setId($id)
    {
        $this->_id = $id;
        return $this;
    }

    /**
     * Get object ID
     *
     * @return int
     */
    public function getId()
    {
        return $this->_id;
    }

    /**
     * Get complete list data
     *
     * @param string $key
     * @param string $class
     * @return stdClass[]
     */
    public function getData($key = '', $class = 'stdClass')
    {
        if (empty($this->_data)) {
            if ($class != 'stdClass' && JModelLegacy::getInstance($class, 'BookingModel')) {
                $class = 'BookingModel'.$class;
            }
            $this->_db->setQuery($this->buildQuery(), $this->getState('limitstart'), $this->getState('limit'));
            $this->_data = $this->_db->loadObjectList($key, $class);
        }
        return $this->_data;
    }

    /**
     * Get fill table object by set ID
     *
     * @return JTable
     */
    public function getObject()
    {
        if ($this->_id) {
            $this->_table->load($this->_id);
        }
        return $this->_table;
    }

    /**
     * Get standard Joomla! pagination object fill with total, limitstart and limit criteria for list
     *
     * @return JPagination
     */
    public function getPagination()
    {
        if (empty($this->_pagination)) {
            jimport('joomla.html.pagination');
            if (IS_ADMIN)
                $this->_pagination = new JPagination($this->getTotal($this), $this->getState('limitstart'), $this->getState('limit'));
            else {
                AImporter::helper('pagination');
                $this->_pagination = new BookingPagination($this->getTotal($this), $this->getState('limitstart'), $this->getState('limit'));
            }
        }
        return $this->_pagination;
    }

    /**
     * Get total items count
     *
     * @return int total count limited by filter
     */
    public function getTotal()
    {
        if (empty($this->_total)) {
            $query = $this->buildQuery();
            $this->_total = $this->_getListCount($query);
        }
        return (int) $this->_total;
    }

    /**
     * Get copy of main table object.
     *
     * @return JTable
     */
    public function getMainTable()
    {
        return $this->_table;
    }

    /**
     * Get MySQL order criteria for items list
     *
     * @return string order criteria in MySQL format
     */
    public function buildContentOrderBy()
    {
        return ' ORDER BY ' . $this->rquoteProperty(isset($this->_lists['order']) && $this->_lists['order'] ? $this->_lists['order'] : 'id') . ' ' . (isset($this->_lists['order_Dir']) && $this->_lists['order_Dir'] ? $this->_lists['order_Dir'] : 'DESC');
    }

    /**
     * Get MySQL where statement.
     *
     * @param array $where
     * @return string
     */
    public static function getWhere(&$where)
    {
        return count($where) ? ' WHERE ' . implode(' AND ', $where) : '';
    }

    /**
     * Build MySQL statement with date limit from to.
     *
     * @param array  $where array (reference) to store SQL query where criterias.
     * @param string $from  name of property with from date value in filter and SQL query.
     * @param string $to    name of property with to date value in filter and SQL query.
     * @return void
     */
    public function addTimeLimit(&$where, $from, $to)
    {
        if (isset($this->_lists[$from]) && ($dbfrom = AModel::datetime2save($this->_lists[$from])))
            $where[] = $this->rquote($from) . ' >= \'' . $dbfrom . '\'';
        if (isset($this->_lists[$to]) && ($dbto = AModel::datetime2save($this->_lists[$to])))
            $where[] = $this->rquote($to) . ' <= \'' . $dbto . '\'';
    }

    /**
     * Set object state property. Allow set option from state.
     *
     * @param string $field name
     * @param array $cids objects IDs
     * @param int $toValue to set value
     * @param mixed $fromValue set allow from state, can be array or single value or null
     * @return boolean success sign
     */
    public function state($field, $cids, $toValue, $fromValue)
    {
        $args = func_num_args();
        $fromValue = array_slice(func_get_args(), 3, $args - 3);
        array_walk($fromValue, array($this , 'quote'));
        array_walk($cids, array($this , 'quote'));
        $field = $this->rquote($field);
        $table = $this->_table->getTableName();
        $query = 'UPDATE ' . $this->rquote($table) . ' SET ' . $field . ' = ' . $this->quote($toValue);
        $id = 'id';
        $query .= ' WHERE ' . $this->rquote($id) . ' IN (' . implode(',', $cids) . ')';
        if (get_class($this) !== 'BookingModelReservation' || AFactory::getConfig()->reservationStatusHandler === 'icon') {
            $query .= ' AND ' . $field . ' IN (' . implode(',', $fromValue) . ')';
        }
        $this->_db->setQuery($query);
        return $this->_db->query();
    }

    /**
     * Empty trashed objects.
     *
     * @param string $field name of field where is saved infromation about trashed
     * @param string $value value of field where is saved infromation about trashed
     */
    public function emptyTrash($field, $value)
    {
        $table = $this->_table->getTableName();
        $query = 'DELETE FROM ' . $this->rquote($table) . ' WHERE ' . $this->rquote($field) . ' = ' . $this->_db->Quote($value);
        $this->_db->setQuery($query);
        return $this->_db->query();
    }

    /**
     * Add single quote to begin and end of string. This function is usable to call back function in array walk method.
     *
     * @param string $str
     * @param int $key
     * @return string
     */
    public function quote(&$str, $key = 0)
    {
        $str = $this->_db->Quote($str);
        return $str;
    }

    /**
     * Add reverse single quote to begin and end of string.
     *
     * @param string $str
     * @return string
     */
    public static function rquote(&$str, $key = null)
    {
        $str = '`' . $str . '`';
        return $str;
    }

    /**
     * Add reverse single quote to begin and end of string, where string is
     * contain from parts.
     *
     * @param string $property
     * @return string
     */
    public function rquoteProperty($property)
    {
        $property = explode('-', $property);
        array_walk($property, array($this , 'rquote'));
        return implode('.', $property);
    }

    /**
     * Check out object before editing.
     *
     * @return boolean success sign
     */
    public function checkout()
    {
        $user = JFactory::getUser();
        return $this->_table->checkout($user->get('id'), $this->_id);
    }

    /**
     * Check in object after editing.
     *
     * @return boolean success sign
     */
    public function checkin()
    {
        return $this->_table->checkin($this->_id);
    }

    public function clean(&$array)
    {
        foreach ($array as $i => $item) {
            if ($item === false) {
                unset($array[$i]);
            } elseif (is_string($item)) {
                $array[$i] = $this->_db->Quote($item);
            }
        }
    }

    /**
     * Help function to generate boolean property in MySQL statement.
     *
     * @param array $where
     * @param string $field
     * @param array $values
     */
    public static function addBooleanPropertyIntoWhere(&$where, $field, $values)
    {
        $keys = array_keys($values);
        if (count($keys) == 1) {
            $where[] = $field . ' = ' . (int) reset($keys);
        }
    }

    /**
     * Get table total rows count without applied any filter.
     *
     * @return int
     */
    public function getTableTotal()
    {
        $this->_db->setQuery('SELECT COUNT(`id`) FROM `' . $this->_table->getTableName() . '`');
        $tableTotel = $this->_db->loadResult();
        return $tableTotel;
    }

    /**
     * Check majority browse filter params.
     *
     * @param $filter
     */
    public static function checkBrowseFilter(&$filter)
    {
        if ($filter->limit == 0 || $filter->limit >= $filter->total)
            $filter->limitstart = 0;
        else
            $filter->limitstart = floor($filter->limitstart / $filter->limit) * $filter->limit;
        if ($filter->total < $filter->limitstart)
            $filter->limitstart = floor($filter->total / $filter->limit) * $filter->limit;
        if ($filter->total == $filter->limitstart)
            $filter->limitstart = (floor($filter->total / $filter->limit) - 1) * $filter->limit;
        $filter->count = $filter->limitstart + $filter->limit;
        if ($filter->count > $filter->total || $filter->limit == 0)
            $filter->count = $filter->total;
    }

    /**
     * Check if table name exists in database.
     *
     * @param string $name add table name in Joomla internal format with prefix mask like #__content or #__users
     * @return boolean
     */
    public static function tableExists($name)
    {
        static $cache;
        $db = JFactory::getDBO();
        /* @var $db JDatabaseMySQL */
        if (is_null($cache)) {
            $query = 'SHOW TABLES';
            $db->setQuery($query);
            $cache = $db->loadAssocList();
        }
        $name = str_replace('#__', $db->getPrefix(), $name);

        $return = false;
        foreach($cache as $p)
        {
        	foreach($p as $q)
        	{
        		 if($q == $name)
        		 	$return = true;
        	}
        }

        return $return;
    }

    /**
     * Prepare datetime to save into database.
     *
     * @param string $datetime date string in format to work with PHP strftime (Joomla 1.5.x.) or date (Joomla 1.6.x) method.
     * @return string database format in GMT0
     */
    public static function datetime2save($datetime, $gmt = false)
    {
        //return AModel::jdate2save($datetime, ADATE_FORMAT_MYSQL_DATETIME, true);
        return AModel::jdate2save($datetime, ADATE_FORMAT_MYSQL_DATETIME, $gmt);
    }

    /**
     * Prepare date to save into database.
     *
     * @param string $date date string in format to work with PHP strftime (Joomla 1.5.x.) or date (Joomla 1.6.x) method.
     * @return string database format in locale
     */
    public static function date2save($date, $gmt = false)
    {
        return AModel::jdate2save($date, ADATE_FORMAT_MYSQL_DATE, $gmt);
    }

    /**
     * Prepare time to save into database.
     *
     * @param string $time time string in format to work with PHP strftime (Joomla 1.5.x.) or date (Joomla 1.6.x) method.
     * @param bool=false get in GMT0
     * @return string database in local format
     */
    public static function time2save($time, $gmt = false)
    {
        return AModel::jdate2save($time, ADATE_FORMAT_MYSQL_TIME, $gmt);
    }

    /**
     * Get current datetime in GMTO for using in SQL query.
     *
     * @param bool=false get in GMT0
     * @return string
     */
    public static function getNow($gmt = false)
    {
    	//FIXME: should be gtm=true?
        return AModel::jdate2save('now', ADATE_FORMAT_MYSQL_DATETIME, $gmt);
    }

    /**
     * Get null date as 0000-00-00 00:00:00 for using in SQL query.
     *
     * @return string
     */
    public static function getNullDate()
    {
        $db = JFactory::getDBO();
        /* @var $db JDatabaseMySQL */
        return $db->getNullDate();
    }

    /**
     * Prepare date to save into database.
     *
     * @param string  $date     date string in format to work with PHP strftime (Joomla 1.5.x.) or date (Joomla 1.6.x) method.
     * @param string  $format   format string to ouput (database format).
     * @param boolean $date2gmt true/false - return in GMT0/return in locale.
     * @return string database format in locale or GMT0.
     */
    public static function jdate2save($jdate, $format = ADATE_FORMAT_MYSQL_DATETIME, $date2gmt = true, $test = false)
    {
        if (($jdate = JString::trim($jdate))) {
            $mainframe = JFactory::getApplication();
            /* @var $mainframe JApplication */
            $jdate = JFactory::getDate($jdate, $tzoffset = $mainframe->getCfg('offset'));
            /* @var $jdate JDate */
            return ISJ16 ? $jdate->format($format, ! $date2gmt) : strftime($format, ($jdate->get('_date') + ($date2gmt ? 0 : ($tzoffset * 60 * 60))));
        }
        return null;
    }

    /**
     * Get concrete user access levels.
     *
     * @param $userId ID of user, if empty take current logged user
     * @return array of available access levels
     */
    public static function getAccess($userId = null)
    {
    	AImporter::helper('user');
    	if ($userId === null || AUser::userExists($userId)) { // prevent for Joomla error if user does not exist
        	$user = JFactory::getUser($userId);
	        /* @var $use JUser */
	        if (ISJ16)
	            return $user->getAuthorisedViewLevels();
	        else {
	            $access[] = SUBJECT_ACCESS_PUBLIC;
	            switch ($user->usertype) {
	                case JUSER_REGISTERED:
	                    $access[] = SUBJECT_ACCESS_REGISTERED;
	                    break;
	                case JUSER_AUTHOR:
	                case JUSER_EDITOR:
	                case JUSER_PUBLISHER:
	                case JUSER_MANAGER:
	                case JUSER_ADMINISTRATOR:
	                case JUSER_SUPER_ADMINISTRATOR:
	                    $access[] = SUBJECT_ACCESS_REGISTERED;
	                    $access[] = SUBJECT_ACCESS_SPECIAL;
	                    break;
	            }
	            return $access;
	        }
    	}
    }

    /**
     * Get list of available user access levels.
     *
     * @return array where key is ID and value title of access level order by set ordering
     */
    public static function getAccesList()
    {
        static $access;
        if (is_null($access)) {
            if (ISJ16) {
                $db = JFactory::getDBO();
                /* @var $db JDatabaseMySQL */
                $db->setQuery('SELECT `id`,`title` FROM `#__viewlevels` ORDER BY `ordering`');
                foreach ($db->loadRowList() as $row)
                    $access[$row[0]] = $row[1];
            } else {
                $access[] = 'Public';
                $access[] = 'Registered';
                $access[] = 'Special';
            }
        }
        return $access;
    }
}


?>
