<?php

/*
 * @component UserPoints
 * @copyright Copyright (C) 2015-2022 Bernard Gilly - Adrien Roussel - Martin Brampton
 * @license : GNU/GPL
 * @Website : https://remository.com
 */

namespace BlackSheepResearch\Component\UserPoints\Site;

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

use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\User\UserHelper;
use Joomla\CMS\Access\Access;
use Joomla\CMS\Mail\MailHelper;
use Joomla\CMS\Router\Route;
use BlackSheepResearch\Component\UserPoints\Site\Utils\Session;
use BlackSheepResearch\Component\UserPoints\Site\Problems\SQL;
use BlackSheepResearch\Component\UserPoints\Site\Problems\Rule;
use BlackSheepResearch\Component\UserPoints\Site\Problems\Medal;
use BlackSheepResearch\Component\UserPoints\Site\Problems\PointsUser;
use BlackSheepResearch\Component\UserPoints\Site\Problems\PointsDetails;

require_once JPATH_SITE . '/components/com_userpoints/userpoints_setup.php';

// N.B. The following code should be at the end of this file and will execute when the file is loaded?
// if (0 == UserPoints::getUser()->id) Session::set('referrerid');

if (!defined('_MINIMUM_LEVEL')) {
    define('_MINIMUM_LEVEL', 2);
}

jimport('joomla.plugin.plugin');

class UserPoints {

    private static $levelrank_empty = true;
    private static $levelranks = array();
    private static $levelrank_count = 0;
    private static $userinfo_by_refer = [];
    private static $userinfo_by_id = [];

    public static function triggerEvent($eventName, $args) {
        $oldevent = str_replace('UserPoints', 'AltaUserPoints', $eventName);
        $app = Factory::getApplication();
        return array_merge($app->triggerEvent($oldevent, $args), $app->triggerEvent($eventName, $args));
    }

    private static function getDBO() {
        return SQL::getInstance();
    }

    public static function getUser(int $id = 0) {
        return PointsUser::getUser($id);
    }

    public static function getUserByUsername($username) {
        $userid = UserHelper::getUserId($username);
        return $userid ? self::getUser($userid) : 0;
    }

    private static function doSQL($sql) {
        $db = self::getDBO();
        $db->setQuery($sql);
        $db->execute();
    }

    private static function loadLevelRanks() {
        if (self::$levelrank_empty) {
            $db = self::getDBO();
            $query = "SELECT * FROM #__alpha_userpoints_levelrank ORDER BY `levelpoints` ASC;";
            $db->setQuery($query);
            $ranks = (array) $db->loadObjectList();
            foreach ($ranks as $rank) {
                self::$levelranks[$rank->id] = $rank;
                if ($rank->levelpoints >= 0.00) {
                    self::$levelrank_count++;
                }
            }
            self::$levelrank_empty = false;
        }
    }

    private static function getLevelRankByID($id) {
        self::loadLevelRanks();
        return isset(self::$levelranks[$id]) ? self::$levelranks[$id] : null;
    }

    private static function getLevelRankCount() {
        self::loadLevelRanks();
        return self::$levelrank_count;
    }

    private static function getLevelRanks($type) {
        self::loadLevelRanks();
        foreach (self::$levelranks as $levelrank) {
            if ($levelrank->levelpoints > 0 AND $levelrank->typerank = $type) {
                $results[] = $levelrank;
            }
        }
        return isset($results) ? $results : [];
    }

    public static function newpoints($plugin_function = '', $referrerid = '', $keyreference = '', $datareference = '', $randompoints = 0, $feedback = false, $force = 0, $frontmessage = '') {
        if ($plugin_function != '') {
            return self::userpoints($plugin_function, $referrerid, 0, $keyreference, $datareference, $randompoints, $feedback, $force, $frontmessage);
        } else {
            return false;
        }
    }

    public static function userpoints($plugin_function, $referreridparm = '', $referraluserpoints = 0.0, $keyreference = '', $datareference = '', $randompoints = 0, $feedback = false, $force = 0, $frontmessage = '') {

        $lang = Factory::getLanguage();
        $lang->load('com_userpoints', JPATH_SITE);
        // Special case for Recommend
        if ($plugin_function == 'sysplgup_recommend' AND $referreridparm != '' AND $keyreference != '' AND $datareference != '') {
            $force = 1;
        }
        $referrerid = self::retrieveReferrerID($referreridparm, $referraluserpoints);
        $pointsrule = self::checkRuleEnabled($plugin_function, $force, $referrerid);
        if (!$referrerid OR !$pointsrule OR !self::checkExcludeUsers($referrerid)
                OR ($keyreference AND self::checkReference($referrerid, $keyreference, $pointsrule->id))) {
            return false;
        }
        $pointsrule->points = self::pointsToUse($randompoints, $pointsrule, $referrerid, $referraluserpoints);
        if (self::canAfford($plugin_function, $referrerid, $referraluserpoints, -$pointsrule->points)) {
            // check account
            self::insertUserPoints($referrerid, $pointsrule, $referraluserpoints, $keyreference, $datareference, $frontmessage);
            return true;
        } else {
            // used for negative operation,
            $error = Text::_('AUP_YOUDONOTHAVEENOUGHPOINTSTOPERFORMTHISOPERATION');
            Factory::getApplication()->enqueueMessage($error, 'notice');
            return false;
        }
    }

    private static function pointsToUse(float $randompoints, $rule, string $referrerid, float $referraluserpoints = 0.0) : float {
        // Referral points take precendence if present
        // force specific points -> overwriting rule points or if rule points is 0
        // This points can be negative (example : single rule in backend rule manager for several products on frontend, each products with different prices...)
        $points = $referraluserpoints ? $referraluserpoints : ($randompoints ? $randompoints : $rule->points);
        if ($points > 0.0 AND 0.0 == $referraluserpoints) {
            return self::checkMaxDailyPoints($referrerid, $points, $rule->plugin_function);
        }
        return $points;
    }

    public static function canAfford($plugin_function, $referreridparm = '', $referraluserpoints = 0, $randompoints = 0) {
        if ($randompoints < 0.0 OR ComponentHelper::getParams('com_userpoints')->get('allowNegativeAccount', 0)) {
            return true;
        }
        $referrerid = self::retrieveReferrerID($referreridparm, $referraluserpoints);
        $pointsneeded = $randompoints ? -$randompoints : Rule::withRuleName($plugin_function)->points;
        return self::operationIsFeasible($referrerid, $pointsneeded);
    }

    private static function retrieveReferrerID($referrerid, $referraluserpoints) {
        if (!$referraluserpoints) {
            if (!$referrerid) {
                $referrerid = Session::get('referrerid');
            }
        }
        if (!$referrerid) {
            $referrerid = self::getReferreid();
        }
        return $referrerid;
    }

