/*
 * Indoorhelper is a JOSM plug-in to support users when creating their own indoor maps.
 *  Copyright (C) 2016  Erik Gruschka
 *
 *  This program 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
 *  (at your option) any later version.
 *
 *  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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package model;

import java.util.ArrayList;
import java.util.List;
import javax.swing.JOptionPane;

import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.command.ChangePropertyCommand;
import org.openstreetmap.josm.data.osm.Filter;
import org.openstreetmap.josm.data.osm.Filter.FilterPreferenceEntry;
import org.openstreetmap.josm.data.osm.Tag;
import org.openstreetmap.josm.gui.dialogs.FilterDialog;
import org.openstreetmap.josm.gui.dialogs.FilterTableModel;

import model.TagCatalog.IndoorObject;

/**
 * Class for the data model which includes indoor data and
 * the functions to handle the plug-in
 * 
 * @author egru
 */

public class IndoorHelperModel{

	private java.util.List<IndoorLevel> levelList;
	private int workingLevel;
	private int workingIndex;
	private TagCatalog tags;
	private PresetCounter counter;
	
	/**
	 * Constructor for the {@link IndoorHelperModel} which sets the current
	 * workingLevel to 0 and creates the {@link TagCatalog}.
	 */
	public IndoorHelperModel() {
		this.workingLevel = 0;
		this.levelList = new ArrayList<>();
		this.tags = new TagCatalog();
		this.counter = new PresetCounter();
	}	
	
	/**
	 * Method to create a list of levels for the current building.
	 * It also creates the filters which are needed to execute the indoor mapping.
	 * minLevel should be lower than maxLevel or the same.
	 * 
	 * @param minLevel the lowest level of the building
	 * @param maxLevel the highest level of the building
	 * @return boolean which indicates if the creation of the levelList was successful
	 */
	public boolean setBuildingLevels(int minLevel, int maxLevel){
		
		if(minLevel < maxLevel){
			
			for(int i=minLevel; i<=maxLevel;i++){
				
				IndoorLevel level = new IndoorLevel(i);
				levelList.add(level);
				
				// Get the filter dialog
				FilterDialog filterDialog = Main.map.getToggleDialog(FilterDialog.class);
				
				if(filterDialog!=null){
					// Create a new filter
					//Filter filter = new Filter("\"indoor:level\"=\""+i+"\"", SearchMode.add, false, false, false);
					FilterPreferenceEntry entry = new FilterPreferenceEntry();
					entry.case_sensitive = false;
					entry.enable = false;
					entry.hiding = false;
					entry.inverted = false;
					entry.mapCSS_search = false;
					entry.mode = "add";
					entry.text = "\"indoor:level\"=\""+i+"\"";
					Filter filter = new Filter(entry);
					
					FilterTableModel filterTableModel = filterDialog.getFilterModel();
					
					boolean exists = false;
					
					// Search if the filter exists already.
					for(Filter listFilter : filterTableModel.getFilters()){
						if(listFilter.equals(filter)){
							exists = true;
						}
					}
					
					// Only add the filter if it is not already in the filter dialog.
					if(exists==false){
						filterTableModel.addFilter(filter);
					}
					
				}else{
					//Show error message if filter dialog is null.
					JOptionPane.showMessageDialog(null, "Filter Dialog is null.", "Error", JOptionPane.ERROR_MESSAGE);
				}
			}
			
			return true;
			
		} else if(minLevel==maxLevel){
			
			IndoorLevel level = new IndoorLevel(minLevel);
			levelList.add(level);
			
			// Get the filter dialog
			FilterDialog filterDialog = Main.map.getToggleDialog(FilterDialog.class);
			
			if(filterDialog!=null){
				// Create a new filter
				//Filter filter = new Filter("\"indoor:level\"=\""+minLevel+"\"", SearchMode.add, false, false, false);
				
				FilterPreferenceEntry entry = new FilterPreferenceEntry();
				entry.case_sensitive = false;
				entry.enable = false;
				entry.hiding = false;
				entry.inverted = false;
				entry.mapCSS_search = false;
				entry.mode = "add";
				entry.text = "\"indoor:level\"=\""+minLevel+"\"";
				Filter filter = new Filter(entry);
				
				FilterTableModel filterTableModel = filterDialog.getFilterModel();
				
				boolean exists = false;
				
				// Search if the filter exists already.
				for(Filter listFilter : filterTableModel.getFilters()){
					if(listFilter.equals(filter)){
						exists = true;
					}
				}
				
				// Only add the filter if it is not already in the filter dialog.
				if(exists==false){
					filterTableModel.addFilter(filter);
				}
			}else{
				JOptionPane.showMessageDialog(null, "Filter Dialog is null.", "Error", JOptionPane.ERROR_MESSAGE);
			}
			
			
			return true;
			
		}
		
		return false;
		
	}
	
	/**
	 * Getter for the levelList of the model.
	 * 
	 * @return the levelList, or null if no levelList was created yet
	 */
	public java.util.List<IndoorLevel> getLevelList(){
		return this.levelList;
	}
	
