<?php

/**
 * A class for parsing weather information from http://weatherbug.com/
 * 
 * Depends on cURL library and PHP => 5
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * @author Robert Basic
 * @email dpfreak@gmail.com
 * @link http://vtssu.rs/~kazil/
 * @copyright 2008 Robert Basic
 * @package myWeather
 * @version 0.0.1
 */

class myWeather
{
	/**
	 * The URL from witch the information is gathered
	 *
	 * @var string
	 */
	var $url = "http://weather.weatherbug.com/Serbia%20and%20Montenegro/{city}-weather/local-forecast/detailed-forecast.html?zcode=z6286";
	
	/**
	*  The cURL status code
	*
	* @var integer
	*/
	var $curl_status_code = 0;
	
	/**
	* Array of cities for which weather information can be gathered
	*
	* @var array
	*/
	var $cities = array("Subotica","Belgrade","Novi Sad");
	
	/**
	* The city for which the weather information is gathered
	*
	* @var string
	*/
	var $city = "";
	
	/**
	* How many days to show in the forecast? Max is 7
	*
	*  @var integer
	*/
	var $number_of_days_for_forecast = 7;
	
	/**
	* Array with the forecast details (day, date, image, high, low, description)
	*
	* @var array
	*/
	var $forecast = array();

	/**
	* Should it translate to Serbian?
	*
	* @var bool
	*/
	var $translate = true;
	
	/**
	* Array with the translation text (must be filled at time, 'cause "new" words can show up)
	*
	* @var array
	*/
	var $translate_text = array(
		"Weather forcast for" => "Vremenska prognoza za",
		"Monday" => "Ponedeljak",
		"Tuesday" => "Utorak",
		"Wednesday" => "Sreda",
		"Thursday" => "Cetvrtak",
		"Friday" => "Petak",
		"Saturday" => "Subota",
		"Sunday" => "Nedelja",
		"Hi" => "Najviša dnevna",
		"Lo" => "Najniža dnevna",
		"Afternoon clouds" => "Popodne naoblacenje",
		"Morning clouds" => "Ujutro naoblacenje",
		"Showers early" => "Ujutro pljusak",
		"Sprinkles early" => "Ujutro kišica",
		"Sprinkles late" => "Uvece kišica",
		"Hot" => "Toplo",
		"Mostly cloudy" => "Uglavnom oblacno",
		"Mostly sunny" => "Uglavnom suncano",
		"Mild" => "Umereno",
		"Pleasantly warm" => "Prijatno toplo",
		"Pleasantly warm, Dry" => "Prijatno toplo, suvo",
		"More sun than clouds" => "Više sunca nego oblaka",
		"Numerous showers" => "Brojni pljuskovi",
		"Temperature of" => "Temperatura",
		"Winds" => "Vetar ce duvati brzinom od",
		"Humidity will be" => "Vlažnost vazduha ce biti",
		"There is a" => "Šansa za padavine je"
	);
	
	/**
	* Date formats in which the parsed date can be transformed to
	*
	* @var array
	*/
	var $date_formats = array("sr" => "d.m.Y.");
	
	/**
	* Convert to this date format
	*
	* @var string
	*/
	var $date_format = "sr";
	
	/**
	*  The name of the cache file (to put into, or to get out from)
	*  Naming: content_randomNumber_unixTimeStamp.html
	*
	* @var string
	*/
	var $cache_file;
	
	/**
	* Folder for the cache. Default is html
	*
	* @var string
	*/
	var $cache_folder;
	
	/**
	* How long should the cache live? Default is 7200 seconds (2 hours)
	*
	* @var integer
	*/
	var $cache_lifetime = 7200;
	
	/**
	* Get the information from the cache file?
	*
	* @var bool
	*/
	var $from_cache = false;
	