    public static function insertUserPoints($referrerid, $pointsrule, $referraluserpoints = 0.0, $keyreference = '', $datareference = '', $frontmessage = '') {
        if ($referrerid) {
            if ($pointsrule->points == 0.0 AND !ComponentHelper::getParams('com_userpoints')->get('insertAllActivities', 0)) {
                return;
            }
            $newdetail = new PointsDetails();
            $newdetail->dataFromRule($pointsrule, $referrerid);
            $newdetail->referreid = $referrerid;
            $newdetail->keyreference = $keyreference;
            $newdetail->datareference = $datareference;
            $newdetail->enabled = 1;

            // load external plugins
            PluginHelper::importPlugin('userpoints');
            self::triggerEvent('onBeforeInsertUserActivityUserPoints', array(&$pointsrule, $newdetail->points, $referrerid, $keyreference, $datareference));

            $newdetail->save();

            if ($referrerid != 'GUEST' OR $referraluserpoints > 0) {
                self::updateUserPoints($pointsrule, $referrerid, $newdetail, $referraluserpoints, $datareference, $frontmessage);
            }
        }
    }

    private static function updateUserPoints($pointsrule, $referrerid, $details, $referraluserpoints, $datareference = '', $frontmessage = '') {
        if (!self::checkExcludeUsers($referrerid)) {
            return;
        }
        // load external plugins for userpoints
        PluginHelper::importPlugin('userpoints');
        Factory::getLanguage()->load('com_userpoints', JPATH_SITE);
        $user = self::getUser();
        $username = ( $user->id ) ? $user->username : '';

        // Get data from alpha_userpoints table
        $pointsuser = PointsUser::withReferrerID($referrerid);
        $pointsuser->last_update = SQL::datetimeSQL();

        $checkWinner = self::notifyWinner($referrerid, $pointsrule->autoapproved, $referraluserpoints);

        if ($pointsrule->autoapproved) {
            if ($pointsrule->plugin_function == 'sysplgup_invitewithsuccess') {
                $pointsuser->referrees = $pointsuser->referrees + 1;
            }
        }
        $pointsuser->save();

        if ($pointsrule->displaymsg AND !$referraluserpoints) {
            self::displayMessage($user->id, $pointsrule->plugin_function, $referrerid, $details->points, $pointsuser->points, str_replace('{username}', $username, $pointsrule->msg));
        }

        $rule_name = ($pointsrule->plugin_function == 'sysplgup_custom' AND $datareference) ? Text::_($datareference) : $pointsrule->rule_name;

        // email notification
        if ($pointsrule->notification AND !$checkWinner) {
            $pointsrule->datareference = Text::_($datareference);
            self::sendnotification($referrerid, $details, $pointsrule);
            self::triggerEvent('onSendNotificationUserPoints', array(&$pointsrule, $rule_name, $details->points, $pointsuser->points, $referrerid, $user->id));
        }

        // Uddeim notification integration
        if (ComponentHelper::getParams('com_userpoints')->get('sendMsgUddeim', 0) AND !$checkWinner) {
            self::sendUddeimNotification($referrerid, $details->points, $rule_name);
        }

        self::triggerEvent('onUpdateUserPoints', array(&$pointsrule, $rule_name, $details->points, $referrerid, $user->id));

        // checking rank and medals and update if necessary
        // if ($rule_id == '') {
        //    $rule_id = self::getRuleID($rule_plugin);
        //}
        self::checkRankMedal($referrerid, $pointsrule->plugin_function);
        self::handleReferralPoints ($pointsuser, $pointsrule, $details);

        // check change user group rule
        //if ( $rule_plugin!='sysplgup_changelevel1' && $rule_plugin!='sysplgup_changelevel2' && $rule_plugin!='sysplgup_changelevel3' )
        //{
        self::checkChangeLevel($referrerid, $details);
        //}

        self::displayMessageSystem($frontmessage);
        self::triggerEvent('onAfterUpdateUserPoints', array(&$pointsrule, $rule_name, $details->points, $referrerid, $user->id));

        // link up rule
        if ($pointsrule->linkup) {
            $plugin_function_linkup = self::getPluginFunction($pointsrule->linkup);
            self::newpoints($plugin_function_linkup, $referrerid);
        }
    }

    private static function handleReferralPoints ($pointsuser, $rule, $details) {
        // referral points rule
        if ($pointsuser->referraluser != '' AND $rule->plugin_function != 'sysplgup_buypointswithpaypal' // not assigned for this rule
                AND $rule->plugin_function != 'sysplgup_raffle' // not assigned for this rule
                AND $rule->plugin_function != 'sysplgup_referralpoints'
                AND $details->points > 0) { // if not already assigned
            $referrule = Rule::withRuleName('sysplgup_referralpoints');
            if ($rule->allow_referral AND $referrule->checkEnabled()) {
                $referraluserpoints = round(($details->points * $referrule->points) / 100, 2);
                if ($referraluserpoints > 0) {
                    self::userpoints('sysplgup_referralpoints', $pointsuser->referraluser, $referraluserpoints, '', Text::_('AUP_REFERRALPOINTS').' - '.$pointsuser->referraluser);
                }
            }
        }
    }

    public static function notifyWinner(string $referrerid, bool $autoapproved, float $referraluserpoints) : bool {
        $rule = self::checkRuleEnabled('sysplgup_winnernotification', 0, $referrerid);
        if ($rule AND ($autoapproved OR $referraluserpoints)) {
            self::sendwinnernotification($referrerid, $rule->content_items);
            // Uddeim notification integration
            if (ComponentHelper::getParams('com_userpoints')->get('sendMsgUddeim', 0)) {
                self::sendUddeimWinnerNotification($referrerid);
            }
            return true;
        }
        return false;
    }

    private static function displayMessage($userid, $rule_plugin, $referrerid, $assignpoints, $pointstotal, $msg) {
        switch ($rule_plugin) {
            case 'sysplgup_bonuspoints':
            case 'sysplgup_recommend':
            case 'sysplgup_reader2author':
            case 'sysplgup_buypointswithpaypal':
            case 'sysplgup_invite':
                // No need congratulation...
                break;
            case 'sysplgup_invitewithsuccess':
                // number points in message = assign points to new user
                $numpoints = self::getPointsRule('sysplgup_newregistered');
                if ($numpoints AND $userid) {
                    if ($msg) {
                        self::sendPointsChangeMessage(Text::_($msg), $numpoints, $pointstotal);
                    } else {
                        self::sendPointsMessage('AUP_CONGRATULATIONS_SINGULAR', 'AUP_CONGRATULATIONS_PLURAL', $numpoints);
                    }
                }
                break;

            default:
                if ($userid AND ($referrerid == self::getAnyUserReferreID($userid))) {
                    if ($assignpoints > 0) {
                        if ($msg) {
                            self::sendPointsChangeMessage(Text::_($msg), $assignpoints, $pointstotal);
                        } else {
                            self::sendPointsMessage('AUP_CONGRATULATIONS_SINGULAR', 'AUP_CONGRATULATIONS_PLURAL', $assignpoints);
                        }
                    } elseif ($assignpoints < 0) {
                        if ($msg != '') {
                            self::sendPointsChangeMessage(Text::_($msg), $assignpoints, $pointstotal);
                        } else {
                            self::sendPointsMessage('AUP_X_POINTS_DEDUCTED_FROM_YOUR_ACCOUNT_SINGULAR', 'AUP_X_POINTS_DEDUCTED_FROM_YOUR_ACCOUNT_PLURAL', $assignpoints);
                        }
                    }
                }
        }
    }

