<?php

const TIME_MAP = array('week' => 604800, 'day' => 86400, 'hour' => 3600, 'minute' => 60, 'off' => 0);

class App {
	
	public static function rrmdir($dir) {
	 if (is_dir($dir)) {
		 $objects = scandir($dir);
		 foreach ($objects as $object) {
			 if ($object != "." && $object != "..") {
				 if (is_dir($dir. DIRECTORY_SEPARATOR .$object) && !is_link($dir."/".$object))
					 App::rrmdir($dir. DIRECTORY_SEPARATOR .$object);
				 else
					 unlink($dir. DIRECTORY_SEPARATOR .$object);
			 }
		 }
		 rmdir($dir);
	 }
	}
	
	public static function find_per($cache_seconds){
		foreach(TIME_MAP as $per => $val){
			if($cache_seconds >= $val){
				return $per;
			}
		}
		return 'off';	
	}
	
	public static function parseDatasources($html_dir, $lines = null){
		$ds = array();
		$ls = array();
		$use_dt = false;
		
		if($lines == null){
			$lines = file($html_dir.'/index.php');
		}
		
		foreach ($lines as $i => $line) {
			if(preg_match('/const SHOW_DATATABLES = (.*);/', $line, $matches)){
				$use_dt = ($matches[1] == 'true');
				
			}else if(preg_match('/<script src="data_(file|pg|gs)(\d+)\.php" data\-jfn="(.*)"/', $line, $matches)){
				$dsi = $matches[2];
				$v = array('data_type' => $matches[1], 'line' => $line, 'ln' => $i, 'name' => $matches[3], 'json_filename' => $matches[3],
							'pg_host' => '', 'pg_db' => '', 'pg_user' => '', 'pg_pwd' => '', 'pg_port' => 5432,
							'gs_host' => '', 'gs_user' => '', 'gs_pwd' => '', 'gs_ws' => '',
							'pg_cache_per' => 'minutes', 'pg_cache_val' => '',
							'gs_cache_per' => 'minutes', 'gs_cache_val' => '');
				
				$content = null;
				if($matches[1] == 'pg'){
					$content = file_get_contents($html_dir.'/data_pg'.$matches[2].'.php');
					if(preg_match('/new Database\("(.*)", "(.*)", "(.*)", "(.*)", (\d+),/', $content, $pg_matches)){
						$v['pg_host'] = $pg_matches[1];
						$v['pg_db']   = $pg_matches[2];
						$v['pg_user'] = $pg_matches[3];
						$v['pg_pwd']  = $pg_matches[4];
						$v['pg_port'] = $pg_matches[5];
					}
					
				}else if($matches[1] == 'gs'){
					$content = file_get_contents($html_dir.'/data_gs'.$matches[2].'.php');
					if(preg_match('/:\/\/(.*):(.*)@(.*)\/geoserver\/.*&typeName=(.*):/', $content, $gs_matches)){
						$v['gs_host'] = $matches[1];
						$v['gs_user'] = $matches[2];
						$v['gs_pwd']  = $matches[3];
						$v['gs_ws']	  = $matches[4];
					}
				}
				
				if($content && preg_match('/\$CACHE_PERIOD = ([0-9]+)/', $content, $matches)) {
					$cache_seconds = $matches[1];
					$per = App::find_per($cache_seconds);
					$v[$v['data_type'].'_cache_val'] = (TIME_MAP[$per] == 0) ? 0 : $cache_seconds / TIME_MAP[$per];
					$v[$v['data_type'].'_cache_per'] = $per;
				}
				
				$ds[$dsi] = $v;
			
			}else if(preg_match('/<\?php include\("layer_(wms|gs_geo)(\d+)\.php"\); \?>/', $line, $matches)){
				$lyi = $matches[2];
				$v = array('layer_type' => $matches[1], 'line' => $line, 'ln' => $i, 'name' => '',
									 'ly_ws'	=> '', 'ly_layer' => '', 'ly_user' => '', 'ly_pwd' => '',
								 'wms_user' => '', 'wms_pwd' => '', 'wms_url' => '',
							 'gs_geo_host' => '', 'gs_geo_user' => '', 'gs_geo_pwd' => '', 'gs_geo_ws' => '', 'gs_geo_layer' => '',
						 	 'gs_geo_color' => '', 'gs_geo_opacity' => '', 'gs_geo_fill_color' => '', 'gs_geo_fill_opacity' => '',
						 	 'gs_geo_cache_per' => 'minutes', 'gs_geo_cache_val' => '');
				
				$content = file_get_contents($html_dir.'/layer_wms'.$lyi.'.php');
				
				if(preg_match('/var (.*) = L\.(WMS\.layer|geoJson\()/', $content, $var_matches)){
					$v['layer_varname'] = $var_matches[1];
				}
				
				if(preg_match('/var .* = L\.WMS\.layer\("(.*)", "(.*):(.*)",\s+{/', $content, $matches)){
					if(str_starts_with($matches[1], 'proxy_wms')){	// if secured through proxy
						$content = file_get_contents($html_dir.'/'.$matches[1]);
						if(preg_match('/const BASE_URL = \'http[s]?:\/\/(.*):(.*)@(.*)\';/', $content, $url_matches)){
							$v['wms_user'] = $url_matches[1];
							$v['wms_pwd']  = $url_matches[2];
							$v['wms_url']  = $url_matches[3];
						}
					}else{
						$v['wms_url'] 		= $matches[1];
					}
					$v['wms_ws']	 		= $matches[2];
					$v['wms_layer']	= $matches[3];
				}
				$v['name'] = $v['wms_ws'].':'.$v['wms_layer'];
				
				if(is_file($html_dir.'/layer_gs_geo'.$lyi.'.php')){
					$content = file_get_contents($html_dir.'/layer_gs_geo'.$lyi.'.php');
					if(preg_match('/:\/\/(.*):(.*)@(.*)\/geoserver\/.*&typeName=(.*):([^&]+)/', $content, $matches)){
						$v['gs_geo_host'] 		= $matches[3];
						$v['gs_geo_user'] 	= $matches[1];
						$v['gs_geo_pwd']  	= $matches[2];
						$v['gs_geo_ws']	 		= $matches[4];
						$v['gs_geo_layer']	= $matches[5];
					}
					
					if(preg_match('/color: "(.*)",/', $content, $matches)){	$v['gs_geo_color'] = $matches[1]; }
					if(preg_match('/fillColor: "(.*)",/', $content, $matches)){	$v['gs_geo_fill_color'] = $matches[1]; }
					if(preg_match('/opacity: ([0-9\.]+),/', $content, $matches)){	$v['gs_geo_opacity'] = $matches[1]; }
					if(preg_match('/fillOpacity: "([0-9\.]+)",/', $content, $matches)){	$v['gs_geo_fill_opacity'] = $matches[1]; }
					
					if(preg_match('/\$CACHE_PERIOD = ([0-9]+)/', $content, $matches)) {
						$cache_seconds = $matches[1];
						$per = App::find_per($cache_seconds);
						$v['gs_geo_cache_val'] = (TIME_MAP[$per] == 0) ? 0 : $cache_seconds / TIME_MAP[$per];
						$v['gs_geo_cache_per'] = $per;
					}
				}
				
				$ls[$lyi] = $v;
			}
		}
		
		return [$ds, $ls, $use_dt];
	}
	
