<?php
/**
 * Created by Roman Liashko.
 * Date: 11/14/2017
 * Time: 2:21 PM
 *
 * This class gets, filters, and returns a display banner with highest bid from Redis
 */

include 'lib/php/include/device-detector/vendor/autoload.php';

use DeviceDetector\DeviceDetector;
use DeviceDetector\Parser\Device\DeviceParserAbstract;
use DeviceDetector\Parser\OperatingSystem;
use DeviceDetector\Parser\Client\Browser;

class Banners
{
    private static $width;
    private static $height;
    private static $country;
    private static $region;
    private static $city;
    private static $ua;
    private static $page_url;
    private static $aff_id;
    private static $categories;  // Array
    private static $banner_floor_bid = 0;

    private static $device_data;
    private static $redis_read;
    private static $redis_write;
    private static $redis_read_host = '132.145.145.237';
    private static $redis_read_port = '6389';
    private static $redis_write_host = array("127.0.0.1");
    private static $redis_write_port = '22130';

    public static function getBanner($config)
    {
		if(!empty( $_GET['debug'] ) ) {
			 echo '--Banner Config--';
			var_dump( $banners_config ) ;
		}
        if (!is_array($config) || !isset($config['width']) || !isset($config['height']) || !isset($config['aff_id']))
            return false;

        foreach ($config as $key => $val) {
            self::$$key = $val;
        }

        //return demo tag if DEMO constant is defined and true
        /*if (defined('DEMO') && DEMO) {
            $tag->bid = 0.1;
            $tag->campaign_id = 123;
            $tag->creative_id = 456;
            $tag->tag = '<img src="https://dummyimage.com/'.self::$width.'x'.self::$height.'/'.dechex(rand(0x333333, 0xCCCCCC)).'/ffffff&text=Banner+'.self::$width.'x'.self::$height.'">"';

            return $tag;
        }*/

        // Connect to Redis read server
        self::$redis_read = new Redis();
        self::$redis_read->pconnect(self::$redis_read_host, self::$redis_read_port);


        // Get campaigns by size
        $key = sprintf("display_banners_%sx%s", self::$width, self::$height);
        $campaigns = json_decode(self::$redis_read->get($key), true);
		if(!empty( $_GET['debug'] ) ) {
			 echo '--Banner Values from Redis--';
			var_dump( $campaigns ) ;
		}
        if (!$campaigns)
            return false;

        if(!empty( $_GET['debug'] ) ) {
            var_dump("Before shuffle array is ".json_encode(array_keys($campaigns)));
        }
        // Shuffle the array to randomize the order
        shuffle($campaigns); // without shuffle, we always see first ad, shuffle will randomize objects.
        if(!empty( $_GET['debug'] ) ) {
            var_dump("After shuffle array is ".json_encode(array_column($campaigns,"id")));
        }
        foreach ($campaigns as $campaign) {
            if(!empty( $_GET['debug'] ) ) {
                var_dump($campaign['id'],self::filter($campaign));
            }
            if (self::filter($campaign))
                break;

            $campaign = null;
        }

        if (!$campaign)
            return false;
		
		if(!empty( $_GET['debug'] ) ) {
			 echo '--All filter Passed--'.PHP_EOL;
		}
        // Save impression to Redis
        $field =
            self::$aff_id . '|' .
            $campaign['id'] . '|' .
            $campaign['creative_id'] . '|' .
            self::getOs() . '|' .
            self::getBrowser() . '|' .
            self::$page_url . '|' .
            self::$country;

        self::$redis_write = new Redis();
        shuffle(self::$redis_write_host);
        self::$redis_write->connect(self::$redis_write_host[0], self::$redis_write_port);
        self::$redis_write->hIncrBy('adc_banners_' . date('Ymd'), $field, 1);
        ////////////////
		
        // Reduce allowable impressions
        //self::$redis_read->hIncrBy('campaign_CPM_allowable_impr', $campaign['id'], -1); c.headbid.com running on php7 throws 500 error here sayong this redis is not meant for writing.
		if(!empty( $_GET['debug'] ) ) {
			 echo '--Save impression values --'.PHP_EOL;
		}
        // Log for RTB
        // $logRTB = new LogForRTB();
        // $logRTB->save($campaign);
		
		if(!empty( $_GET['debug'] ) ) {
			 echo '--Save RTB Logs --'.PHP_EOL;
		}
        return $campaign;
    }

