package cz.cuni.mff.d3s.jdeeco.network.omnet;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;

import cz.cuni.mff.d3s.deeco.simulation.omnet.OMNeTNative;
import cz.cuni.mff.d3s.jdeeco.position.Position;

public class OMNeTConfigGenerator {
	class Node {
		public final int id;
		public int ordinal;
		public final String ipAddress;
		public Position position;

		Node(int id, String ipAddress, Position position) {
			this.id = id;
			this.ipAddress = ipAddress;
			this.position = position;
		}
	}

	static final String DEFAULT_CONTENT = String.format("%s%somnetpp.ini", OMNeTNative.LIB_PATH, File.separator);
	static final double DEFAULT_802_15_4_TX_POWER_MW = 1.0;

	private final long limit;
	
	private double txPower802154 = DEFAULT_802_15_4_TX_POWER_MW;

	private List<Node> nodes = new LinkedList<>();

	public OMNeTConfigGenerator(long limit) {
		this.limit = limit;
	}
	
	public void set802154TxPower(double txpower_mw) {
		txPower802154 = txpower_mw;
	}

	public void addNode(Node node) {
		nodes.add(node);
	}

	public void addNode(int id, String ipAddress, Position position) {
		addNode(new Node(id, ipAddress, position));
	}

	public void addNode(OMNeTSimulation.OMNeTHost host) {
		// Determine node IP address
		String ip = null;
		if (host.infrastructureDevice != null) {
			ip = host.infrastructureDevice.address.ipAddress;
		}

		// Determine node position
		addNode(new Node(host.getId(), ip, host.getInitialPosition()));
	}

	public String getContent() throws IOException {
		StringBuilder content = new StringBuilder();

		// Load default configuration content
		try {
			content.append(new String(Files.readAllBytes(Paths.get(DEFAULT_CONTENT))));
		} catch (FileNotFoundException e) {
			throw new RuntimeException("Base OMNeT configuration file not found", e);
		}
		content.append(String.format("%n%n%n# CONTENT GENERATED BY %s %n%n%n", getClass().getName()));
		
		// Add 802.15.4 tx power
		content.append(String.format(Locale.US, "**.nic802154.mac.txPower = %fmW  # [mW]%n", txPower802154));

		// Add number of nodes
		content.append(String.format("**.numNodes = %d%n", nodes.size()));

		// Add time limit
		content.append(String.format(Locale.US, "sim-time-limit = %fs%n", (double) limit / 1000));

		// Add nodes
		int counter = 0;
		for (Node node : nodes) {
			node.ordinal = counter++;
			content.append(String.format("%n%n# Node %d definition%n", node.id));
			content.append(String.format("**.node[%d].mobility.initialX = %dm%n", node.ordinal, (int) node.position.x));
			content.append(String.format("**.node[%d].mobility.initialY = %dm%n", node.ordinal, (int) node.position.y));
			content.append(String.format("**.node[%d].mobility.initialZ = %dm%n", node.ordinal, (int) node.position.z));
			content.append(String.format("**.node[%d].appl.id = %d", node.ordinal, node.id));
		}

		// Add IP configuration
		content.append(String.format("%n%n%n# IP Static configuration%n"));
		content.append("*.configurator.config = xml(\"");
		content.append(String.format("<config>\\%n"));
		for (Node node : nodes) {
			if (node.ipAddress != null) {
				// Assign custom IP address
				content.append(String.format("\t<interface hosts='**.node[%d]' address='%s' netmask='255.x.x.x'/>\\%n",
						node.ordinal, node.ipAddress));
			} else {
				// Auto assign some IP address
				content.append(String.format("\t<interface hosts='**.node[%d]' address='10.x.x.x' netmask='255.x.x.x'/>\\%n",
						node.ordinal));
			}
		}
		content.append("</config>\")");

		return content.toString();
	}

	public File writeToOmnet() throws IOException {
		// Note: OMNeT finds its parts relative to configuration file
		File temp = new File(String.format("%s%somnentpp-%d.ini", OMNeTNative.LIB_PATH, File.separator,
				System.currentTimeMillis()));
		temp.deleteOnExit();

		FileWriter writer = new FileWriter(temp);
		writer.write(getContent());
		writer.close();

		return temp;
	}
}
