<?php

class In extends Controller {

	/**
	 * @param $f3 \Base
	 * @param $params
	 */
	public function getAd($f3, $params){

		$id = $f3->get('GET.i');
		if (!is_numeric($id)) return;

/*
		 * 1: Standard Html
		 * 2: Rotating
		 * 3: Layer
		 * 4: VAST
		 */
		$code = $f3->get('GET.c');
		if (!is_numeric($code)) return;

		$out = $this->_getAdOutput($f3, $id, $code);
		if (!empty($out)){

			//PHP ad code
			if ($f3->get('GET.at')){
				echo $out;
			} else {
				$jsonOut = array('Code' => $out);
				header('Content-Type: text/javascript; charset=UTF-8');
				echo $f3->get('GET.callback').'('.json_encode($jsonOut).');';
			}

		}

		$baseUrl = $f3->get('SCHEME').'://'.$f3->get('HOST').$f3->get('BASE');
		$this->_callTasks($baseUrl);
	}

	/**
	 * @param $f3 \Base
	 * @param $params
	 */
	public function getGroup($f3, $params){

		$id = $f3->get('GET.i');
		if (!is_numeric($id)) return;

		/*
		 * 1: Standard Html
		 * 2: Rotating
		 * 3: Layer
		 * 4: VAST
		 */
		$code = $f3->get('GET.c');
		if (!is_numeric($code)) return;

		$group = new \Models\Group();
		$group->load(array('_id=?',$id), null, 600);
		if ($group->dry()) return;

		$break = $group->ad_break;

		$ip = $this->_getIp($f3);
		$cacheKey = ip2long($ip).'_group_'.$id.'.code';
		$cache = \Cache::instance();
		$adArray = $cache->get($cacheKey);
		$unique = 0;
		if (empty($adArray)){
			$ads = $group->ads;
			if (empty($ads)) return;
			if (!empty($group->ad_order)){
				$adIdsOrdered = explode(',',$group->ad_order);
				foreach ($adIdsOrdered as $adIdOrdered) {
					$adArray[] = array('id' => $adIdOrdered);
				}
			} else {
				$adArray = $this->_getWeightedRandomOrder($ads);
			}
			$numDisplay = $group->num_display <= count($adArray) ? $group->num_display : count($adArray);
			$unique = 1;
			$cache->set($cacheKey, $adArray, 3600);
		} else {
			$numDisplay = $group->num_display <= count($adArray) ? $group->num_display : count($adArray);
			if ((empty($group->ad_order)) || ($numDisplay <= 1)){
				$out = array_splice($adArray, 0, 1);
				array_splice($adArray, count($adArray), 0, $out);
				$cache->set($cacheKey, $adArray, 3600);
			}
		}



		$out = '';
		$jsonOut = array();
		switch ($code){
			case 1:
			case 3:

				$displayCount = 0;
				foreach ($adArray as $ad) {
					if (empty($ad)) continue;

					$tmpOut = '';
					$tmpOut = $this->_getAdOutput($f3, $ad['id'], $code);
					if (empty($tmpOut)) continue;
					$out .= $tmpOut;
					
					if (($code == 3) || ($numDisplay <= 1)) break;

					$displayCount++;
					if ($numDisplay <= $displayCount) break;
					$out .= $break;
				}
				$jsonOut = array('Code' => $out);

				break;
			case 2:

				$adCount = count($adArray);
				$outArray = new SplFixedArray($adCount);
				$currentHtml = '';
				// Add ad output to array
				for ($i = 0; $i < $adCount; $i++){
					if (empty($adArray[$i])) continue;
					$outArray[$i] = $this->_getAdOutput($f3, $adArray[$i]['id'], $code);
				}
				// Loop through ads building jsonOut which will have
				// as many entries as their are ads
				for ($i = 0; $i < $adCount; $i++){

					if ($numDisplay <= 1) {
						if (empty($outArray[$i])) continue;
						$jsonOut[] = array('Code' => $outArray[$i]);
						continue;
					}

					// If display more than 1 at a time, build string with break
					$currentAdArrayId = $i;
					$numDisplayCount = 0;
					$outArrayCount = count($outArray);
					for ($n = 0; $n < $outArrayCount; $n++){
						if ($numDisplayCount == $numDisplay) break;
						$currentAdArrayId = $currentAdArrayId >= ($adCount - 1)  ? 0 : $currentAdArrayId + 1;
						if (empty($outArray[$currentAdArrayId])) continue;
						$currentHtml .= $outArray[$currentAdArrayId].$break;
						$numDisplayCount++;
					}
					if (empty($currentHtml)) continue;
					$jsonOut[] = array('Code' => $currentHtml);
					$currentHtml = '';
				}
				unset($outArray);

				break;
			case 4:
				break;
		}

		//PHP ad code
		if ($f3->get('GET.at')){
			echo $out;
		} else {
			header('Content-Type: text/javascript; charset=UTF-8');
			echo $f3->get('GET.callback').'('.json_encode($jsonOut).');';
		}

		$this->InsertStatsQueue($f3, $unique, 1, 0, null, $group->_id);
		$baseUrl = $f3->get('SCHEME').'://'.$f3->get('HOST').$f3->get('BASE');
		$this->_callTasks($baseUrl);
	}


