<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
/**
* PHP versions 5
*
****************************************************************************
jma2xml - Class of API for accessing and retrieving information from JMA
Copyright (C) 2010 Kuroneko Square

This program 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 2
of the License, or (at your option) any later version.

This program 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.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*****************************************************************************/

define('DEF_CACHE_DIR', "./cache"); // if null, rebuild feed
define('DEF_CACHE_TIME', 36000);// cache time 1800=30minutes

//version
# 0.08 optimized (runs only available on PHP5)
# 0.07 cache data type changed(cache as serealized data)
# 0.06 bug fixed
# 0.05 function renamed(cache as original html data)
# 0.04 stable
# 0.03 functionized
# 0.02 bug fix
# 0.01 first release

/**
 * Class of API for accessing and retrieving information from JMA.
 *
 * @package API_JMA
 * @author  kurosquare
 * @access  public
 * @version Release: 0.0.8
 */

class jma2xml {
    /**
    * @access public
    */
	public		$evt;
	public		$format;
	public		$options;
	public		$tz;
	
    /**
    * @access private
    * @var string $options
    */
	private 	$api_version;
	private		$unix;
	
	//----------
	public function jma2xml(){
		$this->evt['title'] = 'EVT';
		$this->evt['link'] = 'http://www.kuroneko-square.net/services';
		$this->evt['description'] = '天気予報';
		$this->api_version = '008';
		$this->format = 'rss2';
		$this->options = NULL;
		$this->options['area_id'] = '356';
		$this->tz = '+9:00';
		$this->unix = time();
	}
	//----------
	public function main(){
		if(isset($this->options['area_id'])){
			$area_id = explode(',', htmlspecialchars(preg_replace("/(<.+>)|\n|\s/", NULL, $this->options['area_id'])));
		}else{echo 'エラー: URLが入力されていません';exit;}
		
		//getarray for each area
		$this->type = 'yoho';
		foreach($area_id as $key => $area){
			if($area <=356 && $area >=1){
				$this->area = $area;
				$data[$area] = $this->sendRequest();
			}
		}
		$Result = $data;
		
		//output
		switch ($this->format) {
			default:
				$this->output_xml($Result);
				break;
		}
	}
	//----------
	private function getBaseUrl($type){
		return "http://www.jma.go.jp/jp/yoho/".$this->area.".html";
	}
	//----------
    /**
     * Send Request
     *
     * @param  array   $options
     */
	private function sendRequest(){
		$url = $this->getBaseUrl('request');
		return $this->cacheOperation($url);
	}
	//----------
	private function cacheOperation($url){
		// If CACHE_DIR exist
		$cache_file = '';
		if (DEF_CACHE_DIR != '') {
			$cache_file = DEF_CACHE_DIR .'/'. md5($url);
			$time_newest = @filemtime($cache_file);
			
			$timedif = @(time() - $time_newest);
			if ($timedif < DEF_CACHE_TIME) {
				// If-Modified-Since
				/*$request_headers = apache_request_headers();
				$etag = md5( $_SERVER["REQUEST_URI"] . $time_newest );
				
				if( $request_headers["If-Modified-Since"] ) {
				    $since = parse_http_date( $request_headers["If-Modified-Since"] );
				    if( $since["timestamp"] >= $time_newest ) {
				        header( "HTTP/1.1 304 Not Modified" );
				        header( "Etag: \"$etag\"" );
				        exit();
				    }
				}*/
				// Return cache
				$data = unserialize(file_get_contents($cache_file));
			} else {
				// Create new data
				$data = $this->parse_main(file_get_contents($url));
				// save data as cache file
				if ($f = @fopen($cache_file, 'w')) {
					$s_data = serialize($data);
					fwrite ($f, $s_data, strlen($s_data));
					fclose($f);
				}
			}
		}
		// No cache
		else {
			//getarray
			$data = $this->parse_main(file_get_contents($url));
		}
		return $data;
	}
	//----------
	private function strip_crlf($data){
		$data = str_replace("\r\n", "\n", $data);
		$data = str_replace("\r", "\n", $data);
		$data = str_replace("\n\n", "\n", $data);
		$data = str_replace("\t", "", $data);
		$data = str_replace(PHP_EOL, "", $data);
		return $data;
	}
	//------------
	private function parse_main($data){
		
		//exclude str
		$exp_str = array(' ','%','度');
		$tempexp = "%(.+?)\(&#177;(.+?)\)%";
		
		$pref_id = $this->area;
		$type = $this->type;
		
		//parse
		$data = mb_convert_encoding($data, "UTF-8", "SJIS");
		$data = $this->strip_crlf($data);
		
		$area = '';
		switch($type){
			case 'yoho':
				$replace_exp = array(' align="middle"',' colspan=2',' style="float: left"', ' style="float: right"',' align="left"',' align="right"',' nowrap',' class="font-size-clear"', ' class="jikeibtn" type="button"');
				$data = str_replace($replace_exp, '', $data);
				
				$data = str_replace("<th></th>", "<th> </th>", $data);
				$data = str_replace("<br></td>", "<br> </td>", $data);
				$data = str_replace("max\"></td>", "max\"> </td>", $data);
				$data = str_replace("min\"></td>", "min\"> </td>", $data);
				$data = str_replace("city\"></td>", "city\"> </td>", $data);
				
				//pub
				$pubregexp = "%<pre class=\"textframe\">天気概況(.+?)　(.+?)発表%";
				preg_match_all($pubregexp, $data, $pub);
				$author = $pub[2][0];
				$strpubdate = $this->parse_dtf($pub[1][0]);
				
				//data
				$items = '';
				$itemregexp =  "%<tr><th class=\"th-area\"><div>(.+?)</div><div><input title=\"地域時系列予報へ\" value=\"地域時系列予報へ\" onclick=\"javascript:goJikei\(\)\"></div></th><th class=\"th-rain\">降水確率</th><th class=\"th-temp\">気温予報</th></tr><tr><th class=\"weather\">(.+?)<br><img src=(.+?) title=\"(.+?)\" alt=\"(.+?)\"><br></th><td class=\"info\">(.+?)</td><td class=\"rain\"><div><table class=\"rain\"><tr>(.+?)</tr></table></div></td><td class=\"temp\"><div><table class=\"temp\"><tr><th> </th><th>(.+?)</th><th>(.+?)</th></tr><tr>(.+?)</tr></table></div></td></tr><tr><th class=\"weather\">(.+?)<br><img src=(.+?) title=\"(.+?)\" alt=\"(.+?)\"><br></th><td class=\"info\">(.+?)</td><td class=\"rain\"><div><table class=\"rain\"><tr>(.+?)</tr></table></div></td><td class=\"temp\"><div><table class=\"temp\"><tr><th> </th><th>(.+?)</th><th>(.+?)</th></tr><tr>(.+?)</tr></table></div></td></tr>%";
				preg_match_all($itemregexp, $data, $items);
				
				//image
				$imgregexp = "%alt=\"(.+?)\"%";
				
				$num = count($items[0]);
				$key = 0;
				while($key<=$num-1){
					// "$daystr + 3" means that we needs data of 2days later from the weekly forecast. 
					//forecastdate str
					$forecastdatestr = array($items[2][$key],$items[11][$key]);
					foreach($forecastdatestr as $daystr => $value){
						$area[$daystr+1]['date'] = strip_tags($value);
					}
					//weather
					$weather = array($items[5][$key],$items[14][$key]);
					foreach($weather as $daystr => $value){
						$area[$daystr+1]['weather'] = strip_tags($value);
					}
					//weather detail
					$weather_detail = array($items[6][$key], $items[15][$key]);
					foreach($weather_detail as $daystr => $value){
						$area[$daystr+1]['weather_detail'] = strip_tags($value);
					}
					//rainfallchance
					$rains = array($items[7][$key],$items[16][$key]);
					foreach($rains as $daystr => $value){
						preg_match_all("%<td>(.+?)</td><td>(.+?)</td>%", $value, $rainfallchances);
						$rainfallchance_key = $rainfallchances[1];
						$rainfallchance = $rainfallchances[2];
						foreach($rainfallchance as $k => $value){
							$keyname = $rainfallchance_key[$k];
							$area[$daystr+1]['rainfallchance'][$keyname] = str_replace($exp_str,'',$value);
						}
					}
					//temperature max and min
					$temp = array($items[10][$key],$items[19][$key]);
					
					foreach($temp as $daystr => $value){
						preg_match("%<td class=\"city\">(.+?)</td><td class=\"min\">(.+?)</td><td class=\"max\">(.+?)</td>%", $value, $temperatures);
						$temperature_key = array('city','min','max');
						$temperature = array($temperatures[1], $temperatures[2], $temperatures[3]);
						foreach($temperature as $k => $value){
							$keyname = $temperature_key[$k];
							$area[$daystr+1]['temperature'][$keyname] = str_replace($exp_str,'',$value);
						}
					}
					$areaname = str_replace($exp_str,'',$items[1][$key]);
					$arrayItems[$areaname] = $area;
					$key++;
				}
				break;
			default:
				break;
		}
		return $arrayItems;
	}
	//------------
	// validate parameter
	private function validate_options($options = array()){
		$parameter_available = array(
			'area_id'
		);
		foreach($options as $key => $value){
			if(!in_array($key,$parameter_available)|empty($value)){
				unset($options[$key]);
			}
		}
		return $options;
	}
	