    private static function sendPointsMessage($singular, $plural, $points) {
        $msgtext = abs($points) > 1 ? Text::_($plural) : Text::_($singular);
        $msg = sprintf($msgtext, self::getFormattedPoints(abs($points)));
        Factory::getApplication()->enqueueMessage($msg);
    }

    private static function sendPointsChangeMessage($string, $points, $newpoints) {
        $patterns = ['{points}', '{newtotal}'];
        $substitutions = [self::getFormattedPoints($points), self::getFormattedPoints($newpoints)];
        $message = str_replace($patterns, $substitutions, $string);
        Factory::getApplication()->enqueueMessage($message);
    }

    public static function checkRuleExpireDate($rule_expire, $type_expire_date) {
        if ($rule_expire != '1000-01-01 00:00:00' || $type_expire_date) {
            if ($type_expire_date) {
                $today = date("Y-m-d", SQL::unixtime());
                switch ($type_expire_date) {
                    case (($type_expire_date > 1) && ($type_expire_date < 30)) :
                        $date = new \DateTime($today);
                        $date->modify('+' . intval($type_expire_date) . ' days');
                        break;
                    case $type_expire_date == 1 :
                        $date = new \DateTime($today);
                        $date->modify('+1 day');
                        break;
                    case $type_expire_date == 30 :
                        $date = new \DateTime($today);
                        $date->modify('+1 month');
                        break;
                    case $type_expire_date == 60 :
                        $date = new \DateTime($today);
                        $date->modify('+2 months');
                        break;
                    case $type_expire_date == 90 :
                        $date = new \DateTime($today);
                        $date->modify('+3 months');
                        break;
                    case $type_expire_date == 180 :
                        $date = new \DateTime($today);
                        $date->modify('+6 months');
                        break;
                    case $type_expire_date == 360 :
                        $date = new \DateTime($today);
                        $date->modify('+1 year');
                        break;
                    case $type_expire_date == 720 :
                        $date = new \DateTime($today);
                        $date->modify('+2 years');
                        break;
                    case $type_expire_date == 1080 :
                        $date = new \DateTime($today);
                        $date->modify('+3 years');
                        break;
                    case $type_expire_date == 1440 :
                        $date = new \DateTime($today);
                        $date->modify('+4 years');
                        break;
                    case $type_expire_date == 1800 :
                        $date = new \DateTime($today);
                        $date->modify('+5 years');
                        break;
                    case $type_expire_date == 3600 :
                        $date = new \DateTime($today);
                        $date->modify('+10 years');
                        break;
                }
                $expire_date = $date->format("Y-m-d");
                $expire_date .= " 23:59:59";
            } else {
                $expire_date = $rule_expire;
            }
        } else {
            $expire_date = '1000-01-01 00:00:00';
        }

        return $expire_date;
    }

    public static function checkChangeLevel($referrerid, $details) {
        $ok = false;
        $userpoints = PointsUser::withReferrerID($referrerid)->points;
        $userid = self::getUserID($referrerid);
        $authorizedLevels = Access::getAuthorisedViewLevels($userid);

        $lang = Factory::getLanguage();
        $lang->load('com_userpoints', JPATH_SITE);

        $resultChangeLevel1 = self::checkRuleEnabled('sysplgup_changelevel1', 0, $referrerid);
        $resultChangeLevel2 = self::checkRuleEnabled('sysplgup_changelevel2', 0, $referrerid);
        $resultChangeLevel3 = self::checkRuleEnabled('sysplgup_changelevel3', 0, $referrerid);

        // Upward change
        if (self::checkUpwardChange($resultChangeLevel1, $userpoints, $authorizedLevels, $userid)) {
            $resultChangeLevel = $resultChangeLevel1;
            $ok = true;
        }
        if (self::checkUpwardChange($resultChangeLevel2, $userpoints, $authorizedLevels, $userid)) {
            $resultChangeLevel = $resultChangeLevel2;
            $ok = true;
        }
        if (self::checkUpwardChange($resultChangeLevel3, $userpoints, $authorizedLevels, $userid)) {
            $resultChangeLevel = $resultChangeLevel3;
            $ok = true;
        }

        // Downward change
        if (self::checkDownwardChange($resultChangeLevel3, $userpoints, $userid)) {
            $ok = true;
        }
        if (self::checkDownwardChange($resultChangeLevel2, $userpoints, $userid)) {
            $ok = true;
        }
        if (self::checkDownwardChange($resultChangeLevel1, $userpoints, $userid)) {
            $ok = true;
        }

        if (isset($resultChangeLevel)) {
            self::finaliseLevelChange($ok, $referrerid, $userid, $resultChangeLevel, $userpoints, $details);
        }
    }

    private static function finaliseLevelChange($ok, $referrerid, $userid, $rule, $userpoints, $details) {
        // new name group
        $result = UserHelper::getUserGroups($userid);
        $actualnamegroup = end($result);
        $db = self::getDBO();
        $q = "SELECT `title` FROM `#__usergroups` WHERE `id`={$db->quote($actualnamegroup)} ";
        $db->setQuery($q);
        $nameUserGroup = $db->loadResult();

        self::LevelChangeMessage($referrerid, $userid, $rule, $nameUserGroup, $userpoints, $ok);

        if ($ok) {
            // get params definitions
            $params = ComponentHelper::getParams('com_userpoints');
            // insert this new activity in database
            $datareference = sprintf(Text::_('AUP_DESCRIPTIONACTIVITYONCHANGELEVEL'), self::getFormattedPoints($rule->points2), $nameUserGroup);
            if ($params->get('insertAllActivities', 0)) {
                self::insertUserPoints($referrerid, $rule, 0.0, '', $datareference);
            }
            // load external plugins
            PluginHelper::importPlugin('userpoints');
            //Send notification
            if ($rule->notification) {
                self::sendnotification($referrerid, $details, $rule);
                $rule_name = Text::_($rule->rule_name);
                $results = self::triggerEvent('onSendNotificationUserPoints', array(&$rule, $rule_name, $rule->points2, $userpoints, $referrerid, $userid));
            }
            $results = self::triggerEvent('onChangeLevelUserPoints', array(&$rule, $actualnamegroup, $userid, $referrerid));
        }
    }