	/**
	 * @param $f3 \Base
	 * @param $params
	 */
	public function getSale($f3, $params){

		$id = $f3->get('GET.i');
		if (!is_numeric($id)) return;

		/*
		 * 1: Standard Html
		 * 2: Rotating
		 * 3: Layer
		 * 4: VAST
		 */
		$code = $f3->get('GET.c');
		if (!is_numeric($code)) return;

		$sale = new \Models\Sale();
		$sale->load(array('_id=?',$id), null, 600);
		if ($sale->dry()) return;
		$numDisplay = 1;
		$break = '';
		$maxSpots = $sale->max_spots;
		$ads = $sale->ads;

		$ip = $this->_getIp($f3);
		$cacheKey = ip2long($ip).'_sale_'.$id.'.code';
		$cache = \Cache::instance();
		$adArray = $cache->get($cacheKey);
		if (empty($adArray)){
			// Get array of ads - fill with fill group if necessary and available
			$adArray = array();
			if (!empty($ads)) {

				$sort_with_fill_group = false;
				if (!empty($sale->fill_group)){
					// Check if sort with fill group or separate
					foreach ($ads as $a) {
						if ($a->weight < 10){
							$sort_with_fill_group = true;
							break;
						}
					}

					$adArray = $this->_getWeightedRandomOrder($ads);
					$fillGroupSorted = $this->_getWeightedRandomOrder($sale->fill_group->ads);
					$adArray = array_merge($adArray, $fillGroupSorted);

					if ($sort_with_fill_group) {
						shuffle($adArray);
						usort($adArray, array('In','_weightedSort'));
					}

				} else {
					$adArray = $this->_getWeightedRandomOrder($ads);
				}

			} else {
				if (!empty($sale->fill_group)){
					$adArray = $this->_getWeightedRandomOrder($sale->fill_group->ads);
				}
			}

			$cache->set($cacheKey, $adArray, 3600);
		} else {
			$out = array_splice($adArray, 0, 1);
			array_splice($adArray, count($adArray), 0, $out);
			$cache->set($cacheKey, $adArray, 3600);
		}

		$arrayLength = (count ($adArray) > $maxSpots) ? $maxSpots : count ($adArray);
		$adArray = array_slice($adArray, 0, $arrayLength);

		$out = '';
		$jsonOut = array();

		switch ($code){
			case 1:
			case 3:

				$displayCount = 0;
				foreach ($adArray as $ad) {
					if (empty($ad)) continue;
					$out .= $this->_getAdOutput($f3, $ad['id'], $code);

					if (empty($out)) continue;
					if (($code == 3) || ($numDisplay <= 1)) break;

					$displayCount++;
					if ($numDisplay <= $displayCount) break;
					$out .= $break;
				}
				$jsonOut = array('Code' => $out);

				break;
			case 2:

				$displayCount = 0;
				foreach ($adArray as $ad) {
					if (empty($ad)) continue;

					$out = $this->_getAdOutput($f3, $ad['id'], $code);
					if (empty($out)) continue;
					$jsonOut[] = array('Code' => $out);
					if ($numDisplay <= 1) break;
					$displayCount++;
					if ($numDisplay <= $displayCount) break;
				}

				break;
			case 4:
				break;
		}

		//PHP ad code
		if ($f3->get('GET.at')){
			echo $out;
		} else {
			header('Content-Type: text/javascript; charset=UTF-8');
			echo $f3->get('GET.callback').'('.json_encode($jsonOut).');';
		}

		//$f3, $u=0, $i=1, $c=0, $ad_id=null, $group_id=null, $keyword_id=null, $site_key=null, $sale_id=null, $hostname=null, $referer=null
		//$this->InsertStatsQueue($f3, 0, 1, 0, null, null, null, null, $sale->_id);
		$baseUrl = $f3->get('SCHEME').'://'.$f3->get('HOST').$f3->get('BASE');
		$this->_callTasks($baseUrl);
	}


