<?php
/**
* @package XenCentral Ad Server
* @author Skydevelop EU
* @copyright Drnoyan & Nalyan LDA, Portugal, EU
* @license http://www.dnf.pt/eula.html
* @link http://www.skydevelop.com
* @revision 97
* @version 2.0.0 beta 3 rev. 3
*/


class XenCentral_AdServer_Model_Stats extends XenCentral_AdServer_Model_Abstract
{
    protected $_last_campaign_click_log_id;
    protected $_last_banner_click_log_id;
    public function getBannerStatisticsForExport($banners, $conditions)
    {
        $bannersInfoArray = $this->getBannerModel()->getAllBanners();

        $mergedStats = array();

        foreach ($banners AS $banner) {
            $bannerDbId = $banner['bannerId'];

            $conditions['bannerId'] = $bannerDbId;

            $clickBannerArray = $this->getBannerClickStatisticsForExport($conditions);
            $impressionsBannerArray = $this->getBannerImpressionStatisticsForExport($conditions);

            foreach ($impressionsBannerArray AS $date => $info) {
                if (empty($clickBannerArray[$date])) {
                    $impressionsBannerArray [$date]['click'] = 0;
                } else {
                    $impressionsBannerArray[$date]['click'] = $clickBannerArray[$date]['click'];
                }
            }

            $mergedStats[$bannerDbId] = array(
                'bannerInfo' => $bannersInfoArray[$bannerDbId],
                'bannerStatistics' => $impressionsBannerArray
            );
        }

        return $mergedStats;
    }