    private static function LevelChangeMessage($referrerid, $userid, $rule, $nameUserGroup, $userpoints, $ok) {
        // show message only for current user and if frontend site
        if ($referrerid == Session::get('referrerid') AND Factory::getApplication()->isClient('site') AND $ok) {
            $user = self::getUser($userid);
            // display message for the current user
            $symbols = ['{username}', '{points}', '{newtotal}'];
            $substitutions = [$user->username, self::getFormattedPoints($rule->points), self::getFormattedPoints($userpoints)];
            if ($rule->displaymsg AND $rule->msg != '') {
                self::displayMessageSystem(str_replace($symbols, $substitutions, $rule->msg));
            } elseif ($rule->displaymsg AND $rule->msg == '') {
                self::displayMessageSystem(sprintf(Text::_('AUP_MSG_YOUHAVENEWUSERRIGHTS'), self::getFPoints($rule->points2), $nameUserGroup));
            }
        }
    }

    private static function checkUpwardChange($rule, $userpoints, $authorizedLevels, $userid) {
        if ($rule instanceof Rule) {
            $group = max(1, intval($rule->content_items));
            $checkAlreadyDone = explode(',', $rule->exclude_items);
            if ($rule AND $userpoints >= $rule->points2 && !in_array($group, $authorizedLevels) && !in_array($userid, $checkAlreadyDone)) {
                // Delete from old group
                Rule::removeFromChangeLevelsGroups($userid);
                // Add to new group
                self::addToGroup($userid, $group);

                // Caller has to do this
                // $resultChangeLevel = $rule;

                $rule->exclude_items = ($rule->exclude_items != '') ? "$rule->exclude_items,$userid" : $userid;
                $rule->save();
                return true;
            }
        }
        return false;
    }

    private static function checkDownwardChange($rule, $userpoints, $userid) {
        if ($rule instanceof Rule) {
            $group = max(1, intval($rule->content_items));
            $checkAlreadyDone = explode(',', $rule->exclude_items);
            if ($rule AND $userpoints < $rule->points2 AND in_array($userid, $checkAlreadyDone)) {
                // delete to list checkAlreadyDone
                unset($checkAlreadyDone[array_search($userid, $checkAlreadyDone)]);
                $rule->exclude_items = implode(',', $checkAlreadyDone);
                $rule->save();
                // Delete from old group
                Rule::removeFromChangeLevelsGroups($userid);
                return true;
            } else {
                // Add to new group
                self::addToGroup($userid, $group);
            }
        }
        return false;
    }

    private static function addToGroup($userid, $group) {
        $db = self::getDBO();
        $q = "INSERT INTO `#__user_usergroup_map` VALUES ($userid, $group) "
                . " ON DUPLICATE KEY UPDATE `user_id` = $userid";
        $db->setQuery($q);
        $db->execute();
    }

    public static function getUserID($referrerid) {
        $user = PointsUser::withReferrerID($referrerid);
        return $user ? (int) $user->userid : 0;
    }

    public static function getRuleID($plugin_function) {
        return (int) Rule::withRuleName($plugin_function)->id;
    }

    public static function getPointsRule($plugin_function, $published = false) {
        $rule = Rule::withRuleName($plugin_function);
        if ($published AND !$rule->published) {
            return 0.0;
        }
        return (float) $rule->points;
    }

    public static function getPointsRule2($plugin_function) {
        $rule = Rule::withRuleName($plugin_function);
        return (float) $rule->points2;
    }

    public static function getNameRule($plugin_function) {
        return (string) Rule::withRuleName($plugin_function)->rule_name;
    }

    public static function getPluginFunction($id_rule) {
        return (string) Rule::withRuleID($id_rule)->plugin_function;
    }

    public static function getDatareference($idActivity): string {
        if (!$idActivity) {
            $db = self::getDBO();
            $q = "SELECT `datareference` FROM #__alpha_userpoints_details WHERE `id`=" . $db->quote($idActivity) . " ";
            $db->setQuery($q);
            $datareference = $db->loadResult();
            return $datareference;
        }
        return '';
    }

    public static function getMethod($plugin_function) {
        $rule = Rule::withRuleName($plugin_function);
        return $rule->published ? $rule->method : 0;
    }

    public static function buildKeyreference($plugin_function, $spc = '', $userID = 0) {
        $user = self::getUser();

        $method = self::getMethod($plugin_function);

        $timenow = SQL::unixtime();
        $date = date('Y-m-d', $timenow);
        $week = date('W', $timenow);
        $month = date('m', $timenow);
        $year = date('Y', $timenow);

        $keyreference = '';

        if (!$userID) {
            $userID = $user->id;
        }

        if ($spc != '') {
            $spc .= '|';
        }

        switch ($method) {
            case '1':  // ONCE PER USER
                $keyreference = $spc . $userID;
                break;
            case '2':  // ONCE PER DAY AND PER USER'
                $keyreference = $spc . $userID . '|' . $date;
                break;
            case '3':  // ONCE A DAY FOR A SINGLE USER ON ALL USERS
                $keyreference = $spc . $date;
                break;
            case '5':       // ONCE PER USER PER WEEK
                $keyreference = $spc . $userID . '|' . $week . '|' . $year;
                break;
            case '6':       // ONCE PER USER PER MONTH
                $keyreference = $spc . $userID . '|' . $month . '|' . $year;
                break;
            case '7':       // ONCE PER USER PER YEAR
                $keyreference = $spc . $userID . '|' . $year;
                break;
            case '4':       // WHENEVER
            case '0':
            default:
                $keyreference = $spc . uniqid('', false);
        }

        return $keyreference;
    }

    public static function getIdByKeyreference($keyreference) {
        $db = self::getDBO();
        $query = "SELECT `id` FROM #__alpha_userpoints_details WHERE `keyreference`= {$db->quote($keyreference)} AND enabled='1'";
        $db->setQuery($query);
        return $db->loadResult();
    }

    public static function getRuleByPluginFunction($function) {
        $rule = Rule::withRuleName($function);
        $authorizedLevels = Access::getAuthorisedViewLevels(self::getUser()->id);
        return $rule->checkForAccess($authorizedLevels) ? $rule : false;
    }

    public static function checkExcludeUsers($referreID) {
        $db = self::getDBO();
        $q = "SELECT referreid FROM #__alpha_userpoints
                WHERE `referreid`={$db->quote($referreID)} AND `published`='1'";
        $db->setQuery($q);
        $result = $db->loadResult();
        if ($result) {
            return 1;
        }
        return 0;
    }

    public static function checkRuleEnabled($plugin_function = '', $force = 0, $referrerid = '') {
        if (!$referrerid) {
            $referrerid = Session::get('referrerid');
            $userID = $referrerid ? self::getUserID($referrerid) : self::getUser()->id;
        } else {
            $userID = self::getUserID($referrerid);
        }
        $rule = Rule::withRuleName($plugin_function);
        if ($rule->checkForAccess(Access::getAuthorisedViewLevels($userID), $force)) {
            return $rule;
        }
        return null;
    }