    private static function filter($campaign)
    {
        $targeting = $campaign['targeting'];
		if(!empty( $_GET['debug'] ) ) {
			 echo '--Campaign Targeting --';
			var_dump( $targeting ) ;
		}
        //Ziprecruiter campaign-publisher filter
        if ($campaign['id'] == 43169 && self::$aff_id != 115762)
            return false;

        // Campaign budget
        if ($campaign['budget_spent'] >= $campaign['budget']){
            if(!empty( $_GET['debug'] ) ) {
				 echo '--Budget Exhausted--'.PHP_EOL;
			}
			return false;
		}

        // Check floor bid
        if ($campaign['bid'] < self::$banner_floor_bid){
			if(!empty( $_GET['debug'] ) ) {
				 echo '--Campaign bid.'.$campaign['bid'].' is less then affiliate banner bid floor value :: '.$banner_floor_bid.' --'.PHP_EOL;
			}
            return false;
		}
        // Pacing by impressions per minute
        if (!empty($targeting['pacing'])) {
			
            $allowable_impr = self::$redis_read->hGet('campaign_CPM_allowable_impr', $campaign['id']);

            if ($allowable_impr <= 0){
				if(!empty( $_GET['debug'] ) ) {
					 echo '--Failed due to Allowed impression --'.$allowable_impr.PHP_EOL;
				}
                return false;
			}
        }

        // Check frequency
        if (!empty($targeting['frequency'])) {
            if ($targeting['frequency'] < self::getImpressions($campaign['id'])){
				if(!empty( $_GET['debug'] ) ) {
					 echo '--impresion exceed - from---'.$targeting['frequency'].PHP_EOL;
				}
                return false;
			}
        }

        // Categories (include only)
        if (!empty($targeting['categories'])) {

            if (!self::$categories) {
                if(!empty( $_GET['debug'] ) ) {
                    echo '--categories 1 ---'.PHP_EOL;
                }

                //return false;
            }

            self::$categories = array_map('strtoupper', self::$categories);

            if (!empty($targeting['categories']['include'])) {
                $intersect = array_intersect(self::$categories, $targeting['categories']['include']);
                if (!$intersect) {
                    if(!empty( $_GET['debug'] ) ) {
                        echo '--categories 2 ---'.PHP_EOL;
                    }
                    //return false;
                }
            }

            if (!empty($targeting['categories']['exclude'])) {
                $intersect = array_intersect(self::$categories, $targeting['categories']['exclude']);
                if ($intersect) {
                    if(!empty( $_GET['debug'] ) ) {
                        echo '--categories 3 ---'.PHP_EOL;
                    }
                    //return false;
                }
            }
        }


        // Country
        if (!empty($targeting['countries'])) {
            if (!in_array(strtoupper(self::$country), $targeting['countries'])){
				if(!empty( $_GET['debug'] ) ) {
					 echo '--Country Mismatch tarrget Country'.$targeting['countries'] .' -- Passed Country'.self::$country.PHP_EOL;
				}
				//return false;
                return true;
			}
        }

        // Region (workin only for USA)
        if (!empty($targeting['states'])) {
            if (!in_array(strtoupper(self::$region), $targeting['states'])) {
                if(!empty( $_GET['debug'] ) ) {
                    echo '--states ---'.PHP_EOL;
                }
                //return false;
                return true;
            }
        }

        // City
        if (!empty($targeting['city_include'])) {
            if (!in_array(strtolower(self::$city), $targeting['city_include'])) {
                if(!empty( $_GET['debug'] ) ) {
                    echo '--city include ---'.PHP_EOL;
                }
                //return false;
                return true;
            }
        }

        // Supply Type
        if (!empty($targeting['supplies'])) {
            $device_data = self::getDeviceData();
            if ($device_data->isMobile())
                $suppl = array('mobile', 'app');
            else
                $suppl = array('web');

            $intersect = array_intersect($suppl, $targeting['supplies']);

            if (!$intersect) {
                if(!empty( $_GET['debug'] ) ) {
                    echo '--supplies ---'.PHP_EOL;
                }
                return false;
            }
        }

        // Device
        if (!empty($targeting['devices'])) {
            $device_data = self::getDeviceData();

            if ($device_data->isTablet())
                $device = 'tablet';
            elseif ($device_data->isMobile())
                $device = 'phone';
            elseif ($device_data->isDesktop())
                $device = 'desktop';
            else
                $device = 'others';

            if (!in_array($device, $targeting['devices'])) {
                if(!empty( $_GET['debug'] ) ) {
                    echo '--devices ---'.PHP_EOL;
                }
                return false;
            }
        }

        // OS
        if (!empty($targeting['os'])) {
            $os = self::getOs();

            if (!in_array($os, $targeting['os'])) {
                if(!empty( $_GET['debug'] ) ) {
                    echo '--OS ---'.PHP_EOL;
                }
                return false;
            }
        }

        // Retargeting
        if (!empty($targeting['retargeting']) && is_array($targeting['retargeting']['ids'])) {
            if (empty($_COOKIE['admRtbUidCkey34334Ssp245'])) {
                if(!empty( $_GET['debug'] ) ) {
                    echo '--retargeting cookies is not admRtbUidCkey34334Ssp245 ---'.PHP_EOL;
                }
                return false;
            }

            $redis_pipe = self::$redis_read->multi(Redis::PIPELINE);

            foreach ($targeting['retargeting']['ids'] as $id) {
                $key = 'retargeting_pixel_' . $id;
                $redis_pipe->sIsMember($key, $_COOKIE['admRtbUidCkey34334Ssp245']);
            }

            $result = $redis_pipe->exec();

            if (!$result = array_filter($result)) {
                if(!empty( $_GET['debug'] ) ) {
                    echo '--retargeting 1 ---'.PHP_EOL;
                }
                return false;
            }

            if ($targeting['retargeting']['logic'] == 'and' && count($result) != count($targeting['retargeting']['ids'])) {
                if(!empty( $_GET['debug'] ) ) {
                    echo '--retargeting 2 ---'.PHP_EOL;
                }
                return false;
            }

        }

        // Browsers
        if (!empty($targeting['browsers'])) {
            $browser = self::getBrowser();
            if (!in_array($browser, $targeting['browsers'])) {
                if(!empty( $_GET['debug'] ) ) {
                    echo '--browsers ---'.PHP_EOL;
                }
                return false;
            }
        }

        // Domain list
        /*if (!empty($targeting['domain_lists'])) {

            foreach ($targeting['domain_lists'] as $list_type => $list_ids) {
                foreach ($list_ids as $list_id) {
                    $sismember = self::$redis_read->sismember("adnet_domain_list_$list_id", self::$page_url);

                    if ($sismember && $list_type == 'exclude' || !$sismember && $list_type == 'include')
                    {
                        if(!empty( $_GET['debug'] ) ) {
                            echo '-- domain lists failed because '.$sismember.' - '.$list_type.PHP_EOL;
                        }

                        return false;
                    }
                }
            }
        }*/

        return true;
    }

