/*
 * 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.views.fiveg;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;

import de.tud.kom.p2psim.impl.scenario.DefaultConfigurator;
import de.tud.kom.p2psim.impl.topology.views.FiveGTopologyView;
import de.tudarmstadt.maki.simonstrator.api.Binder;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.component.GlobalComponent;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;

/**
 * This class allows for automated turning on and off of FiveG grid segments.
 * The Fluctuation events are read from a txt file.
 * @author Michal Lipinski
 * @version 1.0, 02.05.2017
 */
public class FiveGFluctuationGenerator implements EventHandler, GlobalComponent {
	

	private static final int _FLUC_EVENT = 2038;
	
	private final String commentsDelimiter = "#";

	private final String SEP = ";";
	
	ArrayList<Fluctuation> fluctuations;
	
	AbstractGridBasedTopologyDatabase gridDatabase = (AbstractGridBasedTopologyDatabase) Binder.getComponentOrNull(FiveGTopologyView.class)
			.getUMTSTopologyDatabase();
	

	@XMLConfigurableConstructor({ "file" })
	public FiveGFluctuationGenerator(String file) {
		
		File f = new File("config/dras/FiveGFlucFiles/" + file);
		
		if(f.exists() || !f.isDirectory()) {
			
			fluctuations = new ArrayList<Fluctuation>();
			
			
			parseFile(f);
			
			
			for(Fluctuation fluc : fluctuations) {
				Event.scheduleWithDelay(fluc.getTime(), this, fluc, _FLUC_EVENT);
			}
		}
		else System.err.println("Five G Fluctuation File not found");
		
		
	}
	
	/**
	 * parse the contents of a text file and create a list of Fluctuation events
	 * @param f
	 */
	public void parseFile(File f) {
		
		Scanner scanner = null;

		try {
			scanner = new Scanner(f);
			
			while(scanner.hasNextLine()) {
				
				String line = scanner.nextLine();
				
				if (line.length() == 0 || line.startsWith(commentsDelimiter))
					continue;

				if (line.indexOf(SEP) > -1) {
					
					String[] params = line.split(SEP);
					
					/*
					 * time ";" segmentIDs (separated by ",") ";" on/off
					 */
					if(params.length == 3) {
						
						//read the time
						long time = DefaultConfigurator.parseNumber(params[0].replaceAll("\\s+", ""), Long.class);
						
						//create a list of segment IDs
						String[] idStrings = params[1].split(",");
						
						ArrayList<Integer> ids = new ArrayList<Integer>();
						for(String s : idStrings) {
							ids.add(new Integer(Integer.parseInt(s.replaceAll("\\s+", ""))));
						}
						
						//read boolean on/off
						boolean turnOn = params[2].replaceAll("\\s+", "").equals("off")? false : true;
						
						fluctuations.add(new Fluctuation(time, ids, turnOn));
						
					}
					
					/*
					 * time  ";"  xCoordinates (separated by ",")  ";"  yCoordinates (separated by ",")  ";"  on/off
					 */
					if(params.length == 4) {
						
						//read the time
						long time = DefaultConfigurator.parseNumber(params[0].replaceAll("\\s+", ""), Long.class);
						
						//create a list of segment IDs from X and Y coordinates
						String[] xCoords = params[1].split(",");
						String[] yCoords = params[2].split(",");
						
						if(xCoords.length != yCoords.length) {
							System.err.println("Amount of specified X and Y coordinates in the "
									+ "FiveG Fluctuation file does not match");
							return;
						}
						
						ArrayList<Integer> ids = new ArrayList<Integer>();
						
						for(int i = 0; i < xCoords.length; i++) {
							
							int xCoord = Integer.parseInt(xCoords[i].replaceAll("\\s+", ""));
							int yCoord = Integer.parseInt(yCoords[i].replaceAll("\\s+", ""));
							
							int segID = gridDatabase.getSegmentID(xCoord, yCoord);
							
							ids.add(segID);
							
						}
						
						
						//read boolean on/off
						boolean turnOn = params[3].replaceAll("\\s+", "").equals("off")? false : true;
						
						fluctuations.add(new Fluctuation(time, ids, turnOn));
						
					}
					
				}
				
			}
			
			
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		finally {
			if(scanner != null) scanner.close();
		}
		
	}
	
	
	@Override
	public void eventOccurred(Object content, int type) {
		
		if(type == _FLUC_EVENT) {
			
			if(gridDatabase != null) {
			
				if(content instanceof Fluctuation) {
					Fluctuation f = (Fluctuation) content;
					
					for(int id : f.getIds()) {
						
						gridDatabase.getEntryFor(id, false).setAvailability(f.isTurnOn());
					}
					
					if(gridDatabase.getVis() != null) gridDatabase.getVis().setNeedsRedraw();
					
				}
			}
			else {
				System.err.println("FiveG database could not be initiated");
			}
		}	
		
	}
	
	private class Fluctuation {
		
		private ArrayList<Integer> ids;
		
		private boolean turnOn;
		
		private long time;
		
		
		public Fluctuation(long time, ArrayList<Integer> ids, boolean turnOn) {
			
			this.ids = ids;
			this.turnOn = turnOn;
			this.time = time;
			
			
		}
		
		public ArrayList<Integer> getIds() {
			return ids;
		}

		public boolean isTurnOn() {
			return turnOn;
		}

		public long getTime() {
			return time;
		}
	}

}
