/*
 * Copyright (C) 2009.
 *
 * 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.combiners;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.CommandArgs;
import uk.me.parabola.mkgmap.Version;


public class NsisBuilder implements Combiner {
	private String baseFilename;
	private String nsisFilename;
	private String licenseFilename;
	private String outputDir;
	private String familyName;
	private String id;
	private int productId;

	private final boolean hasIndex;
	private boolean hasTyp;

	private final List<String> mapList = new ArrayList<>();
	private String typName;

	public NsisBuilder(Map<String, Combiner> combinerMap) {
		this.hasIndex = combinerMap.containsKey("mdx");
	}

	public void init(CommandArgs args) {
		int familyId = args.get("family-id", CommandArgs.DEFAULT_FAMILYID);
		productId = args.get("product-id", 1);

		baseFilename = args.get("overview-mapname", "osmmap");
		familyName = args.get("family-name", "OSM map");

		String tmpId = Integer.toHexString(0x10000 | familyId);

		id = tmpId.substring(3, 5) + tmpId.substring(1, 3);

		nsisFilename = baseFilename + ".nsi";
		licenseFilename = baseFilename + "_license.txt";
		
		outputDir = args.getOutputDir();
	}

	public void onMapEnd(FileInfo info) {
		switch (info.getKind()) {
		case IMG_KIND:
			mapList.add(info.getMapname());
			break;
		case TYP_KIND:
			hasTyp = true;
			File typFile = new File(info.getFilename());
			typName = typFile.getName();
			break;
		default:
			break;
		}
	}

	public void onFinish() {
		writeNsisFile();
		writeLicenceFile();
	}

	private void writeNsisFile() {
		InputStream inStream;

		try {
			inStream = new FileInputStream("resources/installer_template.nsi");
		} catch (Exception ex) {
			inStream = this.getClass().getResourceAsStream("/installer/installer_template.nsi");
			if (inStream == null) {
				Logger.defaultLogger.error("Could not find the installer template.");
				return;
			}
		}

		try (BufferedReader br = new BufferedReader(new InputStreamReader(inStream))) {
			try (PrintWriter pw = new PrintWriter(new FileWriter(Utils.joinPath(outputDir, nsisFilename)))) {
				String strLine;
				while ((strLine = br.readLine()) != null)   {
					if (strLine.contains("INSERT_DEFINES_HERE"))
						writeDefines(pw);
					else if (strLine.contains("INSERT_REGBIN_HERE"))
						writeRegBin(pw);
					else if (strLine.contains("INSERT_ADDED_FILES_HERE"))
						writeAddedFiles(pw);
					else if (strLine.contains("INSERT_REMOVED_FILES_HERE"))
						writeRemovedFiles(pw);
					else
						pw.format(Locale.ROOT, "%s\n", strLine);
				}
			}

		} catch (IOException e) {
			Logger.defaultLogger.error("Could not write NSIS file");
		} finally {
			Utils.closeFile(inStream);
		}
	}
	
	private void writeDefines(PrintWriter pw) {
		pw.format(Locale.ROOT, "!define DEFAULT_DIR \"C:\\Garmin\\Maps\\%s\"\n", familyName);
		pw.format(Locale.ROOT, "!define INSTALLER_DESCRIPTION \"%s\"\n", familyName);
		pw.format(Locale.ROOT, "!define INSTALLER_NAME \"%s\"\n", familyName);
		pw.format(Locale.ROOT, "!define MAPNAME \"%s\"\n", baseFilename);
		pw.format(Locale.ROOT, "!define PRODUCT_ID \"%s\"\n", productId);
		pw.format(Locale.ROOT, "!define REG_KEY \"%s\"\n", familyName);
		if (hasIndex)
			pw.append("!define INDEX\n");
		if (hasTyp)
			pw.format(Locale.ROOT, "!define TYPNAME \"%s\"\n", typName);
	}

	private void writeRegBin(PrintWriter pw) {
		// Ideally we should have a define for the family value but NSIS won't allow "hexadecimal" variables
		pw.format(Locale.ROOT, "  WriteRegBin HKLM \"SOFTWARE\\Garmin\\MapSource\\Families\\${REG_KEY}\" \"ID\" %s\n", id);
}	
			
	private void writeAddedFiles(PrintWriter pw) {
		pw.append("  File \"${MAPNAME}.img\"\n");
		if (hasIndex) {
			pw.append("  File \"${MAPNAME}_mdr.img\"\n");
			pw.append("  File \"${MAPNAME}.mdx\"\n");
		}
		if (hasTyp)
			pw.format(Locale.ROOT, "  File \"%s\"\n", typName);

		pw.append("  File \"${MAPNAME}.tdb\"\n");
		for (String file : mapList)
			pw.format(Locale.ROOT, "  File \"%s.img\"\n", file);
	}

	private void writeRemovedFiles(PrintWriter pw) {
		pw.append("  Delete \"$INSTDIR\\${MAPNAME}.img\"\n");
		if (hasIndex) {
			pw.append("  Delete \"$INSTDIR\\${MAPNAME}_mdr.img\"\n");
			pw.append("  Delete \"$INSTDIR\\${MAPNAME}.mdx\"\n");
		}
		if (hasTyp)
			pw.format(Locale.ROOT, "  Delete \"$INSTDIR\\%s\"\n", typName);
		pw.append("  Delete \"$INSTDIR\\${MAPNAME}.tdb\"\n");
		for (String file : mapList) {
			pw.format(Locale.ROOT, "  Delete \"$INSTDIR\\%s.img\"\n", file);
		}
		pw.append("  Delete \"$INSTDIR\\Uninstall.exe\"\n");
	}


	/**
	 * We write out a license file that is included in the installer.
	 */
	private void writeLicenceFile() {
		InputStream inStream;
		try {
			inStream = new FileInputStream("resources/license_template.txt");
		} catch (Exception ex) {
			inStream = this.getClass().getResourceAsStream("/installer/license_template.txt");
			if (inStream == null) {
				Logger.defaultLogger.error("Could not find the license template.");
				return;
			}
		}

		try (BufferedReader br = new BufferedReader(new InputStreamReader(inStream))) {
			try (PrintWriter pw = new PrintWriter(new FileWriter(Utils.joinPath(outputDir, licenseFilename)))) {
				String strLine;
				while ((strLine = br.readLine()) != null)   {
					pw.format(Locale.ROOT, "%s\n", strLine);
				}

				pw.format(Locale.ROOT, "Map created with mkgmap-r%s\n", Version.VERSION);
			}

		} catch (IOException e) {
			Logger.defaultLogger.error("Could not write license file");
		} finally {
			Utils.closeFile(inStream);
		}
	}
}