	/**
	 * Constructor
	 * 
	 * @param string $city
	 * @param integer $number_of_days_for_forecast
	 * @param string $cache_folder
	 * @param string $date_format
	 * @param bool $translate
	 */
	function __construct($city = "Belgrade",$number_of_days_for_forecast = 7,$cache_folder = "html",$date_format = "sr",$translate = true)
	{
	
		// If no cURL functions available, it should die();
		if(function_exists("curl_init") === FALSE){
			echo "No cURL functions available!";
			exit;
		}

		(in_array($city,$this->cities)) ? $this->city = $city : $this->city = "Belgrade";
		
		if($number_of_days_for_forecast < 1) $number_of_days_for_forecast = 1;
		elseif($number_of_days_for_forecast > 7) $number_of_days_for_forecast = 7;
		$this->number_of_days_for_forecast = $number_of_days_for_forecast;
		
		$this->cache_folder = $cache_folder;
		
		(in_array($date_format,$this->date_formats)) ? $this->date_format = $date_format : $this->date_format = "sr";
		
		$this->translate = $translate;
	
		// Check if cache is young enough or exists
		$this->checkCache();
	
		// If not from cache
		if(!$this->from_cache){
			$this->clearCache();
			$this->setCityInUrl();
			$this->createHtmlFile();
			$this->getDataFromUrl();
		}
	
		// Get the data from the cache file
		if($this->curl_status_code == 200 || $this->from_cache){
			$this->getDataFromHtmlFile();
		}
	
		// if there is forecast to show, show it
		if(count($this->forecast) > 0){
			$this->showForecast();
		}
	
	}
	
	/**
	 * Show the forecast. Needs some Javascript/CSS tuning.
	 * 
	 */
	private function showForecast()
	{
	
		$box = "<link rel=\"stylesheet\" href=\"myWeather.css\" type=\"text/css\" />\n";
		$box .= "<div id='myWeather-main'>\n";
		$box .= "\t<h2>".$this->translate_text['Weather forcast for'].": $this->city</h2>\n";
		
		foreach($this->forecast as $forecast){
			$box .= "<div class='myWeather-day-date'>\n\t".$this->translate($forecast['day'],1). " :: " . $this->translate($forecast['date'],2) . "\n</div>\n";
			$box .= "<div class='myWeather-image'>\n\t".$forecast['image']."\n</div>\n";
			$box .= "<div class='myWeather-high-low'>\n\t".$this->translate($forecast['high'],3)."<br />".$this->translate($forecast['low'],3)."\n</div>\n";
			$box .= "<div class='myWeather-description'>\n\t".$this->translate($forecast['description'],4)."\n</div>\n";
		}
		
		$box .= "</div>\n";
	
		echo $box;
	
	}
	