    public static function checkReference(string $referrerid, string $keyreference, int $ruleid) {
        $db = self::getDBO();

        $plugin_function = self::getPluginFunction($ruleid);
        $method = self::getMethod($plugin_function);

        switch ($method) {
            case '3':
                $q = "SELECT count(*) FROM #__alpha_userpoints_details
                                WHERE `keyreference`={$db->quote($keyreference)} AND `rule_id`= '$plugin_function' AND enabled='1'";
                break;
            default:
                $q = "SELECT count(*) FROM #__alpha_userpoints_details
                                WHERE `referreid`={$db->quote($referrerid)} AND `keyreference`={$db->quote($keyreference)}
                                AND `rule_id` = '$plugin_function' AND enabled='1'";
        }
        $db->setQuery($q);
        // Build Key Reference change on format date version 1.9.3
        $resultKeyReference = str_replace('%', '', $db->loadResult());
        return $resultKeyReference;
    }

    public static function getReferreid(int $userID = 0): string {
        if (!$userID) {
            $user = self::getUser();
            $userID = is_object($user) ? $user->id : 0;
        }
        // get referre ID on login
        $referreid = self::getAnyUserReferreID($userID);
        if ($referreid) {
            // @session_start('userpoints');
            Session::set('referrerid', $referreid);
        }
        return $referreid;
    }

    public static function getAnyUserReferreID(int $userID): string {
        // get referre ID for an author etc...
        $pointsuser = PointsUser::withUserID($userID);
        return (string) $pointsuser->referreid;
    }

    public static function getUserInfo($referrerid = '', $userid = '') {
        $info = self::$userinfo_by_refer[$referrerid] ?? self::$userinfo_by_id[$userid] ?? null;
        if (!$info AND ($referrerid OR $userid)) {
            $db = self::getDBO();
            $where = $referrerid ? "a.referreid={$db->quote($referrerid)} " : "a.userid={$db->quote($userid)} ";
            $db->setQuery("SELECT a.*, a.id AS rid FROM #__alpha_userpoints_combined AS a WHERE $where");
            $info = $db->loadObject();
            self::$userinfo_by_refer[$info->referreid] = $info;
            self::$userinfo_by_id[$info->id] = $info;
        }
        return $info;
    }

    public static function sendnotification($referrerid, $details, $rule, $force = 0) {
        if (!$referrerid OR $referrerid == 'GUEST' OR !$rule->published) {
            return;
        }
        $lang = Factory::getLanguage();
        $lang->load('com_userpoints', JPATH_SITE);
        jimport('joomla.mail.helper');
        // get params definitions
        $params = ComponentHelper::getParams('com_userpoints');

        $userinfo = self::getUserInfo($referrerid);

        $subject = $rule->emailsubject;
        $body = $rule->emailbody;

        if (!$userinfo->block || $force) {
            self::insertValues($subject, $body, $referrerid, $rule, $details->points);

            if (!$params->get('jsNotification', 0)) {
                self::mailerNotify($userinfo, $subject, $body, $rule);
            } else {
                self::jomSocialNotify($userinfo, $subject, $body, $rule);
            }
        }
    }

    private static function mailerNotify($userinfo, $subject, $body, $rule) {
        $app = Factory::getApplication();
        $mailer = Factory::getMailer();
        $mailer->setSender(array($app->getCfg('mailfrom'), $app->getCfg('fromname')));
        $mailer->setSubject($subject);
        $mailer->isHTML((bool) $rule->emailformat);
        if ($rule->emailformat) {
            $mailer->Encoding = 'base64';
        }
        $mailer->CharSet = "utf-8";
        $mailer->setBody($body);
        $mailer->addRecipient($userinfo->email);
        if ($rule->emailformat) {
            $mailer->Encoding = 'base64';
        }
        if ($rule->bcc2admin) {
            // get all users allowed to receive e-mail system
            $db = self::getDBO();
            $q = "SELECT email" .
                    " FROM #__users" .
                    " WHERE sendEmail='1' AND block='0'";
            $db->setQuery($q);
            $rowsAdmins = $db->loadObjectList();

            foreach ($rowsAdmins as $rowsAdmin) {
                $mailer->addBCC($rowsAdmin->email);
            }
        }
        $mailer->Send();
    }

    private static function jomSocialNotify($userinfo, $subject, $body, $rule) {
        require_once JPATH_ROOT . '/components/com_community/libraries/core.php';
        //$actor = Cself::getUser();
        $params = new \CParameter('');
        $jsNotificationAdmin = $params->get('fromIdUddeim', 0);
        \CNotificationLibrary::add('system_messaging', $jsNotificationAdmin, $userinfo->id, $subject, $body, '', $params);
        if ($rule->bcc2admin) {
            // get all users allowed to receive e-mail system
            $db = self::getDBO();
            $q = "SELECT id" .
                    " FROM #__users" .
                    " WHERE sendEmail='1' AND block='0'";
            $db->setQuery($q);
            $rowsAdmins = $db->loadObjectList();

            foreach ($rowsAdmins as $rowsAdmin) {
                //$mailer->addBCC( $rowsAdmin->id );
                \CNotificationLibrary::add('system_messaging', $userinfo->id, $rowsAdmin->id, $subject, $body, '', $params);
            }
        }
    }

    private static function insertValues(&$subject, &$body, $referrerid, $rule, $assignpoints) {
        $userinfo = self::getUserInfo($referrerid);
        if ($subject AND $body) {
            $trkeys = ['{username}', '{name}', '{email}', '{points}', '{newtotal}'];
            $trvalues = [
                $userinfo->username,
                $userinfo->name,
                $userinfo->email,
                self::getFPoints(abs($assignpoints)),
                self::getFPoints($userinfo->points)
            ];
            $subject = str_replace($trkeys, $trvalues, $subject);
            $trkeys[] = '{datareference}';
            $trvalues[] = $rule->datareference;
            $body = str_replace($trkeys, $trvalues, $body);
        } else {
            // default message
            if ($assignpoints > 0) {
                $subject = Text::_('AUP_EMAILNOTIFICATION_SUBJECT');
                $body = sprintf(Text::_('AUP_EMAILNOTIFICATION_MSG'),
                        Factory::getApplication()->getCfg('sitename'),
                        self::getFPoints($assignpoints),
                        self::getFPoints($userinfo->points), Text::_($rule->rule_name));
            } elseif ($assignpoints < 0) {
                $subject = Text::_('AUP_EMAILNOTIFICATION_SUBJECT_ACCOUNT_UPDATED');
                $body = sprintf(Text::_('AUP_EMAILNOTIFICATION_MSG_REMOVE_POINTS'),
                        Factory::getApplication()->getCfg('sitename'),
                        self::getFPoints(abs($assignpoints)),
                        self::getFPoints($userinfo->points), Text::_($rule->rule_name));
            }
        }
        $subject = MailHelper::cleanSubject($subject);
        $body = MailHelper::cleanBody($body);
    }