	/**
	 * @param $f3 \Base
	 * @param $params
	 */
	public function getKeywordsAd($f3, $params){

		$keywordsRaw = $f3->get('GET.k');
		if (empty($keywordsRaw)) return;

		$keywordsUnencoded = strtolower(urldecode($keywordsRaw));
		$keywordsArray = explode(',', $keywordsUnencoded);

		$adsArray = array();
		$adKeywordArray = array();

		foreach ($keywordsArray as $keyword) {

			$keyword_res = new \Models\Keyword();
			$keywordsObj = $keyword_res->find(array('keyword=?', $keyword), null, 3600);

			if (empty($keywordsObj)) continue;

			foreach ($keywordsObj as $keywordObj) {

				$ads = $keywordObj->ads;
				if (empty($ads)) continue;

				foreach ($ads as $ad) {
					if ($ad->status == 0) continue;
					$adsArray[] = $ad;
					$adKeywordArray[$ad->_id][] = $keywordObj->_id;
				}

			}

		}

		if (empty($adsArray)) return;

		$ip = $this->_getIp($f3);
		$cacheKey = ip2long($ip).'_keywordsad_'.md5($keywordsRaw).'.code';
		$cache = \Cache::instance();
		$adArray = $cache->get($cacheKey);
		$unique = 0;
		if (empty($adArray)){
			$adArray = $this->_getWeightedRandomOrder($adsArray);
			$unique = 1;
			$cache->set($cacheKey, $adArray, 3600);
		} else {
			$aout = array_splice($adArray, 0, 1);
			array_splice($adArray, count($adArray), 0, $aout);
			$cache->set($cacheKey, $adArray, 3600);
		}

		/*
		 * 1: Standard Html
		 * 2: Rotating
		 * 3: Layer
		 * 4: VAST
		 */
		$code = $f3->get('GET.c');
		if (!is_numeric($code)) return;
		$out = '';
		$jsonOut = array();

		switch ($code){
			case 1:
			case 3:

				foreach ($adArray as $ad) {
					if (empty($ad)) continue;

					$out = $this->_getAdOutput($f3, $ad['id'], $code);
					if (empty($out)) continue;
					$keywordIds = $adKeywordArray[$ad['id']];
					if (!empty($keywordIds)){
						foreach ($keywordIds as $keywordId) {
							$this->InsertStatsQueue($f3, $unique, 1, 0, null, null, $keywordId);
						}
					}
					break;
				}
				$jsonOut = array('Code' => $out);
				break;
			case 2:

				foreach ($adArray as $ad) {
					if (empty($ad)) continue;
					$out = $this->_getAdOutput($f3, $ad['id'], $code);
					if (empty($out)) continue;
					$jsonOut[] = array('Code' => $out);
					$keywordIds = $adKeywordArray[$ad['id']];
					if (!empty($keywordIds)){
						foreach ($keywordIds as $keywordId) {
							$this->InsertStatsQueue($f3, $unique, 1, 0, null, null, $keywordId);
						}
					}
				}

				break;
			case 4:
				break;
		}

		//PHP ad code
		if ($f3->get('GET.at')){
			echo $out;
		} else {
			header('Content-Type: text/javascript; charset=UTF-8');
			echo $f3->get('GET.callback').'('.json_encode($jsonOut).');';
		}

		$baseUrl = $f3->get('SCHEME').'://'.$f3->get('HOST').$f3->get('BASE');
		$this->_callTasks($baseUrl);
	}


	/**
	 * @param $f3 \Base
	 * @param $params
	 */
	public function getKeywordsGroup($f3, $params){

		$keywordsRaw = $f3->get('GET.k');
		if (empty($keywordsRaw)) return;

		$keywordsUnencoded = strtolower(urldecode($keywordsRaw));
		$keywordsArray = explode(',', $keywordsUnencoded);

		$numDisplay = 1;
		$break = '';
		$groupsArray = array();
		$groupKeywordArray = array();
		foreach ($keywordsArray as $keyword) {

			$keyword_res = new \Models\Keyword();
			$keywordsObj = $keyword_res->find(array('keyword=?', $keyword));

			if (empty($keywordsObj)) continue;
			foreach ($keywordsObj as $keywordObj) {
				$groups = $keywordObj->groups;
				if (empty($groups)) continue;
				foreach ($groups as $group) {
					$groupsArray[] = $group;
					$groupKeywordArray[$group->_id][] = $keywordObj->_id;

					//Will only use settings from last group in loop
					$numDisplay = $group->num_display;
					$break = $group->ad_break;
				}

			}
		}
		if (empty($groupsArray)) return;

		//Get all ads from group
		$adKeywordArray = array();
		$adsArray = array();
		foreach ($groupsArray as $group) {
			$ads = $group->ads;
			if (empty($ads)) continue;
			foreach ($ads as $ad) {
				if ($ad->status == 0) continue;
				if (in_array($ad, $adsArray)) continue;
				$adsArray[] = $ad;
				$adKeywordArray[$ad->_id][] = $groupKeywordArray[$group->_id][0];
			}
		}

		$ip = $this->_getIp($f3);
		$cacheKey = ip2long($ip).'_keywordsgroup_'.md5($keywordsRaw).'.code';
		$cache = \Cache::instance();
		$adArray = $cache->get($cacheKey);
		$unique = 0;
		if (empty($adArray)){
			$adArray = $this->_getWeightedRandomOrder($adsArray);
			$unique = 1;
			$cache->set($cacheKey, $adArray, 3600);
		} else {
			$out = array_splice($adArray, 0, 1);
			array_splice($adArray, count($adArray), 0, $out);
			$cache->set($cacheKey, $adArray, 3600);
		}

		/*
		 * 1: Standard Html
		 * 2: Rotating
		 * 3: Layer
		 * 4: VAST
		 */
		$code = $f3->get('GET.c');
		if (!is_numeric($code)) return;

		$out = '';
		$jsonOut = array();

		switch ($code){
			case 1:
			case 3:

				$displayCount = 0;
				foreach ($adArray as $ad) {
					if (empty($ad)) continue;

					$out .= $this->_getAdOutput($f3, $ad['id'], $code);
					if (empty($out)) continue;
					$keywordIds = $adKeywordArray[$ad['id']];
					if (!empty($keywordIds)){
						foreach ($keywordIds as $keywordId) {
							$this->InsertStatsQueue($f3, $unique, 1, 0, null, null, $keywordId);
						}
					}
					if (($code == 3) || ($numDisplay <= 1)) break;
					$out .= $break;
					$displayCount++;
					if ($numDisplay <= $displayCount) break;
				}
				$jsonOut = array('Code' => $out);
				break;
			case 2:

				$displayCount = 0;
				foreach ($adArray as $ad) {
					if (empty($ad)) continue;
					$out = $this->_getAdOutput($f3, $ad['id'], $code);
					if (empty($out)) continue;
					$jsonOut[] = array('Code' => $out);
					$keywordIds = $adKeywordArray[$ad['id']];
					if (!empty($keywordIds)){
						foreach ($keywordIds as $keywordId) {
							$this->InsertStatsQueue($f3, $unique, 1, 0, null, null, $keywordId);
						}
					}
					if ($numDisplay <= 1) break;
					$displayCount++;
					if ($numDisplay <= $displayCount) break;
				}

				break;
			case 4:
				break;
		}

		//PHP ad code
		if ($f3->get('GET.at')){
			echo $out;
		} else {
			header('Content-Type: text/javascript; charset=UTF-8');
			echo $f3->get('GET.callback').'('.json_encode($jsonOut).');';
		}



		$baseUrl = $f3->get('SCHEME').'://'.$f3->get('HOST').$f3->get('BASE');
		$this->_callTasks($baseUrl);

	}


