/*
 * Copyright (c) 2005-2010 KOM � Multimedia Communications Lab
 *
 * This file is part of PeerfactSim.KOM.
 * 
 * PeerfactSim.KOM is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 * 
 * PeerfactSim.KOM 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.
 * 
 * You should have received a copy of the GNU General Public License
 * along with PeerfactSim.KOM.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package de.tud.kom.p2psim.impl.topology.movement;

import java.io.File;
import java.util.LinkedHashSet;
import java.util.Set;

import de.tud.kom.p2psim.api.topology.movement.MovementModel;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.mapvisualization.IMapVisualization;
import de.tud.kom.p2psim.impl.topology.movement.smarter.dataanalyzer.Analyzer;
import de.tud.kom.p2psim.impl.topology.movement.smarter.dataanalyzer.Statistics;
import de.tud.kom.p2psim.impl.topology.movement.smarter.host.SmarterHost;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location;

/**
 * Represents the movement model of the acquired SMARTER traces. By calling the class an analyzing procedure is started which analyzes the 
 * .sqlite database files and creates If files in case they do not exist already. 
 * Adds and places components to the simulation based on the created If files.
 * 
 * @author MarcelV
 * @version 1.0, 12.06.2018
 */
public class SmarterMovementModelNew implements MovementModel, EventHandler {
	protected IMapVisualization mapVisualization;
	
	private static final Location DEFAULT_LOCATION = new PositionVector(Double.NaN, Double.NaN);
	public static SmarterMovementModelNew movement;
	protected long timeBetweenMoveOperations;

	boolean dataAnalyzed;

	private Set<SimLocationActuator> actuatorList = new LinkedHashSet<SimLocationActuator>();

	/**
	 * Creates the movement model and checks if the databases should be analyzed
	 */
	public SmarterMovementModelNew() {
		movement = this;
		dataAnalyzed = false;
		
		// scheduling initalization!
		Event.scheduleImmediately(this, null, 42);
	
	}

	private void initialize() {
		// TODO get bool from xml to decide whether to analyze data or not
		if(!dataAnalyzed)
			analyzeData();
		
		if (mapVisualization != null) {
			VisualizationInjector.injectComponent(mapVisualization);
		}
	}
	
	/**
	 * Returns the instance of this class
	 * @return instance of this class
	 */
	public static SmarterMovementModelNew getInstance() {
		return movement;
	}

	/**
	 * Calculates statistics of the If files and stores them within a line for each host with the following format:
	 * id;distance;avgSpeed;idleTime;movingTime;phoneOutTime
	 */
	private void analyzeData() {
		// Check if If files have been created in previous simulations. If not, create them.
		if(!filesAlreadyExist()) {
//			System.out.println("FILES DO NOT EXIST, CREATE NEW FILES.");
			Analyzer analyzer = new Analyzer();
			analyzer.analyzeTraces();
		}else
//			System.out.println("FILES ALREADY EXIST, SKIP FILE CREATION STEP.");

		// Calculate statistics using the If files.
		Statistics.clearFile();
		for(int index = 1; index <= 125; index++) {
			String filePath = "smarter/traces-mv/mobilityTraceFile-File-Modified-" + index + ".if";
			Statistics stats = new Statistics(filePath);
			stats.calcStatistics();
		}
		dataAnalyzed = true;
	}
	
	/**
	 * Checks if all 94 If files have already been created.
	 * @return true if files have been created, else false
	 */
	private boolean filesAlreadyExist() {
		String folderName = "smarter/traces-mv/";
		File folder = new File(folderName);
		if (!folder.exists())
			folder.mkdir();

		int counter = 0;

		for(File file : folder.listFiles()) {
			String currentFile = file.getName();
			for(int index = 1; index <= 125; index++) {
				String tempFile = "mobilityTraceFile-File-Modified-" + index + ".if";
				if(currentFile.equals(tempFile))
					counter++;
			}
		}
		// Total correct files collected from the fieldtest is 93 (removed file 116 due to Staumuehle appearance)!
		if(counter == 93)
			return true;
		else
			return false;
	}
	
	/**
	 * Is fired each time an event occured. Updates the location of the host and calculates the next location for the next call of this method
	 */
	@Override
	public void eventOccurred(Object content, int type) {
		
		if(type == 42) {
			initialize();
		}
		else {
			SmarterHost host = (SmarterHost) content;

			Location location = host.getNextLocation();

			if (location != null) {
				host.getActuator().updateCurrentLocation(location);
			}

			if (host.tryNextStep()) {
				long sleepTime = host.getSleepTime();

				// Reschedule next step
				Event.scheduleWithDelay(sleepTime, this, host, 0);
			}
		}
		
	
	}
	
	
	/**
	 * Called once during simulation setup for each host. Creates a SmarterHost object at a default location.
	 */
	@Override
	public void addComponent(SimLocationActuator actuator) {
		actuator.updateCurrentLocation(DEFAULT_LOCATION);

		if (!actuatorList.contains(actuator)) {
			actuatorList.add(actuator);
			Event.scheduleImmediately(this, new SmarterHost(actuator, (int) actuator.getHost().getId().value()), 0);
		}
	}
	
	/**
	 * Called once during simulation setup for each host. Creates a SmarterHost object.
	 */
	@Override
	public void placeComponent(SimLocationActuator actuator) {
		if (!actuatorList.contains(actuator)) {
			actuatorList.add(actuator);
			Event.scheduleImmediately(this, new SmarterHost(actuator, (int) actuator.getHost().getId().value()), 0);
		}
	}

	/**
	 * Set the time between movement operations
	 * @param time the time between movement operations
	 */
	@Override
	public void setTimeBetweenMoveOperations(long time) {
		this.timeBetweenMoveOperations = time;
	}

	/**
	 * Returns the time between movement operations
	 * @return time between movement operations
	 */
	public long getTimeBetweenMoveOperations() {
		return timeBetweenMoveOperations;
	}
	
	public void setIMapVisualization(IMapVisualization mapVisualization) {
		this.mapVisualization = mapVisualization;
	}

	
}
