/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */

import java.util.Hashtable;

/**
 * The simulator computes values for parameters that could be asked.
 */
public class Simulator {

    // maxima
    final double maxcars = 100;
    final double maxbuses= 50;
    final double maxtrucks= 50;
    final double maxairquality = 50;
    final double maxtemperature = 50;

    
    // nomenclature of values for traffic and general air quality
    public static final String CAR_COUNT = "car_count";
    public static final String BUS_COUNT = "bus_count";
    public static final String TRUCK_COUNT = "truck_count";
    public static final String AIR_QUALITY = "air_quality";
    
    // nomenclature for temperatures and air quality per section
    public static final String TEMPERATURE1_LINE1 = "temperature1_line1";
    public static final String TEMPERATURE2_LINE1 = "temperature2_line1";
    public static final String TEMPERATURE3_LINE1 = "temperature3_line1";
    public static final String TEMPERATURE1_LINE2 = "temperature1_line2";
    public static final String TEMPERATURE2_LINE2 = "temperature2_line2";
    public static final String TEMPERATURE3_LINE2 = "temperature3_line2";
    public static final String AIR1_LINE1 = "air1_line1";
    public static final String AIR2_LINE1 = "air2_line1";
    public static final String AIR3_LINE1 = "air3_line1";
    public static final String AIR1_LINE2 = "air1_line2";
    public static final String AIR2_LINE2 = "air2_line2";
    public static final String AIR3_LINE2 = "air3_line2";
    
    //nomenclature for status of traffic lines
    public static final String LINE1_1_STATUS= "line1_1_status";
    public static final String LINE1_2_STATUS= "line1_2_status";
    public static final String LINE2_1_STATUS= "line2_1_status";
    public static final String LINE2_2_STATUS= "line2_2_status";

    // work variables
    private double currentNbCars = 50;
    private double currentNbBuses = 25;
    private double currentNbTrucks = 25;
    private double airQuality = 25;
    private Hashtable<String, Double> temperatures = null;
    private Hashtable<String, Double> air = null;
    private boolean canChangeLineStatus = true;
    private int[][] lines = {{1,1},{2,2}};
    
    
    
    /**
     * Creates the simulator
     */
    public Simulator() {
        
        // create a table for the different temperatures and
        // init the default values
        temperatures = new Hashtable<String, Double>();
        temperatures.put(TEMPERATURE1_LINE1, Double.valueOf(25));
        temperatures.put(TEMPERATURE2_LINE1, Double.valueOf(25));
        temperatures.put(TEMPERATURE3_LINE1, Double.valueOf(25));
        temperatures.put(TEMPERATURE1_LINE2, Double.valueOf(25));
        temperatures.put(TEMPERATURE2_LINE2, Double.valueOf(25));
        temperatures.put(TEMPERATURE3_LINE2, Double.valueOf(25));        

        // create a table for the different air quality and
        // init the default values
        air = new Hashtable<String, Double>();
        air.put(AIR1_LINE1, Double.valueOf(25));
        air.put(AIR2_LINE1, Double.valueOf(25));
        air.put(AIR3_LINE1, Double.valueOf(25));
        air.put(AIR1_LINE2, Double.valueOf(25));
        air.put(AIR2_LINE2, Double.valueOf(25));
        air.put(AIR3_LINE2, Double.valueOf(25));        
    }
    
 
    

    /**
     *
     */
    private Object simulateNbVehicles(String type){
        
        // compute the values
        if(type.equals(CAR_COUNT)){
            currentNbCars = computeNb(0,maxcars,currentNbCars,0.15);
            return Integer.valueOf((int)currentNbCars);
        }else if(type.equals(BUS_COUNT)){
            currentNbBuses = computeNb(0,50,currentNbBuses,0.1);
            return Integer.valueOf((int)currentNbBuses);
        }if(type.equals(TRUCK_COUNT)){
            currentNbTrucks = computeNb(0,50,currentNbTrucks,0.1);
            return Integer.valueOf((int)currentNbTrucks);
        }
        return null;
    }
    