	/**
	 * @param $f3 \Base
	 * @param $params
	 */
	public function getVASTAd($f3, $params){

		$adId = $f3->get('PARAMS.id');
		$ad = new \Models\Ad();
		$ad->load(array('_id=? AND status=1',$adId), null, 3600);
		if ($ad->dry()) return;

		$out = $this->_getVASTAd($f3, $ad);
		if (empty($out)) return;

		header('Content-Type: application/xml; charset=utf-8');
		echo $out;

		return;
	}


	/**
	 * @param $f3 \Base
	 * @param $params
	 */
	public function getVASTGroup($f3, $params){

		$groupId = $f3->get('PARAMS.id');
		$group = new \Models\Group();
		$group->load(array('_id=?',$groupId), null, 600);
		if ($group->dry()) return;

		$ads = $group->ads;
		if (empty($ads)) return;
		$adArray = $this->_getWeightedRandomOrder($ads);
		if (count($adArray) <= 0) return;

		$ad = new \Models\Ad();
		$ad->load(array('_id=?',$adArray[0]['id']));
		$out = $this->_getVASTAd($f3, $ad);
		if (empty($out)) return;

		header('Content-Type: application/xml; charset=utf-8');
		echo $out;

		return;
	}


	/**
	 * @param $f3 \Base
	 * @param $params
	 */
	public function setVASTAdImpression($f3, $params){
		$ip = $this->_getIp($f3);
		$cacheKey = ip2long($ip).'_vasti_'.$f3->get('PARAMS.id').'.code';
		$cache = \Cache::instance();
		if ($cache->exists($cacheKey)){
			$unique = 0;
		} else {
			$unique = 1;
			$cache->set($cacheKey, 1, 3600);
		}
		$this->InsertStatsQueue($f3, $unique, 1, 0, $f3->get('GET.i'));
	}


	/**
	 * @param $f3 \Base
	 * @param $params
	 */
	public function setVASTAdClick($f3, $params){
		$this->InsertStatsQueue($f3, 0, 0, 1, $f3->get('PARAMS.id'));
	}