	/**
	* Translate the forecast. If they change how the info is showing up, this'll need a full rewrite :-/
	*
	* @param string $string
	* @param integer $type
	*/
	private function translate($string, $type = 1)
	{
		// Do not translate
		if($this->translate === false) return $string;
		
		switch($type){
		
			case 1:{ // An ordinary sentence to translate (ie. "Sunny", "Hot", "Raining" etc...)
				if(array_key_exists($string,$this->translate_text)) return $this->translate_text[$string];
				else return $string;
			}
			break;
			
			case 2:{ // Translate the date
				return date($this->date_formats[$this->date_format],strtotime($string));
			}
			break;
			
			case 3:{ // Translate the high/low temperatures
				$string = explode(":",$string);
				list($text, $temperature) = $string;
				if(array_key_exists($text,$this->translate_text)) $text = $this->translate_text[$text];
				return $text .": ". trim($temperature) ."C";
			}
			break;
			
			case 4:{ // Translate sentences with variable data in them. (ie. "Temperature of 24°C", "There is a 37% chance of precipitation." etc)
				$sentences = explode(".",$string); // We go sentence by sentence
				for($i=0;$i<count($sentences);$i++){
					$sentences[$i] = trim($sentences[$i]); // Trim whitespaces
					if(array_key_exists($sentences[$i],$this->translate_text)){ // If an ordinary translation...
						$sentences[$i] = $this->translate_text[$sentences[$i]];
					}
					else{
						//First type of sentences...
						if(preg_match("'(?<=Temperature \s of \s) \d .*'ixs",$sentences[$i],$temp_degree) && array_key_exists("Temperature of",$this->translate_text)){
							preg_match("'(?:Temperature \s of)'ixs",$sentences[$i],$temp_text);
							$sentences[$i] = $this->translate_text[$temp_text[0]] . " " . $temp_degree[0];
						}
						// Second type of sentences
						elseif(preg_match("'(?<=Winds \s) \d .*'ixs",$sentences[$i],$wind_speed_direction) && array_key_exists("Winds",$this->translate_text)){
							preg_match("'(?:Winds)'ixs",$sentences[$i],$wind_text);
							$from = array("E","W","N","S","NE","NW","SE","SW","ENE","WNW","ESE","WSW","NNE","NNW","SSE","SSW");
							$to = array("I","Z","S","J","SI","SZ","JI","JZ","ISI","ZSZ","IJI","ZJZ","SSI","SSZ","JJI","JJZ");
							$wind_speed_direction[0] = str_replace($from,$to,$wind_speed_direction[0]);
							$sentences[$i] = $this->translate_text[$wind_text[0]] . " " . $wind_speed_direction[0];
						}
						//Third type of sentences
						elseif(preg_match("'(?<=Humidity \s will \s be \s) \d{2,3}.'ixs",$sentences[$i],$humidity_percent) && array_key_exists("Humidity will be",$this->translate_text)){
							preg_match("'(?:Humidity \s will \s be)'ixs",$sentences[$i],$humidity_text);
							$sentences[$i] = $this->translate_text[$humidity_text[0]] . " " . $humidity_percent[0];
						}
						// Fourth type of sentences
						elseif(preg_match("'(?<=There \s is \s a \s) \d{1,2}.'ixs",$sentences[$i],$precipitation_percent) && array_key_exists("There is a",$this->translate_text)){
							preg_match("'(?:There \s is \s a)'ixs",$sentences[$i],$precipitation_text);
							$sentences[$i] = $this->translate_text[$precipitation_text[0]] . " " . $precipitation_percent[0];
						}
					}
				}
				return implode(". ",$sentences); // Glue the sentences back together...
			}
			break;
			default:{
				if(array_key_exists($string,$this->translate_text)) return $this->translate_text[$string];
				else return $string;
			}
			break;
		}
	
	}
	
	/**
	* Get the data from the URL. Uses cURL library
	*
	* @param string $string
	* @param integer $type
	*/
	private function getDataFromUrl()
	{
		// Init
		$ch = curl_init($this->url);
		// Open up the cache file for writing
		$fh = fopen($this->cache_file, "wb+");
		// The output of curl goes into the cache file
		curl_setopt($ch, CURLOPT_FILE, $fh);
		// Execute curl
		curl_exec($ch);
		// Get the status code
		$this->curl_status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
		//Close...
		curl_close($ch);
		fclose($fh);
		
	}
	
	/**
	* Check the cache, if the cache file is young enough
	*
	*/
	private function checkCache()
	{
		$files = scandir($this->cache_folder);
		$files = implode(",",$files);
		
		preg_match_all('/(?<=content\_)\w{1,}/i',$files,$matches);
		$matches = $matches[0];
		
		$file_ts = array(); // the timestamp that is glued to the file name
		$file_rnd = array(); // the random number that is glued to the file name
		
		for($i=0;$i<count($matches);$i++){
			$matches[$i] = explode("_",$matches[$i]);
			$file_ts[$i] = $matches[$i][1];
			$file_rnd[$i] = $matches[$i][0];
		}
		$now = time();	
		foreach($file_ts as $key => $val){
			$diff = $now - $val;
			if($diff < $this->cache_lifetime){ // if it's young enough
				$this->from_cache = true;
				$this->cache_file = $this->cache_folder . "/" . "content_" . $file_rnd[$key] . "_" . $val . ".html";
			}
			else{
				$this->from_cache = false;
			}			
		}		
	}
	