	//------------
	/**
	 * Gets the date stored in this FeedDate as an RFC 822 date.
	 *
	 * @return a date in RFC 822 format
	 */
	private function rfc822() {
		//return gmdate("r",$this->unix);
		$date = date("D, d M Y H:i:s", $this->unix);
		if ($this->tz!="") $date .= " ".str_replace(":","",$this->tz);
		return $date;
	}
	
	// convert to ISO 8601 format
	private function parse_dtf($strdate) {
		$mb_num_list = array(
			'０' => '0', '１' => '1', '２' => '2', '３' => '3', '４' => '4',
			'５' => '5', '６' => '6', '７' => '7', '８' => '8', '９' => '9'
		);
		foreach($mb_num_list as $conv_mb_num => $replace) {
			$strdate = str_replace($conv_mb_num, $replace,$strdate);
		}
		$pat = "/(平成|昭和|大正|明治)([0-9０-９]{2})年([0-9０-９]{1,2})月([0-9０-９]{1,2})日([0-9０-９]{1,2})時([0-9０-９]{1,2})分/u";
		if (preg_match($pat, $strdate, $match)) {
			list($gengo, $year, $month, $day, $hours, $minutes, $seconds, $tz) =
			array($match[1], $match[2], $match[3], $match[4], $match[5], $match[6], '00', $tz);
			$strdate = $this->conv_jpyear($gengo,$year) . '-' . $month . '-' . $day . 'T' . $hours . ':' . $minutes . ':' . $seconds . $this->tz;
		}
		return $strdate;
	}
	//-----------------------------------------------
	// □　和暦から西暦に変換
	//-----------------------------------------------
	private function conv_jpyear($gengo,$strwareki_year){
		switch($gengo){
			case '平成':
			case '4':
				$stryear = 1988+(integer)$strwareki_year;
				break;
			case '昭和':
			case '3':
				$stryear=1925+(integer)$strwareki_year;
				break;
			case '大正':
			case '2':
				$stryear=1911+(integer)$strwareki_year;
				break;
			case '明治':
			case '1':
				$stryear=1867+(integer)$strwareki_year;
				break;
		}
		return $stryear;
	}
	//----------
	private function output_xml($items){
		$dom = new DomDocument('1.0');  // create DOM
		$dom->encoding = "UTF-8";
		$dom->formatOutput = true; // 出力XMLを整形(改行,タブ)する
		
		$ExportData = $dom->appendChild($dom->createElement('weatherforecast'));
		
		//header
		$header = array('title' => 'weather forecast xml','description' => '気象庁の天気予報情報を XML で配信。','link'=>"http://", 'pubDate'=>$this->rfc822(), 'author' => '気象庁', 'managingEditor'=>'くろねこスクエア');
		$elements_header = array('title','description','link','pubDate','author','managingEditor');
		foreach($header as $key => $value){
			if(in_array($key, $elements_header)){
				$key = $ExportData->appendChild($dom->createElement($key));
				$key->appendChild($dom->createTextNode($value));
			}
		}
		
		if(is_array($items)){
			//Pref
			foreach($items as $pref_id => $prefitems){
				$pref = $ExportData->appendChild($dom->createElement('pref'));
				$prefname = $this->get_prefname();
				$pref->setAttribute('id',$prefname[$pref_id]);
				
				$elements_area = array('weather','weather_detail','wave','temperature','rainfallchance');
				//area
				foreach($prefitems as $area_id => $area){
					$areacontent= $pref->appendChild($dom->createElement('area'));
					$areacontent->setAttribute('id',$area_id);
					//info
					foreach($area as $info){
						$infocontent= $areacontent->appendChild($dom->createElement('info'));
						foreach($info as $fieldname => $value){
							if(in_array($fieldname, $elements_area)){
								$Field= $infocontent->appendChild($dom->createElement($fieldname));
									switch($fieldname){
										case 'temperature':
											$Field->setAttribute('unit','摂氏');
											foreach($value as $k => $val){
												if($k == 'city'){
													$Field->setAttribute('city',$val);
												}else{
													$temp_detail= $Field->appendChild($dom->createElement('range'));
													$temp_detail->setAttribute('centigrade',$k);
													$temp_detail->appendChild($dom->createTextNode($val));
											}
											}
										break;
										case 'rainfallchance':
											$Field->setAttribute('unit','%');
											foreach($value as $k => $val){
											$temp_detail= $Field->appendChild($dom->createElement('period'));
												$temp_detail->setAttribute('hour',$k);
												$temp_detail->appendChild($dom->createTextNode($val));
											}
											break;
										default:
											$Field->appendChild($dom->createTextNode($value));
									}
							}elseif($fieldname=='date'){
								$infocontent->setAttribute('date',$value);
							}
						}//field
					}//info
				}//area
			}//pref
		}
		header("Content-type: text/xml; name=" . $fileName);
		
		echo $dom->saveXML();
	}
	//----------
	private function get_prefname(){
		return array(
			'301' => '宗谷支庁',
			'302' => '上川、留萌支庁',
			'303' => '網走支庁',
			'304' => '釧路、根室、十勝支庁',
			'305' => '胆振、日高支庁',
			'306' => '石狩、空知、後志支庁',
			'307' => '渡島、檜山支庁',
			'308' => '青森県',
			'309' => '秋田県',
			'310' => '岩手県',
			'311' => '山形県',
			'312' => '宮城県',
			'313' => '福島県',
			'314' => '茨城県',
			'315' => '群馬県',
			'316' => '栃木県',
			'317' => '埼玉県',
			'318' => '千葉県',
			'319' => '東京都',
			'320' => '神奈川県',
			'321' => '山梨県',
			'322' => '長野県',
			'323' => '新潟県',
			'324' => '富山県',
			'325' => '石川県',
			'326' => '福井県',
			'327' => '静岡県',
			'328' => '岐阜県',
			'329' => '愛知県',
			'330' => '三重県',
			'331' => '大阪府',
			'332' => '兵庫県',
			'333' => '京都府',
			'334' => '滋賀県',
			'335' => '奈良県',
			'336' => '和歌山県',
			'337' => '島根県',
			'338' => '広島県',
			'339' => '鳥取県',
			'340' => '岡山県',
			'341' => '香川県',
			'342' => '愛媛県',
			'343' => '徳島県',
			'344' => '高知県',
			'345' => '山口県',
			'346' => '福岡県',
			'347' => '佐賀県',
			'348' => '長崎県',
			'349' => '熊本県',
			'350' => '大分県',
			'351' => '宮崎県',
			'352' => '鹿児島県',
			'353' => '沖縄本島地方',
			'354' => '大東島地方',
			'355' => '宮古島地方',
			'356' => '八重山地方'
		);
	}
	//----------
}
//----------
$debug=1;
if($debug){
	//Set Service option
	//eg. http://example.jp/jma2xml/php?area_id=314
	foreach($_GET as $key => $value){
		$options[$key] = !empty($value)? htmlspecialchars($value):NULL;
	}
	$ObjJMAX = new jma2xml;
	$ObjJMAX->jma2xml();
	$ObjJMAX->format = isset($_GET['format'])? htmlspecialchars($_GET['format']):NULL;
	$ObjJMAX->options = $options;
	$ObjJMAX->main();
}
?>