	/**
	 * @param $f3 \Base
	 * @param $params
	 */
	public function getTrackingPixel($f3, $params){
		ignore_user_abort(true);

		// turn off gzip compression
		if ( function_exists( 'apache_setenv' ) ) {
			apache_setenv( 'no-gzip', 1 );
		}

		ini_set('zlib.output_compression', 0);

		// turn on output buffering if necessary
		if (ob_get_level() == 0) {
			ob_start();
		}

		// removing any content encoding like gzip etc.
		header('Content-encoding: none', true);

		//check to ses if request is a POST
		if ($_SERVER['REQUEST_METHOD'] === 'POST') {
			// the GIF should not be POSTed to, so do nothing...
			echo ' ';
		} else {
			// return 1x1 pixel transparent gif
			header("Content-type: image/gif");
			// needed to avoid cache time on browser side
			header("Content-Length: 42");
			header("Cache-Control: private, no-cache, no-cache=Set-Cookie, proxy-revalidate");
			header("Expires: Wed, 11 Jan 2000 12:59:00 GMT");
			header("Last-Modified: Wed, 11 Jan 2006 12:59:00 GMT");
			header("Pragma: no-cache");

			echo sprintf('%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%',71,73,70,56,57,97,1,0,1,0,128,255,0,192,192,192,0,0,0,33,249,4,1,0,0,0,0,44,0,0,0,0,1,0,1,0,0,2,2,68,1,0,59);
		}

		// flush all output buffers. No reason to make the user wait for OWA.
		ob_flush();
		flush();
		ob_end_flush();

		$adId = $f3->get('PARAMS.id');
		if (!empty($adId)){

			$ip = $this->_getIp($f3);
			$cacheKey = ip2long($ip).'_tp_'.$adId.'.code';
			$cache = \Cache::instance();
			if ($cache->exists($cacheKey)){
				$unique = 0;
			} else {
				$unique = 1;
				$cache->set($cacheKey, 1, 3600);
			}

			$hostname=empty($_SERVER['HTTP_HOST']) ? '' : $_SERVER['HTTP_HOST'];
			$referer=empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'];
			$this->InsertStatsQueue($f3, $unique, 1, 0, $adId, null, null, null, null, $hostname, $referer);
		}
	}


	/** Random sort ads by weight, returning array of id and weight
	 * @param $ads \Models\Ad objects
	 * @return array
	 */
	private function _getWeightedRandomOrder($ads){

		$weightedValues = array();
		foreach ($ads as $ad) {
			if (empty($ad->status)) continue;
			$weightedValues[] = array('id' => $ad->_id, 'weight' => $ad->weight);
		}
		shuffle($weightedValues);
		usort($weightedValues, array('In','_weightedSort'));
		return ($weightedValues);
	}


	/** Sorting method called by usort for weighted random sort
	 * @param $a
	 * @param $b
	 * @return int
	 */
	private static function _weightedSort($a, $b){
		$wa = $a['weight'];
		$wb = $b['weight'];
		$r = mt_rand(mt_rand(1, $wa), $wa);
		return $wb >= $r ? 1 : 0;
	}

	/**
	 * @param $f3 \Base
	 * @param $id int Ad Id
	 * @param $code string Code type
	 */
	private function _getAdOutput($f3, $id, $code){

		$ip = $this->_getIp($f3);
		$cacheKey = ip2long($ip).'_ad_'.$id.'.code';
		$cache = \Cache::instance();

		$out = $cache->get($cacheKey);
		if (!empty($out)){
			$this->InsertStatsQueue($f3, 0, 1, 0, $id);
		} else {
			$out = '';
		}

		$ad = new \Models\Ad();
		$ad->load(array('_id=? AND status=1',$id), null, 3600);
		if ($ad->dry()) return ('');

		if (empty($out)){

			if ($ad->geo_onoff > 0) {
				if (!$this->_checkGeo($ip, $ad->geo_onoff, $ad->geo_countries, $ad->geo_regions, $ad->geo_cities)) return ('');
			}

			$baseUrl = $f3->get('SCHEME').'://'.$f3->get('HOST').$f3->get('BASE');
			$outUrl = $baseUrl.'/index.php?out&a&'.$ad->_id;

			switch ($ad->type){
				case 'banner':
					$out = $this->_showBanner($f3, $outUrl, $ad);
					break;
				case 'textlink':
					$out = $this->_showTextLink($outUrl, $ad);
					break;
				case 'video':
					$out = $this->_showVideo($outUrl, $ad);
					break;
				case 'freeform':
					$out = $this->_showFreeform($ad);
					break;
			}

			$this->InsertStatsQueue($f3, 1, 1, 0, $id);
			$cache->set($cacheKey, $out, 3600);
		}
		if ($code == 3){
			$out = $this->_showLayer($f3, $out);
		}

		if ($ad->type == 'freeform'){
			$adcodeType = $f3->get('GET.at') ? $f3->get('GET.at') : '';
			$text = $ad->type_freeform->text;
			if (stristr($text, '<?php') !== false) {
				if ($adcodeType == 'p'){
					$out = str_replace('<?php', '', $out);
					$out = str_replace('?>', '', $out);
					ob_start();
					eval($out);
					$out = ob_get_contents();
					ob_end_clean();
				}
			}
		}

		return ($out);
	}


