package traffic;

import jason.environment.grid.GridWorldModel;
import jason.environment.grid.Location;
import java.util.logging.Logger;
import java.util.List;
import java.util.ArrayList;

/** 
 * class WorldModel: view code for project jasonTeamSimLocal.mas2j
 *
 * @author Fco. Javier Cordero Cadenas
 */

public class WorldModel extends GridWorldModel {

	public static final int   CITY = 16;									//indentifies a City for the grid (not used at the moment)
	public static final int   VILLAGE = 32;									//indentifies a Village for the grid (not used at the moment)

	Location city;										 		  			//location of the city
	private List<Village> villageList = new ArrayList<Village>();			//village list
	private ArrayList<Habitant> habitantList = new ArrayList<Habitant>();	//List of all habitants
	private ArrayList<Vehicle> vehicleList = new ArrayList<Vehicle>(); 		//List of all vehicles
	private int totalHab;													//Total habs of all village
	private int collectDay = 1;												//collect day
	private Float priceXLitreGas = 1.0f;									//price per gas litre
	private Float kgCO2XLitreGas = 1.0f;									//kilograms of CO2 emitted per gas litre
	private Float lowerLevelLearningFactor;									//lower level of learning factor
	private Float higherLevelLearningFactor;								//higher level of learning factor 
	private Float lowerLevelForgettingFactor;								//lower level of forgetting factor
	private Float higherLevelForgettingFactor;								//higher level of forgetting factor
	private Float roadLength;												//length of the road
	private Float roadSpeed;												//maximum speed allowed on the road
	private Float roadFlow;													//flow of vehicles on the road
	private Float toll;														//toll to enter city

    private Logger logger = Logger.getLogger("jasonTeamSimLocal.mas2j." + WorldModel.class.getName()); //logger
    
	Configuration conf = new Configuration("src/java/traffic/configuration.xml"); 	//configuration file
	
	/**
	 * constructor
	 *
	 * @param numHabs	number of habitants of the world
	 * @param habsXGroup	maximum number of members that form a group
	 */
    public WorldModel(int numHabs, int habsXGroup){
		super(100, 100, numHabs);
		int numGroups, numAg;

        initConfiguration();
        float scale = (float)totalHab / numHabs;
		System.out.println( " Total Habs: " + totalHab + " Scale: " + scale);

        setCity(47, 48, 9, 7);  //(posX, posY, width, height)
		
		Habitant hab;
        for (Village v : villageList)
        {
            numAg = (int)((float)v.getVillageNumHabs() / scale);
            numGroups = (numAg / habsXGroup);
            if (numAg % habsXGroup != 0) {numGroups++;}
            
            // Add groups to the village
            for (int j=0; j<numGroups; j++){
                v.addGroup(new Group(j));
            }
            
            // Add habs to the village
            for(int i=0; i<numAg; i++) {
                hab = addHab(v.getVillageId());
                v.getVillageGroup(i / habsXGroup ).addHabitant(hab);
            }
            
            v.show();
        }

    }
	
	/**
	 * setters and getters
	 */
	public int getCollectDay(){
		return collectDay;
	}
	
	public Float getPriceXLitreGas(){
		return priceXLitreGas;
	}
	
	public Float getKgCO2XLitreGas(){
		return kgCO2XLitreGas;
	}
	
	public Float getLowerLevelLearningFactor(){
		return lowerLevelLearningFactor;
	}
	
	public Float getHigherLevelLearningFactor(){
		return higherLevelLearningFactor;
	}
	
	public Float getLowerLevelForgettingFactor(){
		return lowerLevelForgettingFactor;
	}
	
	public Float getHigherLevelForgettingFactor(){
		return higherLevelForgettingFactor;
	}
	
	public Float getRoadLength(){
		return roadLength;
	}
	
	public Float getRoadSpeed(){
		return roadSpeed;
	}
	
	public Float getRoadFlow(){
		return roadFlow;
	}
	
	public Float getToll(){
		return toll;
	}
	
	public void setPriceXLitreGas(Float price){
		priceXLitreGas = price;
	}
	
	public void setKgCO2XLitreGas(Float kg){
		kgCO2XLitreGas = kg;
	}
	
	public void setLowerLevelLearningFactor(Float lowLearn){
		lowerLevelLearningFactor = lowLearn;
	}
	
	public void setHigherLevelLearningFactor(Float highLearn){
		higherLevelLearningFactor = highLearn;
	}
	
	public void setLowerLevelForgettingFactor(Float lowForget){
		lowerLevelForgettingFactor = lowForget;
	}
	
	public void setHigherLevelForgettingFactor(Float highForget){
		higherLevelForgettingFactor = highForget;
	}
	
	public void setRoadLenght(Float length){
		roadLength = length;
	}
	
	public void setRoadSpeed(Float speed){
		roadSpeed = speed;
	}
	
	public void setRoadFlow(Float flow){
		roadFlow = flow;
	}
	
	public void setToll(Float t){
		toll = t;
	}

	public List<Village> getVillageList() {
		return villageList;
	}

	/**
	 * get the city location
	 */
	public Location getCityPos(){
		return city;
	}
	
	/**
	 * get a village
	 *
	 * @param idVil	village identifier
	 */
	public Village getVillage(int idVil){
		for (Village v : villageList){
			if (idVil == v.getVillageId()){
				return v;
			}
		}
		return null;
	}
		
	/**
	 * establish the area of the city by a rectangle whose upper left corner is at (x, y) and with a height and width.
	 *
	 * @param x	coordinate X of the upper left corner of the city
	 * @param y	coordinate Y of the upper left corner of the city
	 * @param width	widht of the city
	 * @param height	height of the city
	 */
	public void setCity(int x, int y, int width, int height){
		city = new Location(x, y);
		for (int j=x; j<x+width; j++){
			for (int k=y; k<y+height; k++){
				add(WorldModel.CITY, j, k);
			}
		}
	}
	