	public static function update_template($src, $dest, $vars){
		
		$lines  = file($src);
		$fp = fopen($dest, 'w');
		
		foreach($lines as $ln => $line){
			foreach($vars as $k => $v){
				if(str_contains($line, $k)){
					$line = str_replace($k, $v, $line);
				}
			}
			fwrite($fp, $line);
		}
		
		fclose($fp);
	}
	
	private static function extract_varname($filename){
		// extract varname from first line of data file
		$js_fp = fopen($filename, 'r');
		$line = fread($js_fp, 1024);
		fclose($js_fp);
		
		$eq_pos = strpos($line, '=');
		$js_varname_decl = substr($line, 0, $eq_pos);			// var json_neighborhoods_2
		$js_varname = explode(' ', $js_varname_decl)[1];	//     json_neighborhoods_2
		return $js_varname;
	}
	
	public static function updateDatasources($details, $html_dir, $data_dir, $apps_dir){
		$changes = 0;
		$js_varnames = array();
		$lines = file($html_dir.'/index.php');
		list($fds,$lys,$use_dt) = App::parseDatasources($html_dir, $lines);	// file data sources
		
		$newId = $details['id'];
		
		foreach($fds as $dsi => $ds) {
			
			if(	empty($details['data_type'.$dsi])){		// if we have datasource from form					
				continue;
			}

			$json_filename = $ds['json_filename'];
			
			$js_varname = App::extract_varname($data_dir.'/'.$newId.'/'.$json_filename);
			if(isset($details['use_datatable'])){
				array_push($js_varnames, $js_varname);
			}
			
			// update fds lines	
			if($details['data_type'.$dsi] == 'file'){
				
				$vars = ['MAP_ID_VALUE' => $newId,
					'DATA_FILE' => '../../../data/'.$newId.'/'. $json_filename
				];
				App::update_template('../snippets/data_file.php', $html_dir.'/data_file'.$dsi.'.php', $vars);

				$lines[$ds['ln']] = '<script src="data_file'.$dsi.'.php" data-jfn="'.$json_filename.'"></script>'."\n";
				$changes = $changes + 1;
			}else{
				// extract PG table from filename
				$pg_tbl = null;
				if(preg_match('/([a-z]+)/', $json_filename, $matches)){
					$pg_tbl = $matches[1];
				}
					
				if($details['data_type'.$dsi] == 'pg'){
					
					$cache_seconds = TIME_MAP[$details['pg_cache_per'.$dsi]] * intval($details['pg_cache_val'.$dsi]);
					
					$vars = [ 'MAP_ID_VALUE' => $newId, 'VARNAME' => $js_varname, 'CACHE_PERIOD_SECONDS' => $cache_seconds,
						'PG_HOST' => $details['pg_host'.$dsi], 'PG_DB'  => $details['pg_db'.$dsi],
						'PG_USER' => $details['pg_user'.$dsi], 'PG_PWD' => $details['pg_pwd'.$dsi],
						'PG_PORT' => $details['pg_port'.$dsi], 'PG_TBL' => $pg_tbl
					];
					App::update_template('../snippets/data_pg.php', $html_dir.'/data_pg'.$dsi.'.php', $vars);
					$lines[$ds['ln']] = '<script src="data_pg'.$dsi.'.php" data-jfn="'.$json_filename.'"></script>'."\n";
					$changes = $changes + 1;
					
				}else if($details['data_type'.$dsi] == 'gs'){
					
					$cache_seconds = TIME_MAP[$details['gs_cache_per'.$dsi]] * intval($details['gs_cache_val'.$dsi]);
					
					$proto = 'https://';
					$url = '';
					if(0 === strpos($details['gs_host'.$dsi], 'https://')){
						$url = substr($details['gs_host'.$dsi], 8);
					}else if(0 === strpos($details['gs_host'.$dsi], 'http://')){
						$proto = 'http://';
						$url = substr($details['gs_host'.$dsi], 7);
					}else{
						$url = $details['gs_host'.$dsi];	//only hostname, no proto
					}
					$gs_layer = $pg_tbl;
					
					$full_url = $proto.$details['gs_user'.$dsi].":".$details['gs_pwd'.$dsi].'@'.$url. "/geoserver/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=" . $details['gs_ws'.$dsi] . ":" . $gs_layer . "&maxFeatures=3000&outputFormat=application/json";
					
					$vars = ['MAP_ID_VALUE' => $newId, 'VARNAME' => $js_varname, 'CACHE_PERIOD_SECONDS' => $cache_seconds,
						'FULL_URL' => $full_url,
					];
					App::update_template('../snippets/data_gs.php', $html_dir.'/data_gs'.$dsi.'.php', $vars);
					$lines[$ds['ln']] = '<script src="data_gs'.$dsi.'.php" data-jfn="'.$json_filename.'"></script>'."\n";
					$changes = $changes + 1;
				}
			}
		}
		
		$sidebar_included = false;
		foreach($lys as $lyi => $ly) {
			
			if(	empty($details['layer_type'.$lyi]) ){	// if we have layer from form
				continue;
			}

			// update fds lines	
			if($details['layer_type'.$lyi] == 'wms'){
				
				// update url,ws,layer in WMS file
				$content = file_get_contents($html_dir.'/layer_wms'.$lyi.'.php');
				
				if(!empty($details['wms_user'.$lyi])){	// if WMS is secured
					$wms_url = $details['wms_url'.$lyi];
					$pos = strpos($wms_url, '://');
					$auth_url = substr($wms_url, 0, $pos).'://'.$details['wms_user'.$lyi].':'.$details['wms_pwd'.$lyi].'@'.substr($wms_url, $pos + 3);
					
					$vars = [ 'MAP_ID' => $newId, 'BASE_URL_VALUE' => $auth_url ];
					App::update_template('../snippets/proxy_wms.php', $html_dir.'/proxy_wms'.$lyi.'.php', $vars);
					
					$details['wms_url'.$lyi] = 'proxy_wms'.$lyi.'.php';
				}
				
				$replacement = ' = L.WMS.layer("'.$details['wms_url'.$lyi].'", "'.$details['wms_ws'.$lyi].':'.$details['wms_layer'.$lyi].'", {';
				$content = preg_replace('/ = L\.WMS\.layer\("(.*)", "(.*):(.*)",\s+{/', $replacement, $content);
				file_put_contents($html_dir.'/layer_wms'.$lyi.'.php', $content);

				$lines[$ly['ln']] = '<?php include("layer_wms'.$lyi.'.php"); ?>'."\n";
				$changes = $changes + 1;
			
			}else if($details['layer_type'.$lyi] == 'gs_geo'){
				
				$cache_seconds = TIME_MAP[$details['gs_geo_cache_per'.$lyi]] * intval($details['gs_geo_cache_val'.$lyi]);
				
				// add layer varname to show up in datatables
				array_push($js_varnames, $details['layer_varname'.$lyi].'_data');
				
				$proto = 'https://';
				$url = '';
				if(0 === strpos($details['gs_geo_host'.$lyi], 'https://')){
					$url = substr($details['gs_geo_host'.$lyi], 8);
				}else if(0 === strpos($details['gs_geo_host'.$lyi], 'http://')){
					$proto = 'http://';
					$url = substr($details['gs_geo_host'.$lyi], 7);
				}else{
					$url = $details['gs_geo_host'.$lyi];	//only hostname, no proto
				}
				
				$full_url = $proto.$details['gs_geo_user'.$lyi].":".$details['gs_geo_pwd'.$lyi].'@'.$url. "/geoserver/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=" . $details['gs_geo_ws'.$lyi] . ":" . $details['gs_geo_layer'.$lyi] . "&maxFeatures=3000&outputFormat=application/json";
				
				$vars = ['VARNAME' => $details['layer_varname'.$lyi], 'FULL_URL' => $full_url, 'CACHE_PERIOD_SECONDS' => $cache_seconds, 'MAP_ID_VALUE' => $newId,
					'STYLE_COLOR' 		 => $details['gs_geo_color'.$lyi],  		'STYLE_OPACITY' 		 => $details['gs_geo_opacity'.$lyi],
					'STYLE_FILL_COLOR' => $details['gs_geo_fill_color'.$lyi], 'STYLE_FILL_OPACITY' => $details['gs_geo_fill_opacity'.$lyi],
				];
				
				App::update_template('../snippets/layer_gs_geo.php', $html_dir.'/layer_gs_geo'.$lyi.'.php', $vars);
				
				$line = '';
				if(!$sidebar_included){
					$line = 'map.addControl(new sidebarControl());';
					$sidebar_included = true;	// include sidebar once
				}
				$line .= '<?php include("layer_gs_geo'.$lyi.'.php"); ?>'."\n";
				
				$lines[$ly['ln']] = $line;
				$changes = $changes + 1;
			}
		}
		
		if(isset($details['use_datatable']) != $use_dt){
			$changes = $changes + 1;
		}
		
		// update the file
		if($changes){
			$show_dt_updated = false;
			
			$fp = fopen($html_dir.'/index.php', 'w');
			foreach($lines as $line){
					
				if(!$show_dt_updated){
				 	if(preg_match('/const SHOW_DATATABLES = /', $line, $matches)){
						$line = 'const SHOW_DATATABLES = '.(isset($details['use_datatable']) ? 'true' : 'false').';'."\n";
					
					}else if(preg_match('/const JS_VARNAMES = array\(/', $line, $matches)){
						$line = 'const JS_VARNAMES = array("'.implode('","', $js_varnames).'");'."\n";
						$show_dt_updated = true;
					}
				}
				
				fwrite($fp, $line);
			}
			fclose($fp);
		}
	}
	