	/**
	 * @param $f3 \Base
	 * @param $outUrl string Link URL
	 * @param $ad \Models\Ad
	 */
	private function _showBanner($f3, $outUrl, $ad){
		$outLink = '';
		if (!empty($outUrl)) $outLink = '<a href="'.$outUrl.'" rel="nofollow" target="_blank">';

		$bannerSrc = $ad->type_banner->src_lg;
		if ($f3->get('GET.w')){
			if ((!empty($ad->type_banner->src_sm)) && ($f3->get('GET.w') <= 767)){
				$bannerSrc = $ad->type_banner->src_sm;
			}
		}

		$width = empty($ad->type_banner->width) ? '' : ' width = "'.$ad->type_banner->width.'"';
		$height = empty($ad->type_banner->height) ? '' : ' height = "'.$ad->type_banner->height.'"';
		$caption_top = empty($ad->type_banner->caption_top) ? '' : $outLink.$ad->type_banner->caption_top.'</a><br />';
		$caption_bottom = empty($ad->type_banner->caption_bottom) ? '' : '<br />'.$outLink.$ad->type_banner->caption_bottom.'</a>';
		$caption_left = empty($ad->type_banner->caption_left) ? '' : $outLink.$ad->type_banner->caption_left.'</a>';
		$caption_right = empty($ad->type_banner->caption_right) ? '' : $outLink.$ad->type_banner->caption_right.'</a>';
		$css_class = empty($ad->type_banner->css_class) ? '' : ' class="'.$ad->type_banner->css_class.'"';

		$imgCode = $caption_top.$caption_left;

		$imgCode .= $outLink.'<img src="'.$bannerSrc.'"'.$width.$height.$css_class.' alt="'.$ad->type_banner->alt.'"/>';
		if (!empty($outUrl)) $imgCode .= '</a>';
		
		$imgCode .= $caption_right.$caption_bottom;

		return ($imgCode);
	}


	/**
	 * @param $outUrl string Link URL
	 * @param $ad \Models\Ad
	 */
	private function _showTextLink($outUrl, $ad){
		$outLink = '<a href="'.$outUrl.'" rel="nofollow" target="_blank">';

		$caption_top = empty($ad->type_textlink->caption_top) ? '' : $outLink.$ad->type_textlink->caption_top.'</a><br />';
		$caption_bottom = empty($ad->type_textlink->caption_bottom) ? '' : '<br />'.$outLink.$ad->type_textlink->caption_bottom.'</a>';
		$caption_left = empty($ad->type_textlink->caption_left) ? '' : $outLink.$ad->type_textlink->caption_left.'</a>';
		$caption_right = empty($ad->type_textlink->caption_right) ? '' : $outLink.$ad->type_textlink->caption_right.'</a>';
		$css_class_start = empty($ad->type_textlink->css_class) ? '' : '<span class="'.$ad->type_textlink->css_class.'">';
		$css_class_end = empty($ad->type_textlink->css_class) ? '' : '</span>';

		$linkCode = $caption_top.$caption_left;

		$linkCode .= $outLink.$css_class_start.$ad->type_textlink->anchor.$css_class_end.'</a>';

		$linkCode .= $caption_right.$caption_bottom;

		return ($linkCode);
	}


	/**
	 * @param $outUrl string Link URL
	 * @param $ad \Models\Ad
	 */
	private function _showVideo($outUrl, $ad){
		$outLink = '<a href="'.$outUrl.'" rel="nofollow" target="_blank">';

		$videoSrc = $ad->type_video->src;
		$fallbackImage = $ad->type_video->fallback_image_url;

		$extension = strtolower(pathinfo($videoSrc, PATHINFO_EXTENSION));

		$width = empty($ad->type_video->width) ? '' : $ad->type_video->width;
		$height = empty($ad->type_video->height) ? '' : $ad->type_video->height;

		$code = '';
		if ($extension == 'swf') {
			$code .= "<object classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" height=\"" .$height. "\" width=\"" .$width. "\">";
			$code .= "<param name=\"movie\" value=\"" .$videoSrc. "\"> ";
			$code .= "<param name=\"quality\" value=\"high\">";
			$code .= "<param name=\"menu\" value=\"false\">";
			$code .= "<param name=\"flashvars\" value=\"clickTAG=" .urlencode($outUrl). "\">";
			$code .= "<param name=\"allowScriptAccess\" value=\"always\" />";
			$code .= "<param name=\"wmode\" value=\"transparent\">";
			$code .= " <!--[if !IE]>-->";
			$code .= "  <object data=\"" .$videoSrc. "\" height=\"" .$height. "\" width=\"" .$width. "\" type=\"application/x-shockwave-flash\">";
			$code .= "   <param name=\"quality\" value=\"high\">";
			$code .= "   <param name=\"menu\" value=\"false\">";
			$code .= "   <param name=\"pluginurl\" value=\"http://www.macromedia.com/go/getflashplayer\">";
			$code .= "   <param name=\"flashvars\" value=\"clickTAG=" .urlencode($outUrl). "\">";
			$code .= "   <param name=\"allowScriptAccess\" value=\"always\" />";
			$code .= "<div style=\"width:".$width."px; height:".$height."px; overflow:auto\">";
			$code .= "<a href=\"" . $outUrl . "\"><img src=\"".$fallbackImage."\" alt=\"\"></a>";
			$code .= "</div>";
			$code .= "  </object>";
			$code .= " <!--<![endif]-->";
			$code .= "</object>";
		} elseif (($extension == 'mp4') || ($extension == 'webm')) {
			$type = '';
			if ($extension == 'mp4'){
				$type = 'video/mp4';
			} else {
				$type = 'video/webm';
			}
			$div_height = $height - 20;
			$code .= "<a href=\"" .$outUrl. "\"><div style=\"position:absolute; width:" .$width. "px; height:" .$div_height. "px; z-index:10000;\"></div></a>";
			$code .= "<video width=\"" .$width. "\" height=\"" .$height. "\" poster=\"" .$fallbackImage. "\" controls autoplay muted>";
			$code .= "<source src=\"" .$videoSrc. "\" type=\"" .$type. "\"></video>";
			$caption = empty($ad->type_video->caption) ? '' : $outLink.$ad->type_video->caption.'</a><br />';
			$css_class_start = empty($ad->type_video->css_class) ? '' : '<span class="'.$ad->type_video->css_class.'">';
			$css_class_end = empty($ad->type_video->css_class) ? '' : '</span>';
			$code = $css_class_start.$code.$css_class_end.$caption;
		}

		return ($code);
	}