    private static function getDeviceData()
    {
        if(!self::$device_data){

            // OPTIONAL: Set version truncation to none, so full versions will be returned
            // By default only minor versions will be returned (e.g. X.Y)
            // for other options see VERSION_TRUNCATION_* constants in DeviceParserAbstract class
            DeviceParserAbstract::setVersionTruncation(DeviceParserAbstract::VERSION_TRUNCATION_NONE);

            self::$device_data = new DeviceDetector(self::$ua);

            // OPTIONAL: Set caching method
            // By default static cache is used, which works best within one php process (memory array caching)
            // To cache across requests use caching in files or memcache
            $redis = new Redis();
            $redis->pconnect("127.0.0.1",22130);

            self::$device_data->setCache(new \DeviceDetector\Cache\RedisCache($redis));

            self::$device_data->discardBotInformation();

            self::$device_data->parse();
        }

        return self::$device_data;
    }

    private static function getOs()
    {
        static $os;

        if(!$os){
            $device_data = self::getDeviceData();

            $os_data = $device_data->getOs();
            $os_family = OperatingSystem::getOsFamily($os_data['short_name']);
            $os_family = strtolower($os_family);

            if (strpos($os_family, 'windows') === 0)
                $os_family = 'windows';
            elseif ($os_family == 'mac')
                $os_family = 'macos';
            elseif ($os_family == 'chrome os')
                $os_family = 'chromeos';
            elseif ($os_family == 'unix')
                $os_family = 'linux';
            elseif ($os_family != 'android' && $os_family != 'ios' && $os_family != 'blackberry')
                $os_family = 'others';

            $os = $os_family;
        }

        return $os;
    }

