/*
 * Copyright (C) 2006, 2011.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 or
 * version 2 as published by the Free Software Foundation.
 *
 * 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.
 */
package uk.me.parabola.mkgmap.reader.osm.boundary;

import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.mkgmap.reader.osm.Tags;
import uk.me.parabola.mkgmap.reader.osm.Way;
import uk.me.parabola.util.GpxCreator;
import uk.me.parabola.util.Java2DConverter;

public class BoundaryFile2Gpx {
	private final String boundaryFileName;
	private final BoundaryQuadTree bqt; 
	public BoundaryFile2Gpx(String boundaryDirName, String boundaryFileName) {
		this.boundaryFileName = boundaryFileName;
		bqt = BoundaryUtil.loadQuadTree(boundaryDirName, boundaryFileName);
	}

	/**
	 * Create gpx files for areas which contain no information for a given
	 * admin level.  
	 * @param admLevel reasonable values are 2..11
	 */
	public void saveEmptyAreas(int admLevel) {
		Area tileArea = Java2DConverter.createBoundsArea(BoundaryUtil
				.getBbox(boundaryFileName));

		Area coveredArea = bqt.getCoveredArea(admLevel);
		tileArea.subtract(coveredArea);
		String levelId = "admin_level" ;
		if (admLevel < 12)
			levelId += admLevel;
		else
			levelId += "notset";
		if (tileArea.isEmpty()) {
			System.out.println(levelId + " is covered completely.");
		} else {
			String gpxBasename = "gpx/" + boundaryFileName
					+ "/uncovered/" + levelId + "/";
			

			List<List<Coord>> emptyPolys = Java2DConverter
					.areaToShapes(tileArea);
			Collections.reverse(emptyPolys);

			int i = 0;
			for (List<Coord> emptyPart : emptyPolys) {
				String attr = Way.clockwise(emptyPart) ? "o" : "i";
				GpxCreator.createGpx(gpxBasename + i + "_" + attr, emptyPart);
				i++;
			}
		}
	}

	/**
	 * create gpx files for all boundaries contained in one *.bnd file
	 */
	public void saveAsGpx() {
		System.out.println("Start converting " + boundaryFileName);

		Map<String, Tags> bTags = bqt.getTagsMap();
		Map<String, List<Area>> areas = bqt.getAreas();
		
		// verify data: remove boundary ids that have no related areas 
		Iterator<Entry<String, Tags>> tagIter = bTags.entrySet().iterator();
		while (tagIter.hasNext()) {
			Entry<String, Tags> entry = tagIter.next();
			List<Area> aList = areas.get(entry.getKey());
			if (aList == null || aList.isEmpty()){
				System.err.println("no area info for "+ entry.getKey());
				tagIter.remove();
				continue;
			}
		}
		
		for (int adminlevel = 2; adminlevel < 12; adminlevel++) {
			boolean found = false;
			for (Entry<String, Tags> entry: bTags.entrySet()){
				String admLevel = entry.getValue().get("admin_level");
				if (admLevel != null && admLevel.equals(String.valueOf(adminlevel))){
					found = true;
					break;
				}
			}
			if (!found) {
				System.out.println("No boundary with admin_level=" + adminlevel
						+ " found.");
				continue;
			}
			
			for (Entry<String, Tags> entry: bTags.entrySet()){
				// get the admin_level tag
				String admLevel = entry.getValue().get("admin_level");
				if (admLevel == null) {
					admLevel = "notset";
				}

				String bId = entry.getKey();

				String gpxBasename = "gpx/" + boundaryFileName
						+ "/covered/admin_level=" + admLevel + "/" + admLevel + "_" + bId
						+ "_";

				Path2D.Double path = new Path2D.Double();
				List<Area> aList = areas.get(bId);
				for (Area area : aList){
					path.append(area, false);
				}
				int i = 0;
				List<BoundaryElement> bElements = BoundaryUtil.splitToElements(new Area(path),bId);
				for (BoundaryElement be : bElements){
					String gpxFile = gpxBasename;
					if (be.isOuter()) {
						gpxFile += "o_" + i;
					} else {
						gpxFile = "i_" + i;
					}
					GpxCreator.createGpx(gpxFile, be.getPoints());
					i++;
				}
			}
			saveEmptyAreas(adminlevel);
		}
		System.out.println("Finished " + boundaryFileName);

	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		String boundaryDirName = args[0];
		System.out.println(boundaryDirName);
		File boundaryDir = new File(boundaryDirName);
		List<String> boundaryFileNames;
		if (boundaryDir.isFile() && boundaryDir.getName().endsWith(".bnd")) {
			boundaryDirName = boundaryDir.getParent();
			if (boundaryDirName == null)
				boundaryDirName = ".";
			boundaryFileNames = new ArrayList<>();
			boundaryFileNames.add(boundaryDir.getName());
		} else {
			boundaryFileNames = BoundaryUtil.getBoundaryDirContent(boundaryDirName);
		}

		for (String boundaryFileName : boundaryFileNames) {
			BoundaryFile2Gpx converter = new BoundaryFile2Gpx(boundaryDirName,boundaryFileName);
			converter.saveAsGpx();
		}

	}

}
