/*
 * Copyright (C) 2006 Steve Ratcliffe
 * 
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License 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.
 * 
 * 
 * Author: Steve Ratcliffe
 * Create date: 16-Dec-2006
 */
package uk.me.parabola.mkgmap.main;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;

import uk.me.parabola.imgfmt.FileExistsException;
import uk.me.parabola.imgfmt.FileNotWritableException;
import uk.me.parabola.imgfmt.FileSystemParam;
import uk.me.parabola.imgfmt.FormatException;
import uk.me.parabola.imgfmt.MapFailedException;
import uk.me.parabola.imgfmt.app.map.Map;
import uk.me.parabola.imgfmt.app.srt.Sort;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.CommandArgs;
import uk.me.parabola.mkgmap.build.MapBuilder;
import uk.me.parabola.mkgmap.combiners.OverviewBuilder;
import uk.me.parabola.mkgmap.general.LoadableMapDataSource;
import uk.me.parabola.mkgmap.reader.MapReader;

/**
 * Main routine for the command line map-making utility.
 *
 * @author Steve Ratcliffe
 */
public class MapMaker implements MapProcessor {
	private static final Logger log = Logger.getLogger(MapMaker.class);
	private Sort sort;
	private final boolean createOverviewFiles;

	public MapMaker(boolean createOverviewFiles) {
		this.createOverviewFiles = createOverviewFiles;
	}

	public String makeMap(CommandArgs args, String filename) {
		if (new File(filename).isDirectory()) {
			Logger.defaultLogger.error("Need a single file, not a directory: " + filename);
			return filename;
		}
		try {
			LoadableMapDataSource src = loadFromFile(args, filename);
			sort = args.getSort();
			if (createOverviewFiles){
				if (src.overviewMapLevels() != null){
					makeMap(args, src, OverviewBuilder.OVERVIEW_PREFIX);
				} else {
					String fname = OverviewBuilder.getOverviewImgName(args.getMapname());
					
					File f = new File(args.getOutputDir(), fname);
					tryRemove(f);
				}
			}
			return makeMap(args, src, "");
		} catch (FormatException e) {
			Logger.defaultLogger.error("Bad file format: " + filename, e);
			return filename;
		} catch (FileNotFoundException e) {
			Logger.defaultLogger.error("Could not open file: " + filename);
			return filename;
		}
	}

	private static void tryRemove(File f) {
		if (f.exists() && f.isFile()) {
			try {
				Files.delete(f.toPath());
				log.warn("removed " + f);
			} catch (IOException e) {
				log.warn("removing " + f + "failed with " + e.getMessage());
			}
		}
	}

	/**
	 * Make a map from the given map data source.
	 *
	 * @param args User supplied arguments.
	 * @param src The data source to load.
	 * @param mapNamePrefix prefix for output file (e.g. ovm_ for overview map component files)
	 * @return The output filename for the map.
	 */
	private String makeMap(CommandArgs args, LoadableMapDataSource src, String mapNamePrefix) {

		if (src.getBounds().isEmpty())
			return null;

		FileSystemParam params = new FileSystemParam();
		params.setBlockSize(args.getBlockSize());
		params.setMapDescription(args.getDescription());
		log.info("Started making", args.getMapname(), "(" + args.getDescription() + ")");
		try {
			Map map = Map.createMap(mapNamePrefix + args.getMapname(), args.getOutputDir(), params, args.getMapname(), sort, false);
			setOptions(map, args);

			MapBuilder builder = new MapBuilder(OverviewBuilder.OVERVIEW_PREFIX.equals(mapNamePrefix), false);
			builder.config(args.getProperties());
			builder.makeMap(map, src);

			// Collect information on map complete.
			String outName = map.getFilename();
			log.info("finished making map", outName, "closing");
			map.close();
			return outName;
		} catch (FileExistsException e) {
			Logger.defaultLogger.error(e.getMessage());
			throw new MapFailedException("File exists already", e);
		} catch (FileNotWritableException e) {
			Logger.defaultLogger.error(e.getMessage());
			throw new MapFailedException("Could not create or write to file", e);
		}
		catch (MapFailedException e) {
			Logger.defaultLogger.error(e.getMessage()); // make sure the filename is logged
			throw e;
		}
	}

	/**
	 * Set options from the command line.
	 *
	 * @param map The map to modify.
	 * @param args The command line arguments.
	 */
	private static void setOptions(Map map, CommandArgs args) {
		map.config(args.getProperties());

		String s = args.getCharset();
		if (s != null)
			map.setLabelCharset(s, args.isForceUpper());

		Sort sort = args.getSort();
		map.setSort(sort);
	}

	/**
	 * Load up from the file.  It is not necessary for the map reader to completely
	 * read the whole file in at once, it could pull in map-features as needed.
	 *
	 * @param args The user supplied parameters.
	 * @param name The filename or resource name to be read.
	 * @return A LoadableMapDataSource that will be used to construct the map.
	 * @throws FileNotFoundException For non existing files.
	 * @throws FormatException When the file format is not valid.
	 */
	private static LoadableMapDataSource loadFromFile(CommandArgs args, String name) throws FileNotFoundException {
		LoadableMapDataSource src = MapReader.createMapReader(name);
		src.config(args.getProperties());
		log.info("Started loading", name);
		src.load(name, !args.getProperties().getProperty("transparent", false));
		log.info("Finished loading", name);
		return src;
	}
}