	/**
	 * @param $ad \Models\Ad
	 */
	private function _showFreeform($ad){

		$freeformText = $ad->type_freeform->text ? $ad->type_freeform->text : '';

		return ($freeformText);
	}



	/**
	 * @param $f3 \Base
	 * @param $html string html code to show
	 */
	private function _showLayer($f3, $html){
		$loc = 'top';
		if ($f3->get('GET.loc')) $loc = $f3->get('GET.loc');

		$t = time();
		$layer = '<div style="'.$loc.':0;z-index:256;width: 90%;position: fixed;left: 50%;margin: 0 0 0 -45%;text-align:center;background-color:#FFF;" id="m'.$t.'">';
		$layer .= $html;
		$layer .= '<span style="position:absolute;top:0px;right:0px;margin-left:2px;vertical-align:top;cursor:pointer;font-weight:bold;font-size:18px;" onclick="javascript:document.getElementById(\'m'.$t.'\').style.display=\'none\'">&#10006;</span>';
		$layer .= '</div>';

		return ($layer);
	}

	/**
	 * @param $f3 \Base
	 * @param $ad \Models\Ad
	 */
	private function _getVASTAd($f3, $ad){
		$baseUrl = $f3->get('SCHEME').'://'.$f3->get('HOST').$f3->get('BASE');

		$videoSrc = $ad->type_video->src;
		$extension = strtolower(pathinfo($videoSrc, PATHINFO_EXTENSION));
		$type = '';
		switch ($extension){
			case 'flv':
				$type = 'video/x-flv';
				break;
			case 'mp4':
				$type = 'video/mp4';
				break;
			case 'webm':
				$type = 'video/webm';
				break;
		}
		if (empty($type)) return ('');

		try {

			// Get VAST instance
			// Default version 3.0
			require_once(dirname(__FILE__) . '/../lib/vast/VAST.php');
			$vast = VAST::getInstance();

			// Set params
			$vast->setSystem('mySimpleAds');
			$vast->setTitle($ad->title);
			$vast->setImpressions(array(
					$baseUrl.'/index.php?in&v&a&i&'.$ad->_id
			));
			$vast->setDuration($ad->type_video->duration); // sec

			// Set Media Files
			$vast->setMediaFiles(array(
					array(
							'value'=> $videoSrc,
							'attributes' => array(
									'width' => $ad->type_video->width,
									'height' => $ad->type_video->height,
									'delivery' => 'progressive',
									'type' => $type
							)
					)
			));

			// Set Video Clicks
			$vast->setVideoClicks(array(
					'ClickThrough'=> $ad->type_video->url,
					'ClickTracking'=> $baseUrl.'/index.php?in&v&a&c&'.$ad->_id,
			));

			return($vast->getInline()->toString());
		} catch (Exception $e){
			\Utils::Logger('[getVASTAd]Error generating doc - '.$e);
		}

		return ('');
	}


	/** Check if should show ad to this location
	 * @param $ip
	 * @param $geoOnOff 1=In Selected, 2=In All But Selected
	 * @param null $geoCountries
	 * @param null $geoRegions
	 * @param null $geoCities
	 * @return bool true on passes geo, false otherwise
	 */
	private function _checkGeo($ip, $geoOnOff, $geoCountries = null, $geoRegions = null, $geoCities = null){

		if ((empty($geoCountries)) && (empty($geoRegions)) && (empty($geoCities)) ) return true;

		$found = false;
		$codes = Geolookup::GetCountyRegionCity($ip);

		if ((!empty($geoCities)) && (!empty($codes['city']))){
			foreach ($geoCities as $city) {
				if ($city == $codes['city']){
					$found = true;
					break;				
				}
			}
			if ( (($geoOnOff == 1) && ($found)) || (($geoOnOff == 2) && (!$found)) ){
				return true;
			} else {
				return false;
			}
		} // end cities


		if ((!empty($geoRegions)) && (!empty($codes['region']))){
			foreach ($geoRegions as $region) {
				if ($region == $codes['region']){
					$found = true;
					break;
				}
			}
			if ( (($geoOnOff == 1) && ($found)) || (($geoOnOff == 2) && (!$found)) ){
				return true;
			} else {
				return false;
			}
		} // end regions


		if ((!empty($geoCountries)) && (!empty($codes['country']))){
			foreach ($geoCountries as $country) {
				if ($country == $codes['country']){
					$found = true;
					break;
				}
			}
			if ( (($geoOnOff == 1) && ($found)) || (($geoOnOff == 2) && (!$found)) ){
				return true;
			} else {
				return false;
			}
		} // end countries

		return true;
	}