    public static function sendUddeimNotification($referrerid, $assignpoints, $rule_name) {
        $app = Factory::getApplication();

        $lang = Factory::getLanguage();
        $lang->load('com_userpoints', JPATH_SITE);

        // integration Uddeim
        if (!$referrerid || $referrerid == 'GUEST') {
            return;
        }

        // check if component installed
        $uddeim_exist = JPATH_ADMINISTRATOR . '/components/com_uddeim/admin.uddeimlib15.php';
        if (!file_exists($uddeim_exist)) {
            return;
        }

        // get params definitions
        $params = ComponentHelper::getParams('com_userpoints');

        $SiteName = $app->getCfg('sitename');
        $userinfo = self::getUserInfo($referrerid);
        $fromIdUddeim = intval($params->get('fromIdUddeim', 0));

        if (!$userinfo->block && $fromIdUddeim > 0) {
            require_once JPATH_SITE . "/components/com_uddeim/uddeim.api.php";

            if ($assignpoints > 0) {
                $message = sprintf(Text::_('AUP_EMAILNOTIFICATION_MSG'), $SiteName, self::getFPoints($assignpoints), self::getFPoints($userinfo->points), Text::_($rule_name));
            } elseif ($assignpoints < 0) {
                $message = sprintf(Text::_('AUP_EMAILNOTIFICATION_MSG_REMOVE_POINTS'), $SiteName, self::getFPoints(abs($assignpoints)), self::getFPoints($userinfo->points), Text::_($rule_name));
            }
            $uddeimapi = new uddeIMAPI();
            $uddeimapi->sendNewMessage($fromIdUddeim, $userinfo->userid, $message);
        }
    }

    public static function sendUddeimWinnerNotification($referrerid) {
        $app = Factory::getApplication();

        $lang = Factory::getLanguage();
        $lang->load('com_userpoints', JPATH_SITE);

        // get params definitions
        $params = ComponentHelper::getParams('com_userpoints');

        if (!$referrerid || $referrerid == 'GUEST') {
            return;
        }

        // check if component installed
        $uddeim_exist = JPATH_ADMINISTRATOR . '/components/com_uddeim/admin.uddeimlib15.php';
        if (!file_exists($uddeim_exist)) {
            return;
        }

        $userinfo = self::getUserInfo($referrerid);
        $name = $userinfo->name;
        $fromIdUddeim = intval($params->get('fromIdUddeim', 0));

        if (!$userinfo->block && $fromIdUddeim > 0) {
            require_once JPATH_SITE . "/components/com_uddeim/uddeim.api.php";
            // send notification to winner
            $message = sprintf(Text::_('AUP_EMAILWINNERNOTIFICATION_MSG_USER'), $name, self::getFPoints($userinfo->points));
            $uddeimapi = new uddeIMAPI();
            $uddeimapi->sendNewMessage($fromIdUddeim, $userinfo->userid, $message);
        }
    }

    public static function sendwinnernotification($referrerid, $emailadmins = '') {
        $app = Factory::getApplication();
        jimport('joomla.mail.helper');

        $lang = Factory::getLanguage();
        $lang->load('com_userpoints', JPATH_SITE);

        if (!$referrerid || $referrerid == 'GUEST') {
            return;
        }
        $MailFrom = $app->getCfg('mailfrom');
        $FromName = $app->getCfg('fromname');
        $userinfo = self::getUserInfo($referrerid);
        $name = $userinfo->name;
        $email = $userinfo->email;
        $formatMail = 1;
        if (!$userinfo->block && $userinfo->points) {
            // send notification to winner
            $subject = Text::_('AUP_EMAILWINNERNOTIFICATION_SUBJECT_MSG_USER');
            $body = sprintf(Text::_('AUP_EMAILWINNERNOTIFICATION_MSG_USER'), $name, self::getFPoints($userinfo->points));

            $mailer = Factory::getMailer();
            $mailer->setSender(array($MailFrom, $FromName));
            $mailer->setSubject($subject);
            $mailer->isHTML((bool) $formatMail);
            if ($formatMail) {
                $mailer->Encoding = 'base64';
            }
            $mailer->CharSet = "utf-8";
            $mailer->setBody($body);
            $mailer->addRecipient($email);
            if ($emailadmins) {
                $mailer->addBCC($emailadmins);
            }
            $mailer->Send();
        }
    }

    public static function getCurrentTotalPoints($referreid = '', $userid = 0, $formatted = 0) {
        $db = self::getDBO();
        if ($referreid != '') {
            $condition = "`referreid`={$db->quote($referreid)}";
        } elseif ($userid > 0) {
            $condition = "`userid`={$db->quote(intval($userid))}";
        } else {
            return 0;
        }
        $q = "SELECT points FROM #__alpha_userpoints_combined WHERE $condition AND `blocked` = 0";
        $db->setQuery($q);
        $currenttotalpoints = (float) $db->loadResult();
        return $formatted ? self::getFormattedPoints($currenttotalpoints) : $currenttotalpoints;
    }

    public static function operationIsFeasible($referreid = '', $numpoints = 0) {
        if ($referreid) {
            return (self::getCurrentTotalPoints($referreid) + $numpoints) >= 0 ? true : false;
        }
    }

    public static function checkMaxDailyPoints($referreid, $numpoints, $plugin_function = '') {
        // except for raffles, coupons code, bonus points, custom rule, profile etc...
        switch ($plugin_function) {
            case 'sysplgup_raffle':
            case 'sysplgup_couponpointscodes':
            case 'sysplgup_buypointswithpaypal':
            case 'sysplgup_bonuspoints':
            case 'sysplgup_custom':
                return $numpoints;
            default:
                $maxdailypoints = ComponentHelper::getParams('com_userpoints')->get('limit_daily_points', 0);
        }
        if ($referreid AND $maxdailypoints) {
            $sptotal = PointsDetails::dailyPlusPoints($referreid) + $numpoints;
            return min($numpoints, $numpoints + $maxdailypoints - $sptotal);
        }
        return $numpoints;
    }

    public static function getUserRank(string $referrerid = '', int $userid = 0) {
        if (!$referrerid && !$userid) {
            return;
        }
        $db = self::getDBO();
        $q = "SELECT lv.*, aup.leveldate FROM #__alpha_userpoints_levelrank AS lv, #__alpha_userpoints AS aup
                WHERE lv.id=aup.levelrank AND (aup.referreid={$db->quote($referrerid)} OR aup.userid=$userid)";
        $db->setQuery($q);
        $userrankinfo = $db->loadObject();
        return $userrankinfo;

        // detail/explain
        // return $userrankinfo->id
        // return $userrankinfo->rank (name of rank)
        // return $userrankinfo->description (description of rank)
        // return $userrankinfo->levelpoints (level points to reach this rank)
        // return $userrankinfo->typerank (return 0)
        // return $userrankinfo->icon (name file icon)
        // return $userrankinfo->image (name file image)
    }