	/**
	* Clear the cache folder, we don't want old files hangin' around...
	*
	*/
	private function clearCache()
	{
		$files = scandir($this->cache_folder);
		foreach($files as $file){
			if($file != "." || $file != "..") @unlink("html/$file");
		}
	}
	
	/**
	* Get the data from the html (cache) file
	*
	*/
	private function getDataFromHtmlFile()
	{
		$file_content = file_get_contents($this->cache_file);
		if($file_content != ""){
			$this->getForecast($file_content);
		}
	}
	
	/**
	* Gets the forecast information from the html (cache) file
	* It's done in several stages, this way was easier...
	*
	*/
	private function getForecast(&$file_content)
	{
		$forecast = array();
		for($i=0;$i<$this->number_of_days_for_forecast;$i++){
			$j=$i+1;
			// For the last day it's a bit different regex
			$regex_1st_part = "'<div \s class=\"wXforecast-detailed-day-int\" \s id=\"wXforecast-day-".$i."\">(.*?)";
			($i==6) ? $regex_2nd_part = "<div \s class=\"wXforecast-additional\">'ixs" : $regex_2nd_part = "<div \s class=\"wXforecast-detailed-day-int\" \s id=\"wXforecast-day-".$j."\">'ixs";
			$regex = $regex_1st_part . $regex_2nd_part;
			preg_match($regex,$file_content,$forecast[$i]);
		}
	
		foreach($forecast as $key => $val){
			$forecast[$key] = $forecast[$key][1];
		}
		
		$tmp_forecast = array();
		
		foreach($forecast as $key => $val){
			// Get the day
			preg_match("'<div \s class=\"wXfore-day\">(.*?)</div>'ixs",$val,$tmp_forecast['day'][$key]);
			// Get the date
			preg_match("'<div \s class=\"wXfore-date\">(.*?)</div>'ixs",$val,$tmp_forecast['date'][$key]);
			// Get the image
			preg_match("'<div \s class=\"wXfore-day-img\">(.*?)</div>'ixs",$val,$tmp_forecast['image'][$key]);
			// Get the high
			preg_match("'<div \s class=\"wXforecast-hi\">(.*?)</div>'ixs",$val,$tmp_forecast['high'][$key]);
			// Get the low
			preg_match("'<div \s class=\"wXforecast-lo\">(.*?)</div>'ixs",$val,$tmp_forecast['low'][$key]);
			// Get the whole text
			preg_match("'<div \s class=\"wXfore-day-desc\">(.*?)</div>'ixs",$val,$tmp_forecast['description'][$key]);
		}

		for($i=0;$i<count($tmp_forecast['day']);$i++){
			$this->forecast[$i]['day'] = $tmp_forecast['day'][$i][1];
			$this->forecast[$i]['date'] = $tmp_forecast['date'][$i][1];
			$this->forecast[$i]['image'] = $tmp_forecast['image'][$i][1];
			$this->forecast[$i]['high'] = $tmp_forecast['high'][$i][1];
			$this->forecast[$i]['low'] = $tmp_forecast['low'][$i][1];
			$this->forecast[$i]['description'] = $tmp_forecast['description'][$i][1];
		}
	}
	
	/**
	* Set the city in the URL
	*
	*/
	private function setCityInUrl()
	{
		$this->url = str_replace("{city}",$this->city,$this->url);
	}
	
	/**
	* Actually, create the cache file... but it's the same...
	*
	*/
	private function createHtmlFile()
	{
		if(!is_dir($this->cache_folder)){
			mkdir($this->cache_folder);
			chmod($this->cache_folder, 0755);
		}
		mt_srand();
		$random = mt_rand()."_".time();
		$this->cache_file = $this->cache_folder . "/" . "content_".$random.".html";
	}
	
}

?>