	/** Calls backend processing tasks
	 * @param string $baseUrl
	 */
	private function _callTasks($baseUrl){
		$timer = '';
		$t = time();

		$cache = \Cache::instance();
		if ($cache->exists('tasks_last_call',$timer)) {
			$timers = explode('|', $timer);

			//Call stats queue processing
			if ($t >= $timers[0] + (60 * 1)){
				$cache->set('tasks_last_call',$t.'|'.$timers[1]);
				$this->_asyncWebCall($baseUrl.'/index.php?pq');
			}

			//Call scheduler
			if ($t >= $timers[1] + (60 * 30)){
				$cache->set('tasks_last_call',$timers[0].'|'.$t);
				$this->_asyncWebCall($baseUrl.'/index.php?scheduler');
			}

		} else {
			$cache->set('tasks_last_call',$t.'|'.$t);
			return;
		}

	}


	/** Makes a pseudo-async web call
	 * @param $url
	 */
	private function _asyncWebCall($url){
		$arrContext = array('http' =>
				array(
						'method' => 'GET',
						'max_redirects' => 0, // Follow no redirects
						'follow_location' => 0,
						'ignore_errors' => true, // Return non-200 requests.
						'timeout' => 0.01
				)
		);

		$context = stream_context_create($arrContext);
		$handle = @fopen($url, 'r', false, $context);
	}

	/**
	 * @param $f3 \Base
	 * @param $u int Uniques
	 * @param $i int Impressions
	 * @param $c int Clicks
	 * @param $ad_id int Ad Id
	 * @param $group_id int Group Id
	 * @param $keyword_id int Keyword Id
	 * @param $site_key string Site Key
	 * @param $sale_id int Sale Id
	 */
	public static function InsertStatsQueue($f3, $u=0, $i=1, $c=0, $ad_id=null, $group_id=null, $keyword_id=null, $site_key=null, $sale_id=null, $hostname=null, $referer=null){
		try{
			$userAgent=empty($_SERVER['HTTP_USER_AGENT']) ? '' : $_SERVER['HTTP_USER_AGENT'];

			//Check bot
			if ((!empty($userAgent)) && (self::_is_bot($userAgent))) return;

			if ($f3->get('GET.r')){
				$ip = $f3->get('GET.r');
			} else {
				$ip=$f3->get('IP');
			}
			if ($ip == '::1') $ip = '127.0.0.1';

			$hostname = ($f3->get('GET.h')) ? $f3->get('GET.h') : ($hostname ? $hostname : '');
			$referer = ($f3->get('GET.rf')) ? $f3->get('GET.rf') : ($referer ? $referer : '');
			if ($hostname != '') $hostname = htmlspecialchars($hostname, ENT_QUOTES);
			if ($referer != '') $referer = htmlspecialchars($referer, ENT_QUOTES);
			
			$f3->get('DB')->exec('INSERT INTO mysa_stats_q (dt, ad_id, group_id, keyword_id, site_key, sale_id, u, i, c, ip, hostname, referer, agent) VALUES(:dt, :ad_id, :group_id, :keyword_id, :site_key, :sale_id, :u, :i, :c, :ip, :hostname, :referer, :agent)', array(':dt' => time(), ':ad_id' => $ad_id,':group_id' => $group_id, ':keyword_id' => $keyword_id, ':site_key' => $site_key, ':sale_id' => $sale_id, ':u' => $u, ':i' => $i, ':c' => $c, ':ip' => ip2long($ip), ':hostname' => $hostname, ':referer' => $referer, ':agent' => $userAgent));
		} catch (Exception $e){
			\Utils::Logger('[_insertStatsQueue]Error inserting stat - '.$e);
		}
	}

	/*
	*
	* @param mixed $user_agent
	* @return returns true if the user agent is a bot
	*/
	private static function _is_bot($user_agent) {
		$user_agent = strtolower($user_agent);

		//array of bot strings to check for
		$bot_strings = array('google', 'bot', 'yahoo', 'spider', 'bing', 'archiver', 'curl', 'python', 'baidu', 'perl', 'sphere', 'pear', 'java', 'wordpress', 'crawl', 'yandex', 'monitor', 'mechanize', 'facebook', 'ask');
		foreach ($bot_strings as $bot) {
			if (strpos($user_agent, $bot) !== false)
				return true;
		}

		return false;
	}


	/**
	 * @param $f3 \Base
	 */
	private function _getIp($f3){
		if ($f3->get('GET.r')){
			$ip = $f3->get('GET.r');
		} else {
			$ip=$f3->get('IP');
		}
		if ((empty($ip)) || ($ip == '::1')) $ip = '127.0.0.1';
		return ($ip);
	}
}