    public static function getUserMedals($referrerid = '', $userid = 0) {
        return ($referrerid OR $userid) ? Medal::userMedals($referrerid, $userid) : [];

        // detail/explain
        // sample -> for each ( $medalslistuser as medallistuser ) {
        // return $medallistuser->id
        // return $medallistuser->rank (name of medal)
        // return $medallistuser->description (reason for awarded)
        // return $medallistuser->icon (name file icon)
        // return $medallistuser->image (name file image)
    }

    private static function getUserMedalCounts(int $id): array {
        return Medal::byRid($id);
    }

    private static function getUserPoints($referrerid) {
        $db = self::getDBO();
        $now = SQL::datetimeSQL();
        $query = "SELECT rule_id, SUM(points) AS points FROM #__alpha_userpoints_details WHERE approved='1' AND referreid={$db->quote($referrerid)} " .
                " AND rule > 0 AND (`expire_date` > '$now' OR `expire_date`='1000-01-01 00:00:00') AND `enabled`='1' GROUP BY rule";
        $db->setQuery($query);
        foreach ($db->loadObjectList() as $point) {
            $result[$point->rule_id] = $point->points;
        }
        return isset($result) ? $result : [];
    }

    public static function checkRankMedal($referrerid = '', $rule_id = 0) {
        // check level/rank/ medals by level points
        if (!$referrerid OR !self::getLevelRankCount()) {
            return;
        }
        $userinfo = self::getUserInfo($referrerid);
        // load external plugins
        PluginHelper::importPlugin('userpoints');
        self::checkRankMedalOneUser($referrerid, $userinfo);
    }

    public static function recalcPointsAndMedals() {
        $db = self::getDBO();
        $now = SQL::datetimeSQL();
        $query = "SELECT a.userid, a.referreid, a.upnid, SUM(d.points) AS points, a.last_update, "
                . "a.referraluser, a.referrees, a.blocked, a.avatar, a.levelrank, a.leveldate, "
                . " a.profileviews, a.shareinfos, a.id AS rid, u.* "
                . "FROM #__alpha_userpoints AS a INNER JOIN #__users AS u ON a.userid = u.id"
                . " INNER JOIN #__alpha_userpoints_details AS d ON a.referreid = d.referreid "
                . " WHERE d.approved='1' AND (d.expire_date > '$now' OR d.expire_date = '1000-01-01 00:00:00') AND `enabled`='1' "
                . " HAVING SUM(d.points) IS NOT NULL";
        $db->setQuery($query);
        $userinfos = $db->loadObjectList();
        foreach ($userinfos as $userinfo) {
            self::checkRankMedalOneUser($userinfo->referreid, $userinfo);
        }
    }

    private static function checkRankMedalOneUser($referrerid, $userinfo) {
        $oldrank = $userinfo->levelrank;

        $userpoints = self::getUserPoints($referrerid);
        $previousrank = 0;

        // check level/rank
        foreach (self::getLevelRanks(USERPOINTS_LEVEL_BY_RANK) as $rank) {
            $newrank = $userinfo->points < $rank->levelpoints ? $previousrank : $rank->id;
            $previousrank = $rank->id;
        }
        if (isset($newrank) AND $newrank != $oldrank) {
            self::doSQL(sprintf("UPDATE #__alpha_userpoints SET levelrank = %d, leveldate = '%s' WHERE id = %d", $newrank, SQL::datetimeSQL(), $userinfo->rid));
            $rankdetails = self::getLevelRankByID($newrank);
            $results = self::triggerEvent('onGetNewRankUserPoints', array(&$userinfo, &$rankdetails));
        }

        // check medal(s)
        $medals = self::getLevelRanks(USERPOINTS_LEVEL_BY_MEDAL);
        $medalcounts = self::getUserMedalCounts($userinfo->rid);
        foreach ($medals as $medal) {
            $currentpoints = $userpoints[$medal->rule_id] ?? $userinfo->points;
            if ($medal->levelpoints <= $currentpoints) {
                // check if user medal not already awarded
                if (empty($medalcounts[$medal->id])) {
                    // add medal
                    $newmedal = new Medal();
                    $newmedal->rid = intval($userinfo->rid);
                    $newmedal->medal = intval($medal->id);
                    $newmedal->medaldate = Medal::datetimeSQL();
                    $newmedal->reason = $medal->description;
                    $newmedal->save();
                    $results = self::triggerEvent('onUnlockMedalUserPoints', array(&$userinfo, &$medal));
                    break;
                }
            } else {
                // check if user medal not already awarded
                if (!empty($medalcounts[$medal->id])) { //if he has, we delete it as he should not have it
                    // delete medal
                    Medal::deleteWithRid($userinfo->rid, $medal->id);
                    break;
                }
            }
        }
    }

    public static function getNextUserRank($referrerid, $userid, $currentrank) {
        if (!$referrerid && !$userid) {
            return;
        }
        //get all ranks info order by levelpoints
        $db = self::getDBO();
        $q = "SELECT * FROM #__alpha_userpoints_levelrank WHERE `typerank` = 0 ORDER BY `levelpoints` ASC;";
        $db->setQuery($q);
        $nextrankinfo = $db->loadObjectList();
        //create a single level index array
        $rankindex = array();
        foreach ($nextrankinfo as $onerank) {
            //copy just rank to search for index
            $rankindex[] = $onerank->rank;
        }
        //find the index of the current rank
        $index = array_search($currentrank, $rankindex);
        //increase index to next
        $index = $index + 1;

        return $nextrankinfo[$index];

        // detail/explain
        // return $nextrankinfo->id
        // return $nextrankinfo->rank (name of rank)
        // return $nextrankinfo->description (description of rank)
        // return $nextrankinfo->levelpoints (level points to reach this rank)
        // return $nextrankinfo->typerank (return 0)
        // return $nextrankinfo->icon (name file icon)
        // return $nextrankinfo->image (name file image)
    }

    public static function getAupLinkToProfil($userid, $Itemid = '', $xhtml = true) {
        if (!$userid) {
            return;
        }

        ($Itemid != '') ? $theItemid = "&amp;Itemid=" . $Itemid : $theItemid = self::getItemidAupProfil();
        $profile = self::getUserInfo('', $userid);
        $linktoprofil = "index.php?option=com_userpoints&amp;view=account&amp;userid=" . $profile->referreid . $theItemid;
        $linktoprofil = Route::_($linktoprofil, $xhtml);
        return $linktoprofil;

        // USAGE
        // $linktoAUPprofil = self::getAupLinkToProfil($userid);   OR  $linktoAUPprofil = self::getAupLinkToProfil($userid, $YourItemid);
        // Think to call and include this API helper.php in your script
    }