	/**
	 * Function to set the level the user wants to work on (with the level index) and activates the corresponding filter.
	 * 
	 * @param index the index of the level the user wants to work on
	 */
	public void setWorkingLevel(int index){
		this.workingIndex = index;
		this.workingLevel = this.getLevelNumberFromIndex(index);
		
		FilterDialog filterDialog = Main.map.getToggleDialog(FilterDialog.class);
		FilterTableModel filterTableModel = filterDialog.getFilterModel();
		
	
		for(Filter filter : filterTableModel.getFilters()){
			// disable the filter for the current level
			if(filter.text.equals("\"indoor:level\"=\""+workingLevel+"\"")){
				filterTableModel.setValueAt(false, filterTableModel.getFilters().indexOf(filter), FilterTableModel.COL_ENABLED);
				filterTableModel.setValueAt(false, filterTableModel.getFilters().indexOf(filter), FilterTableModel.COL_HIDING);
			} else if(filter.text.startsWith("\"indoor:level\"=\"")){
				filterTableModel.setValueAt(true, filterTableModel.getFilters().indexOf(filter), FilterTableModel.COL_ENABLED);
				filterTableModel.setValueAt(true, filterTableModel.getFilters().indexOf(filter), FilterTableModel.COL_HIDING);
			}
		}
	}
	
	/**
	 * Function to get the current working level of the plug-in
	 * 
	 * @return {@link Integer} which represents the current working level
	 */
	public int getWorkingLevel(){
		return this.workingLevel;
	}
	
	/**
	 * Method to get the index of the current working level of the plug-in.
	 * 
	 * @return {@link Integer} which represents the index
	 */
	public int getWorkingIndex(){
		return this.workingIndex;
	}
	
	/**
	 * Returns the level number which is corresponding to a specific index.
	 * 
	 * @param index index of the level
	 * @return a level number as an {@link Integer}
	 */
	public int getLevelNumberFromIndex(int index){
		return levelList.get(index).getLevelNumber();
	}
	
	/**
	 * Function to set the nameTag of a specific level.
	 * 
	 * @param levelNumber number of the level
	 * @param levelName tag which the user wants to set
	 * @return boolean which indicates if the level was found in the levelList
	 */
	public void setLevelName(int levelIndex, String levelName){
		if((levelName.length()>0) && (levelName != null)){
			levelList.get(levelIndex).setNameTag(levelName);
		}
	}
	
	/**
	 * Function to get a tag-set out of the {@link TagCatalog}.
	 * 
	 * @param object the {@link IndoorObject} from which you want to get the tag-set
	 * @return a {@link List} of {@link Tag}s
	 */
	public List<Tag> getObjectTags(TagCatalog.IndoorObject object){
		return this.tags.getTags(object);
	}
	
	
	/**
	 * Method which adds the selected tag-set to the currently selected OSM data.
	 * It also adds the level tag corresponding to the current working level.
	 * 
	 * @param object the object which defines the tag-set you want to add
	 * @param userTags the tags which are given by the user input
	 */
	public void addTagsToOSM(IndoorObject object, List<Tag> userTags){
		if(!Main.main.getCurrentDataSet().selectionEmpty() && !Main.main.getInProgressSelection().isEmpty()){
			
			List<Tag> tags = this.getObjectTags(object);
			tags.addAll(userTags);
			tags.add(new Tag("indoor:level", Integer.toString(workingLevel)));
			
			if(!this.getLevelList().get(workingIndex).hasEmptyName()){
				tags.add(this.getLevelList().get(workingIndex).getNameTag());
			}
			
			// Increment the counter for the presets
			this.counter.count(object);
			
			//Add the tags to the current selection
			for(Tag t : tags){
				Main.main.undoRedo.add(new ChangePropertyCommand(Main.main.getInProgressSelection(), t.getKey(), t.getValue()));
			}
			
		} else if(Main.main.getCurrentDataSet().selectionEmpty()){
			
			JOptionPane.showMessageDialog(null, "No data selected.", "Error", JOptionPane.ERROR_MESSAGE);
			
		}
		
	}
	
	/**
     * Method which adds the selected tag-set to the currently selected OSM data.
	 * It also adds the level tag corresponding to the current working level.
	 * 
	 * @param object the object which defines the tag-set you want to add
	 */
	public void addTagsToOSM(IndoorObject object){
		
		if(!Main.main.getCurrentDataSet().selectionEmpty() && !Main.main.getInProgressSelection().isEmpty()){
			List<Tag> tags = this.getObjectTags(object);
			tags.add(new Tag("indoor:level", Integer.toString(workingLevel)));
			
			// Increment the counter for the presets
			this.counter.count(object);
			
			//Add the tags to the current selection
			for(Tag t : tags){
				Main.main.undoRedo.add(new ChangePropertyCommand(Main.main.getInProgressSelection(), t.getKey(), t.getValue()));
			}
		} else if(Main.main.getCurrentDataSet().selectionEmpty()){
			JOptionPane.showMessageDialog(null, "No data selected.", "Error", JOptionPane.ERROR_MESSAGE);
		}
		
		
	}
	
	/**
	 * Returns the current ranking of the preset counter, which includes the 4 most used items.
	 * 
	 * @return a list of the 4 most used IndoorObjects
	 */
	public List<IndoorObject> getPresetRanking(){
		return counter.getRanking();
	}
	
	

}