    private static function getBrowser()
    {
        static $browser;

        if(!$browser){
            $device_data = self::getDeviceData();

            $browserFamily = Browser::getBrowserFamily($device_data->getClient('short_name'));
            $browserFamily = strtolower($browserFamily);

            if ($browserFamily == 'internet explorer')
                $browserFamily = 'msie';
            elseif (!in_array($browserFamily, array('firefox', 'chrome', 'safari', 'opera')))
                $browserFamily = 'unknown';

            $browser = $browserFamily;
        }

        return $browser;
    }

    private static function getImpressions($campaign_id)
    {
        static $redis;

        if(!$redis){
            $redis = new Redis();
            $redis->pconnect("127.0.0.1",22130);
        }
        $fqc_key = "fqcap_".date("Ymd");
        $fqc_hash = $campaign_id.'_'.$_SERVER['HTTP_X_FORWARDED_FOR'];

        return $redis->hGet($fqc_key, $fqc_hash);
    }
}

class LogForRTB
{
    private $redis_ip = array('127.0.0.1' => 22130);
    private $redis;
    public function __construct()
    {
    }

    public function save($campaign)
    {
        foreach ($this->redis_ip as $ip => $port) {
            $this->connect($ip, $port);

            $incr_price = floatval($campaign['bid']/1000);

            $master_multi = $this->redis->multi();

            // Hash for all keys
            $hash = "cid_".$campaign['id'];

            // Spend by min
            $csm_key = "campaignsSpendByMin_".date("Ymd")."_".date("Hi");

            // Spend for the day
            $cs_key = "campaignsSpendByDay_".date("Ymd");

            // First minute of spend
            $cfm_key = "campaignsSpendFirstMin_".date("Ymd");
            $curr_min_in_day = intval(date("G"))*60 + intval(date("i"));

            if (!empty($campaign['targeting']['frequency'])) {
                $fqc_key = "fqcap_".date("Ymd");
                $fqc_hash = $campaign['id'].'_'.$_SERVER['HTTP_X_FORWARDED_FOR'];
                $master_multi->hincrby($fqc_key, $fqc_hash, 1);
                $master_multi->expire($fqc_key, 86400);
            }

            $master_multi->hincrbyfloat($csm_key, $hash, $incr_price);
            $master_multi->hincrbyfloat($cs_key, $hash, $incr_price);
            $master_multi->hsetnx($cfm_key, $hash, $curr_min_in_day);
            $master_multi->expire($csm_key, 86400);
            $master_multi->expire($cs_key, 86400);
            $master_multi->expire($cfm_key, 86400);

            $master_multi->exec();
        }
    }

    private function connect($ip, $port)
    {
        $this->redis = new Redis();
        $this->redis->pconnect($ip, $port);
    }


}