    public function getBannerImpressionStatisticsForExport($conditions)
    {
        $stats = $this->_getDb()->fetchAll("
            SELECT dateline, count  FROM xcas_impressions_banner
            WHERE bannerId= " . $conditions['bannerId'] . "
            AND dateline > " . $conditions['start_date'] . "
            AND dateline < " . $conditions['end_date'] . "
        ");

        $groupedArray = array();
        foreach ($stats as $record) {
            $date = XenForo_Locale::date($record['dateline'], 'd/m');

            if (empty($groupedArray[$date])) {
                $groupedArray[$date] = array(
                    'dateline' => $date,
                    'count' => 0
                );
            }
            $groupedArray[$date]['count'] += $record['count'];
        }

        return $groupedArray;
    }

    public function getBannerClickStatisticsForExport($conditions)
    {
        $stats = $this->_getDb()->fetchAll("
            SELECT dateline FROM xcas_clicks_banner
            WHERE bannerId= " . $conditions['bannerId'] . "
            AND dateline > " . $conditions['start_date'] . "
            AND dateline < " . $conditions['end_date'] . "
        ");

        $groupedArray = array();
        foreach ($stats as $record) {
            $date = XenForo_Locale::date($record['dateline'], 'd/m');

            if (empty($groupedArray[$date])) {
                $groupedArray[$date] = array(
                    'dateline' => $date,
                    'click' => 0
                );
            }
            $groupedArray[$date]['click']++;
        }


        return $groupedArray;
    }

    public function getZoneStatisticsForExport($conditions)
    {
        // make sure impressions are up to date
        $this->buildImpressionCounts();

        $clickZoneArray = $this->getZoneClickStatisticsForExport($conditions);
        $impressionsZoneArray = $this->getZoneImpressionsStatisticsForExport($conditions);

        foreach ($clickZoneArray as $zoneId => $clickPerZone) {
            foreach ($clickPerZone AS $clickDate => $info) {
                if (empty($impressionsZoneArray[$zoneId][$clickDate])) {
                    // shouldn't happen actually
                    continue;
                }
                $impressionsZoneArray[$zoneId][$clickDate]['click'] = $info['click'];
            }
        }

        $zoneInfoAll = $this->getZoneModel()->getAllZones();
        foreach ($impressionsZoneArray as $zoneId => $value) {
            $impressionsZoneArray[$zoneId] = array(
                'zoneInfo' => $zoneInfoAll[$zoneId],
                'zoneStatistics' => $value
            );
        }
        return $impressionsZoneArray;
    }


    public function getZoneClickStatisticsForExport($conditions)
    {
        $where = array('1=1');
        if ($conditions['zoneId'] > 0) {
            $where[] = 'stat.zoneId=' . $conditions['zoneId'];
        }
        $stats = $this->_getDb()->fetchAll("
            SELECT stat.dateline, stat.zoneId  FROM xcas_clicks_zone AS stat
            INNER JOIN xcas_zone AS zone ON zone.zoneId=stat.zoneId
            WHERE stat.dateline > " . $conditions['start_date'] . "
            AND stat.dateline < " . $conditions['end_date'] . "
            AND " . implode(" AND ", $where) . "
            ORDER BY dateline ASC
        ");

        $groupedStats = array();
        foreach ($stats as $record) {
            $date = XenForo_Locale::date($record['dateline'], 'd/m');
            $zoneId = $record['zoneId'];
            if (empty($groupedStats[$zoneId][$date])) {
                $groupedStats[$zoneId][$date] = array(
                    'click' => 0
                );
            }
            $groupedStats[$zoneId][$date]['click'] += 1;
        }

        return $groupedStats;
    }

    public function getZoneImpressionsStatisticsForExport($item)
    {
        $where = array('1=1');
        if ($item['zoneId'] > 0) {
            $where[] = 'stat.zoneId=' . $item['zoneId'];
        }

        $stats = $this->_getDb()->fetchAll("
            SELECT stat.*  FROM xcas_impressions_zone AS stat
            INNER JOIN xcas_zone AS zone ON zone.zoneId=stat.zoneId
            WHERE stat.dateline > " . $item['start_date'] . "
            AND stat.dateline < " . $item['end_date'] . "
            AND " . implode(' AND ', $where) . "
        ");

        $impressionsZoneArray = array();
        foreach ($stats as $record) {
            $zoneId = $record['zoneId'];
            $date = XenForo_Locale::date($record['dateline'], 'd/m');
            if (empty($impressionsZoneArray[$zoneId][$date])) {
                $impressionsZoneArray[$zoneId][$date] = array(
                    'dateline' => $date,
                    'count' => 0,
                    'click' => 0
                );
            }
            $impressionsZoneArray[$zoneId][$date]['count'] += $record['count'];
        }

        foreach ($impressionsZoneArray as $zoneId => $impressionsZone) {
            foreach ($impressionsZone as $dateline => $value) {
                unset($impressionsZoneArray[$zoneId][$dateline]['zoneId']);
            }
        }

        return $impressionsZoneArray;
    }

    public function storeImpressionLogs($bannerImpressions, $zoneImpressions)
    {
        $options = XenForo_Application::get('options');
        if (!$options->xcas_disable_statistics) {
            $insert = array();
            foreach ($bannerImpressions AS $bannerId) {
                $insert[] = "($bannerId, 'banner', " . XenForo_Application::$time . ")";
            }
            foreach ($zoneImpressions AS $zoneId) {
                $insert[] = "($zoneId, 'zone', " . XenForo_Application::$time . ")";
            }

            if (!empty($insert)) {
                $this->_getDb()->query("
                    INSERT INTO xcas_impressions_temp (itemId, itemType, dateline)
                    VALUES " . implode(",\n", $insert) . "
                ");
            }
        }
    }

    public function buildImpressionCounts()
    {
        if(XenForo_Application::getOptions()->get('xcas_disable_statistics')) {
            return;
        }

        $allImpressions = $this->_getDb()->fetchAll("
            SELECT * FROM xcas_impressions_temp
            LIMIT 50000
        ");

        $this->_getDb()->query('
            TRUNCATE TABLE xcas_impressions_temp
        ');

        $groupedImpressions=array();

        $zoneImpressions = array();
        $bannerImpressions = array();

        $bannerImpressionLog = array();

        foreach ($allImpressions AS $impression) {
            $impression['dateline'] = $this->_getEffectiveDateline($impression['dateline']);
            if(empty($groupedImpressions[$impression['itemType']])) {
                $groupedImpressions[$impression['itemType']]=array();
            }
            $groupedImpressions[$impression['itemType']][]= "({$impression['itemId']}, {$impression['dateline']}, 1)";
            if ($impression['itemType'] == 'banner') {
                if (isset($bannerImpressionLog[$impression['itemId']])) {
                    $bannerImpressionLog[$impression['itemId']]++;
                } else {
                    $bannerImpressionLog[$impression['itemId']] = 1;
                }
            }
        }

        foreach($groupedImpressions AS $type=>$impressions) {
            $this->_saveUpdatedImpressions($type, $impressions);
        }

        if ($bannerImpressions) {
            $this->_getDb()->query("
                INSERT INTO xcas_impressions_banner (bannerId, dateline, count)
                VALUES " . implode(",\n", $bannerImpressions) . "
                ON DUPLICATE KEY UPDATE count=count+VALUES(count)
            ");
        }


        if ($zoneImpressions) {
            $this->_getDb()->query("
                INSERT INTO xcas_impressions_zone (zoneId, dateline, count)
                VALUES " . implode(",\n", $zoneImpressions) . "
                ON DUPLICATE KEY UPDATE count=count+VALUES(count)
            ");
        }

        foreach ($bannerImpressionLog AS $bannerId => $count) {
            $banner = $this->_getDb()->fetchRow("
                SELECT * FROM xcas_banner
                WHERE bannerId=$bannerId
            ");
            if (
                $banner['active_from'] < time() AND $banner['active_to'] > time()
            ) {
                // the banner is still in time activity period, do not change impressions left
                continue;
            }

            if (!$banner['active_from'] AND !$banner['active_to'] AND $banner['impressions_left'] == -1) {
                // unrestricted banner at all
                continue;
            }

            $impressions_left_updated = max($banner['impressions_left'] - $count, 0);
            if ($impressions_left_updated != $banner['impressions_left']) {
                $this->_getDb()->query("
                    UPDATE xcas_banner
                    SET impressions_left=$impressions_left_updated
                    WHERE bannerId=$bannerId
                ");
            }
        }
    }

    protected function _saveUpdatedImpressions($type, $impressions)
    {
        $this->_getDb()->query("
            INSERT INTO xcas_impressions_{$type} ({$type}Id, dateline, count)
            VALUES " . implode(",\n", $impressions) . "
            ON DUPLICATE KEY UPDATE count=count+VALUES(count)
        ");

        return true;
    }

    public function logBannerClick($bannerId)
    {
        $additionalStats= $this->_getClickAdditionalStats();

        // skip this check on dev boards to have some more stats
        if(
            XenForo_Application::getOptions()->get('xcas_debug_mode')==false
        ) {
            // TODO implement banner click flooding check
            $lastClick = $this->_getDb()->fetchRow('
                SELECT * FROM xcas_clicks_banner
                WHERE bannerId=?
                AND ip_address=?
            ', array($bannerId, $additionalStats['ip_address']));

            if ($lastClick AND $lastClick['dateline'] > time() - 3600) {
                // only one click per IP is allowed in one hour
                return false;
            }
        }

        $log = array(
                'bannerId' => $bannerId
            ) + $additionalStats;

        $this->_getDb()->insert('xcas_clicks_banner', $log);

        return $this->_last_banner_click_log_id=$this->_getDb()->lastInsertId();
    }

    public function logZoneClick($zoneId)
    {
        $log = array(
                'zoneId' => $zoneId
            ) + $this->_getClickAdditionalStats();

        $this->_getDb()->insert('xcas_clicks_zone', $log);

        return $this->_getDb()->lastInsertId();

    }

    public function logCampaignClick(
        $bannerId,
        $ownerId,
        $bannerClickId,
        $zoneClickId,
        $price
    ) {
        $this->_getDb()->insert('xcas_click_credit_log', array(
            'bannerId'=>$bannerId,
            'ownerId'=> $ownerId,
            'bannerClickId'=>$bannerClickId,
            'zoneClickId'=>$zoneClickId,
            'price'=>$price
        ));

        $this->_last_campaign_click_log_id=$this->_db->lastInsertId();
    }

    public function getFirstZoneImpressionDate($zones)
    {
        $zoneIds = array_keys($zones);
        if (empty($zoneIds)) {
            $date=0;
        } else {
            $date = $this->_getDb()->fetchOne("
            SELECT MIN(dateline) FROM xcas_impressions_zone
            WHERE zoneId IN(?)
        ", array(implode(',', $zoneIds)));
        }

        if($date==0) {
            return time()-86400;
        }

        return $date;
    }

    public function getFirstBannerImpressionDate($banners)
    {
        $bannerIds = array_keys($banners);
        if (empty($bannerIds)) {
            $date=0;
        } else {
            $date = $this->_getDb()->fetchOne("
                SELECT MIN(dateline) FROM xcas_impressions_banner
                WHERE bannerId IN(?)
            ", array(implode(',', $bannerIds)));
        }

        if($date==0) {
            return time()-86400;
        }

        return $date;
    }

    public function getStatsInTimeData($content_array, $content_type, $stats_type, $from, $to)
    {
        $data = array(
            'columns' => array(
                array('type' => $this->_getDateColumnType($from, $to), 'label' => (new XenForo_Phrase('xcas_date')) . '')
            ),
            'rows' => array()
        );

        $itemIds = array_keys($content_array);

        $statsQueryHelper = new XenCentral_AdServer_Helper_StatsInTime();
        $statsQueryHelper->setTo($to);
        $statsQueryHelper->setFrom($from);
        $statsQueryHelper->setContentIds($itemIds);
        $statsQueryHelper->setContentType($content_type);
        $statsQueryHelper->setStatsType($stats_type);
        if ($statsQueryHelper->getPrimaryKey()) {
            $statsData = $this->fetchAllKeyed($statsQueryHelper->getQuery(), $statsQueryHelper->getPrimaryKey());
        } else {
            $statsData = $this->_getDb()->fetchAll($statsQueryHelper->getQuery());
        }

        $totalCount = count($statsData);

        $indexCounts = array();

        foreach ($statsData AS $dataEntry) {
            $dataId = $dataEntry[$content_type . 'Id'];
            $dataArray = $content_array[$dataId];

            $colIndex = array_search($dataId, array_keys($content_array));

            if ($colIndex === false) {
                continue;
            }

            $colIndex++;

            if (!isset($data['columns'][$colIndex])) {
                $data['columns'][$colIndex] = array(
                    'type' => 'number',
                    'label' => $dataArray['title']
                );

                $indexCounts[$colIndex] = 0;
            }

            $dateline = $this->_getOptimizedDate($dataEntry['dateline'], $from, $to);

            if (!isset($data['rows'][$dateline][$colIndex])) {
                $data['rows'][$dateline][$colIndex] = $statsQueryHelper->getCountFromDataRow($dataEntry);
                $indexCounts[$colIndex]++;
            } else {
                $data['rows'][$dateline][$colIndex] += $statsQueryHelper->getCountFromDataRow($dataEntry);
            }
        }

        if (empty($data['rows'])) {
            // no data found for this
            return false;
        }

        foreach ($data['rows'] AS &$row) {
            foreach ($data['columns'] AS $index => $column) {
                if ($index == 0) {
                    continue;
                }
                if (!isset($row[$index])) {
                    $row[$index] = 0;
                }
            }
        }

        $data['dataCount'] = count($data['rows']);

        return $data;
    }

    public function getAttributePercentageStats($content_array, $content_type, $attribute, $from, $to)
    {
        $data = array(
            'columns' => array(
                array('type' => 'string', 'label' => new XenForo_Phrase('xcas_attribute_' . $attribute)),
                array('type' => 'number', 'label' => new XenForo_Phrase('xcas_count'))
            ),
            'rows' => array()
        );

        $itemIds = array_keys($content_array);
        $class = "XenCentral_AdServer_Helper_Attributes_" . ucfirst($attribute);
        /** @var XenCentral_AdServer_Helper_Attributes_Abstract $statsQueryHelper */
        $statsQueryHelper = new $class();
        $statsQueryHelper->setTo($to);
        $statsQueryHelper->setFrom($from);
        $statsQueryHelper->setContentIds($itemIds);
        $statsQueryHelper->setContentType($content_type);

        $statsData = $this->_getDb()->fetchAll($statsQueryHelper->getQuery());
        $statsData = $statsQueryHelper->parseData($statsData);

        foreach ($statsData AS $dataRow) {
            $data['rows'][] = array(
                $statsQueryHelper->getLabelFromData($dataRow),
                $statsQueryHelper->getValueFromData($dataRow)
            );
        }

        return $data;
    }

    public function getPercentageStatsData($content_array, $content_type, $stats_type, $from, $to)
    {
        $data = array(
            'columns' => array(
                array('type' => 'string', 'label' => new XenForo_Phrase('xcas_' . $content_type)),
                array('type' => 'number', 'label' => new XenForo_Phrase('xcas_' . $stats_type))
            ),
            'rows' => array()
        );

        $itemIds = array_keys($content_array);

        $statsQueryHelper = new XenCentral_AdServer_Helper_StatsInPercentage();
        $statsQueryHelper->setTo($to);
        $statsQueryHelper->setFrom($from);
        $statsQueryHelper->setContentIds($itemIds);
        $statsQueryHelper->setContentType($content_type);
        $statsQueryHelper->setStatsType($stats_type);

        if ($statsQueryHelper->getPrimaryKey()) {
            $statsData = $this->fetchAllKeyed($statsQueryHelper->getQuery(), $statsQueryHelper->getPrimaryKey());
        } else {
            $statsData = $this->_getDb()->fetchAll($statsQueryHelper->getQuery());
        }

        $total = 0;

        foreach ($statsData AS $itemImpressionData) {
            $total += $itemImpressionData['count'];
            $item = $content_array[$itemImpressionData[$content_type . 'Id']];
            $data['rows'][] = array(
                $item['title'],
                intval($itemImpressionData['count'])
            );
        }

        $data['total'] = $total;

        return $data;
    }

    public function getZoneAverageStats($zoneId)
    {
        return $this->_getAverageStats('zone', $zoneId);
    }

    public function getBannerAverageStats($bannerId)
    {
        return $this->_getAverageStats('banner', $bannerId);
    }

    public function getItemAverageStats($content_type, $content_id)
    {
        return $this->_getAverageStats($content_type, $content_id);
    }

    public function resetStats()
    {
        $this->_getDb()->query("
            TRUNCATE TABLE xcas_clicks_banner
        ");
        $this->_getDb()->query("
            TRUNCATE TABLE xcas_clicks_zone
        ");
        $this->_getDb()->query("
            TRUNCATE TABLE xcas_impressions_banner
        ");
        $this->_getDb()->query("
            TRUNCATE TABLE xcas_impressions_temp
        ");
        $this->_getDb()->query("
            TRUNCATE TABLE xcas_impressions_zone
        ");
        $this->_getDb()->query("
            TRUNCATE TABLE xcas_stats_summary
        ");

    }

    protected function _getDateColumnType($from, $to)
    {
        if ($to - $from > 5 * 86400) {
            return 'date';
        }

        return 'datetime';
    }

    protected function _getOptimizedDate($date, $min, $max)
    {
        $dateDelta = $max - $min;

        if ($dateDelta < 1000) {
            return $date;
        }

        if ($date < $min || $date > $max) {
            return $date;
        }

        $dateStep = floor($dateDelta / 1000);

        $currentDate = $min;

        while (true) {
            if ($currentDate > $date) {
                return $currentDate;
            }

            $currentDate += $dateStep;

            if ($currentDate > $max) {
                break;
            }
        }

        return $max;
    }

    protected function _getAverageStats($content_type, $content_id)
    {
        $impressionsInfo = $this->_getDb()->fetchRow("
            SELECT SUM(count) AS impressions, GROUP_CONCAT(CAST(dateline AS CHAR)) AS dates
            FROM xcas_impressions_{$content_type}
            WHERE {$content_type}Id=?
        ", array($content_id));

        $running = 0;
        $impressions = 0;
        if ($impressionsInfo && $impressionsInfo['impressions'] !== null) {
            $lastDate = 0;
            $gaps = 0;
            foreach (explode(',', $impressionsInfo['dates']) AS $date) {
                if ($date - $lastDate > 3600) {
                    $gaps++;
                    $lastDate = $date;
                    continue;
                }
                $running += $date - $lastDate;
                $lastDate = $date;
            }
            $running += $gaps * 3600;

            $impressions = $impressionsInfo['impressions'];
        }

        // clicks
        $clicks = $this->_getDb()->fetchOne("
            SELECT COUNT(*) FROM xcas_clicks_{$content_type}
            WHERE {$content_type}Id=?
        ", array($content_id));

        return array(
            'impressions' => $impressions,
            'clicks' => intval($clicks),
            'running_time' => $running
        );
    }

    protected function _getEffectiveDateline($time = false)
    {
        if (!$time) {
            $time = XenForo_Application::$time;
        }
        return $time - $time % 3600;
    }

    protected function _getClickAdditionalStats()
    {
        $time = XenForo_Application::$time - XenForo_Application::$time % 60; // to decrease Cardinality a bit
        $ip = XenForo_Helper_Ip::getBinaryIp();
        $referrer = '';
        if (!empty($_SERVER['HTTP_REFERER'])) {
            $referrer = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST);
        }
        return array(
            'dateline' => $time,
            'ip_address' => $ip,
            'referrer' => $referrer
        );
    }

    /**
     * @return mixed
     */
    public function getLastCampaignClickLogId()
    {
        return $this->_last_campaign_click_log_id;
    }

    /**
     * @param mixed $last_banner_click_log_id
     */
    public function setLastBannerClickLogId($last_banner_click_log_id)
    {
        $this->_last_banner_click_log_id = $last_banner_click_log_id;
    }

    /**
     * @return mixed
     */
    public function getLastBannerClickLogId()
    {
        return $this->_last_banner_click_log_id;
    }

    /**
     * @param mixed $last_campaign_click_log_id
     */
    public function setLastCampaignClickLogId($last_campaign_click_log_id)
    {
        $this->_last_campaign_click_log_id = $last_campaign_click_log_id;
    }
}