	/**
	 * add an habitant to a village
	 +
	 * @param idVil	village identifier
	 */
	public Habitant addHab(int idVil)
	{	
		Habitant habitant = null;
		String startAt = "09:15";
		Village village = getVillage(idVil);
		if (village!=null){	
			
			habitant = village.newHabitant(startAt, habitantList.size() );
			habitantList.add(habitant);

			//setAgPos(id, posX, posY);
			
			//add the vehicles to the habitant
			Vehicle v = newVehicle(VehicleType.car, habitant.getHabitantId() );
			habitant.addVehicle(v);
			village.newVehicle(v);
			v.setConsumption(12.0f);
			v.setConsumptionStop(6.0f);
			
		}
		return habitant;
	}
	
	/**
	 * Create a new vehicle 
	 *
	 * @return Vehicle added
	 */
	public Vehicle newVehicle(VehicleType type, int owner){
		Vehicle v;
		int seats;
		float vel;
		
		switch(type) {
			case car:
				seats = 4;
				vel = 120.0f;
				break;
			case moto:
				seats = 2;
				vel = 120.0f;
				break;
			case bus:
				seats = 40;
				vel = 80.0f;
				break;
			case train:
				seats = 1000;
				vel = 80.0f;
				break;
			case bike:
				seats = 1;
				vel = 10.0f;
				break;
			default:
				seats = 1;
				vel = 10.0f;
		}
		// Se deber�a quitar el +1 ??
		v = new Vehicle(type.toString() + (vehicleList.size() + 1), type, seats, vel, owner);
		vehicleList.add(v);
		return v;
	}

	/**
	 * get an habitant from the habitant list
	 *
	 * @param id	identifier of the required habitant
	 *
	 * @return Habitant if it exists, else return null 
	 */
	public Habitant getHabitant(int id){

		if(id >= 0 && id < habitantList.size() )
		{
			// assert id == habitantList.get(id).getHabitantID();
			return (habitantList.get(id) );
		}
		else
		{
			System.out.println(" Err: " + id + " - " + habitantList.size() );
			return null;
		}
	}
	
	/**
	 * get a vehicle from the vehicle list
	 *
	 * @param id	identifier of the required vehicle
	 *
	 * @return vehicle if it exists, else return null 
	 */
	public Vehicle getVehicle(int id){

		if(id >= 0 && id < vehicleList.size() )
		{
			return (vehicleList.get(id) );
		}
		else
		{
			System.out.println(" Err: " + id + " - " + vehicleList.size() );
			return null;
		}
	}

	/**
	 * get a vehicle from the vehicle list
	 *
	 * @param id	identifier of the required vehicle
	 *
	 * @return vehicle if it exists, else return null 
	 */
	public Vehicle getVehicle(String strId){
		Integer id = null;
		String type;

		for(VehicleType vType: VehicleType.values() ) {
			type = vType.toString();
			if(strId.startsWith(type) ) {
				id = Integer.valueOf(strId.substring(type.length() ) );
				break;
			}
		}
		return getVehicle(id.intValue() - 1);
	}

	/**
	 * show the model
	 */
	public void mostrarModelo(){
		logger.info("____________________--- MODEL CONFIGURATION ---____________________");
		for (Village v : villageList) {
			v.show();
			v.showVehicleList();
			for (Habitant h : v.getHabitantList()) {
				h.show();
				h.showVehicleList();
			}
			v.showGroupList();
		}
	}
	
	/**
	 * configure the model
	 */
	public void initConfiguration(){
		collectDay = conf.getCollectDay();
		priceXLitreGas = conf.getPriceXLitreGas();
		kgCO2XLitreGas = conf.getPriceXLitreGas();
		lowerLevelLearningFactor = conf.getLowerLevelLearningFactor();
		higherLevelLearningFactor = conf.getHigherLevelLearningFactor();
		lowerLevelForgettingFactor = conf.getLowerLevelForgettingFactor();
		higherLevelForgettingFactor = conf.getHigherLevelForgettingFactor();
		roadLength = conf.getRoadLength();
		roadSpeed = conf.getRoadSpeed();
		roadFlow = conf.getRoadFlow();
		toll = conf.getToll();
        
		villageList = conf.getVillageList();
		Road r = new Road(roadLength, roadSpeed, roadFlow);
        totalHab = 0;
        for (Village villa : villageList) {
			villa.setRoad(r);
            totalHab += villa.getVillageNumHabs();
			if (villa.getHasBus()){
				Vehicle v = newVehicle(VehicleType.bus, -(villa.getVillageId() ) );
				villa.newCityVehicle(v);
				//Vehicle v = new Vehicle("bus"+villa.getVillageId(), "bus", 40, 80.0f, -1);
				v.setConsumption(20.0f);
				v.setConsumptionStop(5.0f);
			}
			if (villa.getHasTrain()){
				Vehicle v = newVehicle(VehicleType.train, -(villa.getVillageId() ) );
				villa.newCityVehicle(v);
				//Vehicle v = new Vehicle("train"+villa.getVillageId(), "train", 100000, 60.0f, -1);
				v.setConsumption(0.0f);
				v.setConsumptionStop(0.0f);
			}
			for (int j=villa.getVillagePosX(); j<villa.getVillagePosX()+villa.getVillageWidth(); j++){
				for (int k=villa.getVillagePosY(); k<villa.getVillagePosY()+villa.getVillageHeight(); k++){
					add(WorldModel.VILLAGE, j, k);
				}
			}
        }
	}
}