    private double computeNb(double min,double max,double current, double amplitude){
        double res;
        double delta = Math.sin(System.currentTimeMillis())*(max*amplitude);
        res = current + (Math.random()*delta) ;
        if(res<min) res=min;
        if(res>max) res=max;
        return res;
    }
    

    /**
     * Air quality is depending on number of vehicles in the tunnel :
     * 
     * nb_vehicles*max_air_quality / max_vehicles
     *
     */
    private Object simulateAirQuality() {
        airQuality = ((currentNbBuses+currentNbCars+currentNbTrucks)*maxairquality) / (maxbuses+maxcars+maxtrucks);
        return Integer.valueOf((int)airQuality);
    }
    
    
    /**
     * 
     */
    private Object simulateTemperature(String type) {
        Double temp = (Double)temperatures.get(type);
        double current = temp.doubleValue();
        double delta = Math.sin(System.currentTimeMillis())*(maxtemperature*0.03);
        double res = current + delta ;
        if(res<0) res=0;
        if(res>maxtemperature) res=maxtemperature;
        temp = Double.valueOf(res);
        temperatures.put(type,temp);
        return temp;
    }
    
    
    /**
     * 
     */
    private Object simulateAir(String type) {
        Double temp = (Double)air.get(type);
        double current = temp.doubleValue();
        double delta = Math.sin(System.currentTimeMillis())*(maxairquality*0.03);
        double res = current + delta ;
        if(res<0) res=0;
        if(res>maxairquality) res=maxairquality;
        temp = Double.valueOf(res);
        air.put(type,temp);
        return temp;
    } 
    
    
    /**
     * 
     */
    private Object simulateLineStatus(String type) {
        
        // compute new staus of lines
        double z = Math.random();
        if(canChangeLineStatus && z>0.997){
            //randomize current line
            int currentLine = Math.round( (float)Math.random()*1.0f );
            // determine secondary line
            int secondaryLine = (currentLine==0)?1:0;
            
            // randomize current line status: 0 (no traffic),1(to-right) or 2(to-left)
            int currentLineStatus = Math.round( (float)Math.random()*2.0f );
            lines[currentLine][0]=currentLineStatus;
            lines[currentLine][1]=currentLineStatus;
            // if 0 then no traffic: then put traffic 1 and 2 on the secondary line
            if(currentLineStatus==0){
                lines[secondaryLine][0]=1;
                lines[secondaryLine][1]=2;
            }
            // if 1 then traffic is 2 on secondary line
            else if(currentLineStatus==1){
                lines[secondaryLine][0]=2;
                lines[secondaryLine][1]=2;
            }
            // if 2 then traffic is 1 on secondary line
            else if(currentLineStatus==2){
                lines[secondaryLine][0]=1;
                lines[secondaryLine][1]=1;
            }
            
            canChangeLineStatus = false;
        }
        
        
        // return traffic according to requested line
        Integer res = null;
        if(type.equals(LINE1_1_STATUS)){
            res = Integer.valueOf(lines[0][0]);
        }
        else if(type.equals(LINE1_2_STATUS)){
            res = Integer.valueOf(lines[0][1]);
        }
        else if(type.equals(LINE2_1_STATUS)){
            res = Integer.valueOf(lines[1][0]);
        }
        else if(type.equals(LINE2_2_STATUS)){
            res = Integer.valueOf(lines[1][1]);
            //status of last line is requested then allows change for the next time... maybe
            canChangeLineStatus = true;
        }
        
        return res;
    } 
    
    /**
     * Returns a value for the specified mapped parameter 
     */
    public Object getValue(String name){
        
        if(name.equals(CAR_COUNT) || name.equals(BUS_COUNT) || name.equals(TRUCK_COUNT)){
            return  simulateNbVehicles(name);
        }
        else if(name.equals(AIR_QUALITY)){
            return simulateAirQuality();
        }
        else if(name.startsWith("temperature")){
            return simulateTemperature(name);
        }
        else if(name.startsWith("air")){
            return simulateAir(name);
        }
        else if(name.startsWith("line") && name.endsWith("_status")){
            return simulateLineStatus(name);
        }
        
        return null;
        
    }

}