<?php
require_once('thread.php');
require_once('/home/lambertus/garmin/utils/matchit/matchit-new.php');
require_once('config.inc.php');
$start = microtime();
doIt();
echo "Total time for map update: ".((microtime()-$start)/1000)." seconds.\n";
function doIt() {
        global $cities;
        global $name;
        global $mapid;
        global $initial_nodes;
        global $script_dir;
        global $type;
        global $split_dir;
        global $max_nodes;        
        
        $web_dir = "/var/www/garmin/$name";
        $build_dir = "/home/lambertus/garmin/$name/build";
        $render_dir = $build_dir."/render";
        
        
        $threading = false; // Use threading method for rendering
        
        $date = strftime('%d-%m-%Y', time());
        echo "$date\n";
        if (!is_dir($build_dir)) {
                mkdir($build_dir);
        }
        chdir($build_dir);
        echo spaces(0)."Creating render dir\n";
        if (!is_dir($render_dir)) {
                mkdir($render_dir);
        }
        // Create a directory where rendering will take place and move the transliterated files there
        $files = getSourceIds($split_dir, "translit.$type");
        foreach($files as $file) {
                //echo spaces(0)."$file\n";
                if (file_exists("$split_dir/$file.translit.$type")) {
                        echo spaces(0)."Linking $split_dir/$file.translit.$type to render dir $render_dir/".($file+$mapid).".$type\n";
                        symlink("$split_dir/$file.translit.$type", "$render_dir/".($file+$mapid).".$type");
                }
        }
        copy($split_dir."/areas.list", "$render_dir/areas.list");
        copy($split_dir."/template.args", "$render_dir/template.args");
        copy($split_dir."/initial.kml", "$render_dir/initial.kml");
        // Set the working directory to the render directory
        chdir($render_dir);
        // Upgrade the initial split id range to the mapid range
        echo spaces(0)."Upgrade the tile id's in the initial KML\n";
        upgradeKml("initial.kml", "initial.kml", $mapid);
        
        // The real tile generating work starts here
        echo spaces(0)."Start rendering. Find files to process\n";
        $maxid = getMaxSourceId($type);
        $files = getSourceIds("",$type);
        if ($threading) {
                // Render multiple tiles in parallel
                // Note: this is not functional yet!
                $maxThreads = 2;
                
                // Wait for all the threads to finish
                $threads = array();
                while( !empty( $files ) || !empty($threads) ) {
                        // Check thread statusses
                        foreach( $threads as $index => $threadinfo) {
                                $thread = $threadinfo["thread"];
                                if( ! $thread->isAlive() ) {
                                        echo "Thread $index is done\n";
                                        unset( $threads[$index] );
                                        $threads = array_values($threads);
                                }
                        }
                        // Check if there is room to start a new thread
                        if (count($threads) < $maxThreads && !empty($files)) {
                                echo "**\n**Starting thread with file ".$files[0]." and maxid $maxid**\n**\n";
                                // Start the render thread
                                $thread = new Thread("render");
                                $thread->start($render_dir, $files[0], $maxid, $initial_nodes, 0);
                                $threads[] = array("thread" => $thread, "dir" => $thread_dir);
                                $maxid+=100;        // Assume maximum of 100 splitted tiles per initial tile
                                
                                // Remove this tile from the todo list
                                unset($files[0]);
                                $files = array_values($files);
                        }
                        // let the CPU do its work
                        sleep( 1 );
                        die();
                }
        } else {
                // Use non-threaded recursive method for rendering
                $maxid = getMaxSourceId($type);
                $files = getSourceIds("",$type);
                foreach($files as $file) {
                        echo spaces(0)."Render top level file $file\n";
                        $maxid = render($render_dir, $file, $maxid, $initial_nodes, 0, false);
                }
        }
        
        // Delete any file that has 0 bytes length (has to be done before the updateKml function)
        echo spaces(0)."Cleanup after rendering\n";
        $files = getSourceIds("","img");
        foreach($files as $file) {
                if (filesize("$file.img") == 0) {
                        echo spaces(0)."Deleting file $file.img because it has 0 bytes length\n";
                        unlink("$file.img");
                }
        }
        // Update the initial KML split result file that lists all the tiles with the actual tiles produced by the recursive process
        echo spaces(0)."Update the KML file\n";
        updateKml("initial.kml", "world.kml");
        // Create a list of countries and their associated tiles
        echo spaces(0)."Match the countries with their associated tiles\n";
        // Matchit is a custom made script to match country bboxes to tile bboxes.
        $matchit = new Matchit($date, "/home/lambertus/garmin/utils/matchit/countries", $render_dir, "world.kml");
        $matchit->Run();
        // Move the results to the final directory
        // Create the web directory where the tiles are stored
        echo spaces(0)."Create the web dir: $web_dir/$date\n";
        if (!is_dir("$web_dir")) {
                mkdir("$web_dir");
        }
        if (!is_dir("$web_dir/$date")) {
                mkdir("$web_dir/$date");
        }
        // Move all files to the new directory
        echo spaces(0)."Move the images to the webdir\n";
        exec("mv *.img $web_dir/$date");
        exec("mv world.kml $web_dir/$date");
        exec("cp countries.js $web_dir/$date");
        exec("cp countries.xml $web_dir/$date");
        echo "Distribute the version to the other server(s)\n";
        exec("$script_dir/distribute.sh $date");
        echo spaces(0)."Finalize by writing a new version\n";
        file_put_contents("$web_dir/version", $date);
        echo spaces(0)."Done!\n";
}
// Recursive functions that splits and renders until success (or no solution)
function render($thread_dir, $sourceid, $maxid, $nodes, $level, $noSubsplit) {
        global $script_dir;
        global $family_id;
        global $product_id;
        global $name;
        global $style_dir;
        global $style;
        global $bounds;
        global $description;
        global $max_size;
        global $type;
        global $mkgmapOptions;
        
        $mkgmap = "/home/lambertus/garmin/utils/mkgmap/mkgmap.jar";
        
        chdir($thread_dir);
        
        //echo "Styles: $style_dir, style: $style\n";
        //$mapid = $maxid+1;
        $file = "$sourceid.$type";
        $name = "$sourceid.img";
        
        $command = "ulimit -t 900 && java -Xmx1792M -ea -jar $mkgmap";
        $command.= " --family-id=$family_id";
        $command.= " --product-id=$product_id";
        $command.= " --draw-priority=20";
        $command.= " --description='$description'";
        $command.= " --series-name='$description'";
        $command.= " --style-file='$style_dir'";
        $command.= " --style='$style'";
        $command.= " --bounds='$bounds'";
        foreach ($mkgmapOptions as $option) {
                $command.=$option;
        }
        $command.= " --input-file=$file";
        echo "$command\n";
        $output = passthru($command, $retval);
        $exists =file_exists($name);
        $size = filesize($name);
        
        if ($noSubsplit == true) {
                if ($size == 0) {
                        echo spaces($level)." Failed render attempt $name, but cannot be split further\n";
                        if (file_exists($name)) {
                                unlink($name);
                        }
                } else {
                        echo spaces($level)." Just don't subsplit $name further\n";
                }
        } else {
                if ($retval == 1 || !$exists || $size == 0 || $size>$max_size) {
                        echo spaces($level)."Processing file $file failed (result=$retval, exists=$exists, size=$size)\n";
                
                                echo spaces($level)." Removing $name\n";
                                if (file_exists($name)) {
                                        unlink($name);
                        }
                        $level++;
                        $maxid = subsplit($thread_dir, $sourceid, $maxid, $nodes, $level);
                }
        }
        return $maxid;
}
// Split an OSM file
function subsplit($thread_dir, $tileid, $maxid, $nodes, $level) {
        global $cities;
        global $type;
        $mapid = $maxid+1;
        $newtileid = $tileid;
        if ($nodes >= 10000) {
                echo spaces($level)."Acquire the split lock\n";
                $fp = fopen("splitter.lock", "w+");
                flock($fp, LOCK_EX);
                
                echo spaces($level)."Splitting $tileid with nodes=$nodes, starting at $mapid. Max id was $maxid, level = $level\n";
                $output = "xml";
                if (strcmp($type, "o5m") == 0) {
                        $output = "o5m";
                }
                $command = "java -Xmx2048M -jar ~/garmin/utils/splitter/splitter.jar --output=$output --keep-complete=true --mapid=".$mapid." --max-nodes=".$nodes." --write-kml=".$tileid.".kml --no-trim=true --geonames-file=$cities $tileid.$type";
                //echo spaces($level).$command."\n";
                //passthru($command);
                $result = array();
                exec($command, $result);
                
                $newtileid = $maxid;
                // Determine the maximum id produced by the split
                $newmaxid = getMaxSourceId($type);
                
                // Release the split lock
                flock($fp, LOCK_UN);
                fclose($fp);
                
                // Find "nodes but can't be split further" in Splitter's output
                $bCannotSplit = false;
                foreach ($result as $line) {
                        if (strpos($line, "nodes but can't be split further") > 0){
                                echo spaces($level)." Found the text 'nodes but cant be split further' -> giving up subsplitting\n";
                                $bCannotSplit = true;
                                break;
                        }
                }
                
                // Check the result
                if (($newmaxid - $maxid) < 2 && $bCannotSplit == false) {                // require at least two sub files
                        echo spaces($level)."The split did not result in at least two subtiles, try again with less nodes per tile\n";
                        $level++;
                        if ($nodes > 100000) {
                                $nodes = $nodes - 100000;
                        } else {
                                $nodes = $nodes - 10000;
                        }
                        $newmaxid = subsplit($thread_dir, $tileid, $maxid, $nodes, $level);
                } else {
                        // Render the new source files
                        $maxid++;
                        $tiles = $newmaxid - $maxid;
                        for ($i = 0; $i <= $tiles; $i++) {
                                echo spaces($level)."Start render with $maxid, $newmaxid, $level, $nodes\n";
                                $newmaxid = render($thread_dir, $maxid, $newmaxid, $nodes, $level, $bCannotSplit);
                                $maxid++;
                        }
                        echo spaces($level)."Done with subsplit at level $level\n";
                }
        } else {
                echo spaces($level)."Giving up....\n";
                $newmaxid = $maxid;
                
                // Remove the last Splitter result XML file
                unlink("$tileid.kml");
        }        
        return $newmaxid;
}
function getMaxSourceId($type) {
        $maxid = 0;
        $ids = getSourceIds("",$type);
        foreach ($ids as $id) {
                if ($id > $maxid)
                        $maxid = $id;
        }
        return $maxid;
}
function getSourceIds($path = "", $extention = "osm.gz") {
        $ids = array();
        sleep(1);
        if (strlen($path) > 0 && substr($path, -1) != "/") $path.="/";
        echo "getSourceIds(): searching for $path*.$extention\n";
        foreach (glob("$path*.$extention") as $filename) {
                $parts = explode(".", $filename);
                $paths = explode("/", $parts[0]);
                $ids[] = $paths[count($paths)-1];
        }
        return $ids;
}
function spaces($level) {
        $ret="";
        for ($i = 0; $i < $level; $i++) {
                $ret .= " ";
        }
        $ret .= date('H:i:s')." ";
        return $ret;
}
function simplexml_append(SimpleXMLElement $parent, SimpleXMLElement $new_child){
$node1 = dom_import_simplexml($parent);
$dom_sxe = dom_import_simplexml($new_child);
$node2 = $node1->ownerDocument->importNode($dom_sxe, true);
$node1->appendChild($node2);
}
function simplexml_replace(SimpleXMLElement $parent, SimpleXMLElement $new_child){
$node1 = dom_import_simplexml($parent);
$dom_sxe = dom_import_simplexml($new_child);
$node2 = $node1->ownerDocument->importNode($dom_sxe, true);
$node1->parentNode->replaceChild($node2,$node1);
}
// The input KML is assumed to use tile id 1 to [n] while we want to work with mapid to mapid+[n]
// so the map id is added to the input id and written to the output
function upgradeKml($input, $output, $mapid) {
        // Load the original KML file
        if (file_exists($input)) {
                $xml = simplexml_load_file($input);
        } else {
                exit("Failed to open $input.");
        }
        
        foreach ($xml->Document->Placemark as $tile) {
                $id = (int)$tile->name+$mapid;
                $tile->name = $id;
        }
        
        // Write out the new version of the KML file
        $fh = fopen($output, "w+");
        if ($fh) {
                fwrite($fh, $xml->asXML());
                fclose($fh);
        }
}
function updateKml($input, $output) {
        $kmlRed = "
<Style id='Red'>
<LineStyle>
<color>ffff0000</color>
<width>1.5</width>
</LineStyle>
<PolyStyle>
<color>7d0000ff</color>
</PolyStyle>
</Style>";
        $kmlBlue = "
<Style id='Blue'>
<LineStyle>
<color>ffff0000</color>
<width>1.5</width>
</LineStyle>
<PolyStyle>
<color>00007dff</color>
</PolyStyle>
</Style>";
        // Load the original KML file
        if (file_exists($input)) {
                $xml = simplexml_load_file($input);
        } else {
                exit("Failed to open $input.");
        }
        
        // loop through all KML files to update the original KML file
        foreach (glob("*.kml") as $replacement) {
                //if (strpos($replacement, $name) === false) {
                        $id = explode(".", $replacement);
                
                        // Load each new KML file
                        $newxml = simplexml_load_file($replacement);
                        // Loop through the original KML file until the replacement ID is found
                        foreach ($xml->Document->Placemark as $oldPlacemark) {
                                //echo "split result id = ".$id[0].", world.kml compare: ".$oldPlacemark->name."\n";
                                if (strpos($oldPlacemark->name, $id[0]) !== false) {
                                        // This KML file contains subtiles -> delete the tile they replace
                                        echo "removing initial split result ".$oldPlacemark->name."\n";
                                        // Remove the placemark from the original KML file
                                        //unset($oldPlacemark);        // This should also work to replace the two lines below
                                        $oNode = dom_import_simplexml($oldPlacemark);
                                        $oNode->parentNode->removeChild($oNode);
                                        
                                        // Add the replacement placemarks
                                        foreach ($newxml->Document->Placemark as $newPlacemark) {
                                                echo "adding ".$newPlacemark->name." for ".$id[0]."\n";
                                                simplexml_append($xml->Document, $newPlacemark);
                                        }
                                        break;
                                }
                        }
                
                        
                //}
        }
        // Determine which tiles actually exist (update KML rendering)
        $red = simplexml_load_string($kmlRed);
        $blue = simplexml_load_string($kmlBlue);
        simplexml_replace($xml->Document->Style, $red);
        simplexml_append($xml->Document, $blue);
        
        foreach ($xml->Document->Placemark as $tile) {
                $name = $tile->name.".img";
                if (file_exists($name) == true) {
                        $tile->styleUrl = "#Blue";
                } else {
                        $tile->styleUrl = "#Red";
                }
                // Add filesize
                $size = filesize($name);
                $tile->addChild("size", $size);
        }
        // Write out the new version of the KML file
        $fh = fopen($output, "w+");
        if ($fh) {
                fwrite($fh, $xml->asXML());
                fclose($fh);
        }
}
// ensure $dir ends with a slash
function delTree($dir) {
$files = glob( $dir . '*', GLOB_MARK );
foreach( $files as $file ){
if( substr( $file, -1 ) == '/' )
delTree( $file );
else
unlink( $file );
}
rmdir( $dir );
}
?>