	public static function upload_dir($username){
		$ftp_home = shell_exec('grep "^'.$username.':" /etc/passwd | cut -f6 -d:');
		$upload_dir = substr($ftp_home, 0, -1);
		return $upload_dir;
	}
	
	public static function copy_r($source, $target){
		if ( is_dir( $source ) ) {
        @mkdir( $target );
        $d = dir( $source );
        while ( FALSE !== ( $entry = $d->read() ) ) {
            if ( $entry == '.' || $entry == '..' ) {
                continue;
            }
            $Entry = $source . '/' . $entry; 
            if ( is_dir( $Entry ) ) {
                App::copy_r( $Entry, $target . '/' . $entry );
            } else {
							copy( $Entry, $target . '/' . $entry );
						}
        }

        $d->close();
    }else {
        copy( $source, $target );
    }
	}
	
	public static function installApp($newId, $details, $html_dir, $data_dir, $apps_dir){
		
		// move html dir to apps
		App::copy_r($html_dir, $apps_dir.'/'.$newId);
		// work in new html dir
		$html_dir = $apps_dir.'/'.$newId;
		
		// index.html -> index.php
		rename($html_dir.'/index.html', $html_dir.'/index.php');
		
		$auth_lines = file_get_contents('../snippets/index_prefix.php');
		$auth_lines = str_replace('MAP_ID', $newId, $auth_lines);
		
		if(isset($details['use_datatable'])){
			$auth_lines = preg_replace('/const SHOW_DATATABLES = false;/', 'const SHOW_DATATABLES = true;', $auth_lines);
		}

		//insert our php auth code above <!doctype html> in index.php
		$content = file_get_contents($html_dir.'/index.php');
		file_put_contents($html_dir.'/index.php', $auth_lines . $content);
		
		// data directory outside of /var/www/html to /var/www/data
		rename($html_dir.'/data', $data_dir.'/'.$newId);
		
		// Replace sources to data files
		$lines = file($html_dir.'/index.php');
		
		$fp = fopen($html_dir.'/index.php', "w");

		$no_exec = '<?php if(empty(DB_HOST)){ die("Error: Can\'t execute!"); } ?>';
		$di = 0; $li = 0;
		
		for($i=0; $i < count($lines); $i++){
			$line = $lines[$i];
			
			if(preg_match('/src="data\/(.*)"/', $line, $matches)){
				$json_filename = $matches[1];
				
				$vars = [ 'MAP_ID_VALUE' => $newId,
					'DATA_FILE' => '../../../data/'.$newId.'/'. $json_filename ];
				App::update_template('../snippets/data_file.php', $html_dir.'/data_file'.$di.'.php', $vars);
				
				$line = '<script src="data_file'.$di.'.php" data-jfn="'.$json_filename.'"></script>'."\n";
				$di = $di + 1;
				
			}else if(preg_match('/var (.*) = L\.WMS\.layer\("(.*)", "(.*):(.*)",\s+{/', $line, $matches)){
				
				if(!empty($details['wms_user'.$li])){	// if WMS is secured
					$wms_url = $matches[2];
					$pos = strpos($wms_url, '://');
					$auth_url = substr($wms_url, 0, $pos).'://'.$details['wms_user'.$li].':'.$details['wms_pwd'.$li].'@'.substr($wms_url, $pos + 3);
					
					$vars = [ 'MAP_ID' => $newId, 'BASE_URL_VALUE' => $auth_url ];
					App::update_template('../snippets/proxy_wms.php', $html_dir.'/proxy_wms'.$li.'.php', $vars);
					
					$line = str_replace($wms_url, 'proxy_wms'.$li.'.php', $line);
				}
				
				$wms_content = $line;
				
				$lb = substr_count($line, '{');	//left brackets
				$rb = substr_count($line, '}');	//right brackets
				
				while($lb > $rb){
					$i++;
					$line = $lines[$i];
					$wms_content .= $line;
					$lb += substr_count($line, '{');	//left brackets
					$rb += substr_count($line, '}');	//right brackets
				}
				
				file_put_contents($html_dir.'/layer_wms'.$li.'.php', $no_exec."\n".$wms_content);
				$line = 'var '.$matches[1].' = <?php include("layer_wms'.$li.'.php"); ?>'."\n";
				$li = $li + 1;
				
			}else if(str_contains($line, 'src="js/leaflet.pattern.js"')){
				$line .= '<script src="../../assets/dist/js/sidebar_control.js"></script>'."\n";
				$line .= '<script src="../../assets/dist/js/leaflet.browser.print.min.js"></script>'. "\n";
			
			}else if(str_contains($line, 'map.attributionControl.setPrefix')){
				
				$line = file_get_contents('../snippets/datatables_control.php'). "\n".$line;
				$line = file_get_contents('../snippets/permalink_control.js'). "\n".$line;
							
			}else if(str_contains($line, '<body>')){
				$line .= file_get_contents('../snippets/index_header.php');
			
			}else if(str_contains($line, '</body>')){
				
				$line = file_get_contents('../snippets/datatables.php')."\n".$line;
				$line = file_get_contents('../snippets/permalink_modal.html')."\n".$line;
				
			}else if(str_contains($line, 'width:')){
				
				if(str_contains($lines[$i-1], '#map {')){
					$line = 'width: 100%;'."\n";
				}
			
			}else if(str_contains($line, 'var measureControl')){
				$line = '<?php if($loc) {?>map.flyTo([<?=$loc[1]?>, <?=$loc[2]?>], <?=$loc[0]?>);<?php } ?>'. "\n". $line;
				$line = file_get_contents('../snippets/print_control.js') . "\n" . $line;
			}

			fwrite($fp, $line);
		}
		fclose($fp);
	}
	
	public static function uninstallApp($id, $data_dir, $apps_dir){
		App::rrmdir($data_dir.'/'.$id);
		App::rrmdir($apps_dir.'/'.$id);
	}
	
	public static function getApps($apps_dir) {
		$rv = array();
    $entries = scandir($apps_dir);
		foreach($entries as $e){
			if(is_dir($apps_dir.'/'.$e) && !str_starts_with($e, '.')){
				array_push($rv, $e);
			}
		}
		return $rv;
  }
};
?>