    public static function getAupUsersURL($xhtml = true) {
        $db = self::getDBO();
        $q = "SELECT id FROM #__menu
		WHERE `link`='index.php?option=com_userpoints&view=users'
		AND `type`='component' AND `published`='1'";
        $db->setQuery($q);
        $AupItemidUsers = $db->loadResult();
        $AupUsersURL = "index.php?option=com_userpoints&amp;view=users&amp;Itemid=" . $AupItemidUsers;
        $AupUsersURL = Route::_($AupUsersURL, $xhtml);
        return $AupUsersURL;
    }

    public static function getItemidAupProfil() {
        $db = self::getDBO();
        $q = "SELECT id FROM #__menu
		WHERE `link`='index.php?option=com_userpoints&view=account'
		AND `type`='component' AND `published`='1'";
        $db->setQuery($q);
        $AupItemidProfile = $db->loadResult();
        return $AupItemidProfile;
    }

    public static function displayMessageSystem($message) {
        if ($message) {
            $app = Factory::getApplication();
            $app->enqueueMessage(Text::_($message));
        }
    }

    public static function getUpVersion() {
        return USERPOINTS_NUM_VERSION;
    }

    public static function getMedalsPath() {
        return Uri::root() . 'components/com_userpoints/assets/images/awards/large/';
    }

    public static function getMedalsLivePath() {
        return Uri::base(true) . '/components/com_userpoints/assets/images/awards/large/';
    }

    public static function getAupVersion() {
        return USERPOINTS_NUM_VERSION;
        ;
    }

    // get list of activities
    // $params  $type = all | positive | negative
    // $params  $userid = all or unique userID
    // $params  $limit = int (0 by default)
    // example-1 -> --------------------------------------------------------------------------
    // example-1 -> $listActivity = self::getListActivity('all', 'all');
    // example-1 SAME AS -> $listActivity = self::getListActivity();
    // example-1 -> show all acivities with pagination, positive and negative points of activity for all users
    // ------------------------------------------------------------------------------------
    // example-2 -> --------------------------------------------------------------------------
    // example-2 -> $user =  self::getUser();
    // example-2 -> $userID = $user->id;
    // example-2 -> $listActivity = self::getListActivity('positive', $userID, 20);
    // example-2 -> show only positive points of activity for the current user logged in and show only 20 rows of recent activities
    //
    // return an array or $rows
    //
    // list of available fields :
    // insert_date
    // referreid
    // points (of this activity)
    // datareference
    // rule_name
    // plugin_function
    // category

    public static function getListActivity($type = 'all', $userid = 'all', $numrows = 0) {
        $app = Factory::getApplication();
        $db = self::getDBO();
        $now = SQL::datetimeSQL();
        $typeActivity = "";
        $userID = "";

        switch ($type) {
            case 'positive':
                $typeActivity = " AND a.points >= 0";
                break;
            case 'negative':
                $typeActivity = " AND a.points <= 0";
                break;
            case 'all':
            default:
                $typeActivity = "";
        }

        switch ($userid) {
            case ( $userid > 0 && $userid != 'all' ):
                $userID = " AND aup.userid = {$db->quote($userid)} ";
                break;
            case ( $userid = 0 ):
                return;
            case 'all':
            default:
                $userID = "";
        }

        $q = "SELECT a.insert_date, a.referreid, a.points, a.datareference,
		r.rule_name, r.plugin_function, r.category
		FROM #__alpha_userpoints_details AS a, #__alpha_userpoints AS aup, #__users AS u, #__alpha_userpoints_rules AS r
		WHERE aup.referreid=a.referreid AND aup.userid=u.id AND aup.published='1' AND a.approved='1' AND a.enabled='1'
		AND (a.expire_date>='" . $now . "' OR a.expire_date='1000-01-01 00:00:00') AND a.rule_id=r.plugin_function AND r.displayactivity='1'"
                . $typeActivity
                . $userID
                . " ORDER BY a.insert_date DESC"
        ;

        $countquery = "SELECT COUNT(a.referreid)
		FROM #__alpha_userpoints_details AS a, #__alpha_userpoints AS aup, #__users AS u, #__alpha_userpoints_rules AS r
		WHERE aup.referreid=a.referreid AND aup.userid=u.id AND aup.published='1' AND a.approved='1' AND a.enabled='1'
		AND (a.expire_date>='" . $now . "' OR a.expire_date='1000-01-01 00:00:00') AND a.rule_id=r.plugin_function AND r.displayactivity='1'"
                . $typeActivity
                . $userID
        ;

        $db->setQuery($countquery);
        $total = $db->loadResult();

        if ($numrows) {
            $q .= " LIMIT " . intval($numrows);
            $db->setQuery($q);
            $result = $db->loadObjectList();
            return $result;
        } else {
            // Get the pagination request variables
            list($limitstart, $limit) = PointsUser::getLimitsFromRequest($app->input->get('limit', $app->getCfg('list_limit'), 'int'));
            $q .= " LIMIT $limitstart, $limit";
            $db->setQuery($q);
            $result = $db->loadObjectList();
            return array($result, $total, $limit, $limitstart);
        }
    }

    public static function getFormattedPoints($p) {
        $formatPoints = ComponentHelper::getParams('com_userpoints')->get('formatPoints', '0');
        $points = (float) $p;
        switch ($formatPoints) {
            case "1":
                $fpoints = number_format($points, 2, '.', ',');
                break;
            case "2":
                $fpoints = number_format($points, 2, ',', '');
                break;
            case "3":
                $fpoints = number_format($points, 2, ',', ' ');
                break;
            case "4":
                $fpoints = number_format($points, 0, '', '');
                break;
            case "5":
                $fpoints = number_format($points, 0, '', ' ');
                break;
            case "6":
                $fpoints = number_format($points, 0, '', ',');
                break;
            case "7":
                $fpoints = number_format(floor($points), 0, '', '');
                break;
            case "8":
                $fpoints = number_format(floor($points), 0, '', ' ');
                break;
            case "9":
                $fpoints = number_format(floor($points), 0, '', ',');
                break;
            case "0":
            default:
                $fpoints = number_format($points, 2);
        }
        return $fpoints;
    }

    public static function getFPoints($points) {
        return self::getFormattedPoints($points);
    }

    public static function getMenuParameters() {
        $menus = Factory::getApplication()->getMenu();
        return $menus->getParams($menus->getActive()->id);
    }

    public static function trace() {
        static $counter = 0;
        $html = '';
        foreach (debug_backtrace() as $back) {
            if (isset($back['file']) AND $back['file']) {
                $html .= "\n<br />" . $back['file'] . ':' . $back['line'];
            }
        }
        $counter++;
        if (1000 < $counter) {
            echo $html;
            die('Program killed - Probably looping');
        }
        return $html;
    }
}

/*
if (0 == UserPoints::getUser()->id) {
    Session::set('referrerid');
}
 *
 */
