public class DBHelper {
	private static Statement statement = null;
	private static PreparedStatement preparedStatement = null;
	private static ResultSet resultSet = null;
	private static Connection conn = null;
	
	public static Record[] displayDataBase(String username) throws Exception {
		try {
			//establish connection
			conn = DriverManager.getConnection("jdbc:mysql://localhost/sbs_db?"
					+ "user=root&password=");
			
			// Statements allow to issue SQL queries to the database
			statement = conn.createStatement();

			String sql = "select * from sbs_userdata";
			if (username != null && !username.isEmpty()) {
				sql += " where userName=\"" + username + "\"";
			}
			// Result set get the result of the SQL query
			resultSet = statement.executeQuery(sql);
			
			ArrayList<Record> records = new ArrayList<Record>();
			while (resultSet.next()) {
				// It is possible to get the columns via name
				// also possible to get the columns via the column number
				// which starts at 1
				// e.g. resultSet.getString(2);
				int id = resultSet.getInt("Id");
				String user = resultSet.getString("userName");
				int scenarioId = resultSet.getInt("scenarioId");
				Float score = resultSet.getFloat("score");
				String report = resultSet.getString("report");
				String debrief = resultSet.getString("debrief");
				
				System.out.println("Id: " + id);
				System.out.println("User: " + user);
				System.out.println("scenarioId: " + scenarioId);
				System.out.println("score: " + score);
				System.out.println("report: " + report);
				System.out.println("debrief: " + debrief);
				System.out.println("-----------------------------");
				records.add(new Record(user, scenarioId, score, report, debrief));
			}
			return records.toArray(new Record[1]);
		} catch (SQLException ex) {
			// handle any errors
			System.out.println("SQLException: " + ex.getMessage());
			System.out.println("SQLState: " + ex.getSQLState());
			System.out.println("VendorError: " + ex.getErrorCode());
		} finally {
			close();
		}
		return null;
	}
	/**
	 * 
	 * @param userName
	 * @param scenarioId
	 * @param score
	 * @param report
	 * @return id of the insertion row
	 */
	public static int insertResult(String userName, int scenarioId, double score, String report){	
		if(userName == null){
			userName = "";
		}
		if(report == null){
			report = "";
		}

		// PreparedStatements can use variables and are more efficient
		try {
			//establish connection
			conn = DriverManager.getConnection("jdbc:mysql://localhost/sbs_db?"
					+ "user=root&password=");
			
			preparedStatement = conn
					.prepareStatement("INSERT INTO sbs_userdata VALUES (default, ?, ?, ?, ?, ?)");
			// Parameters start with 1
			preparedStatement.setString(1, userName);
			preparedStatement.setInt(2, scenarioId);
			preparedStatement.setFloat(3, (float) score);
			preparedStatement.setString(4, report);
			preparedStatement.setString(5, "No debriefing.");
			
			preparedStatement.executeUpdate();
			
			preparedStatement = conn.prepareStatement("SELECT LAST_INSERT_ID()");
			ResultSet res = preparedStatement.executeQuery();
			res.absolute(1);
			return res.getInt(1);
			
		} catch (SQLException e) {
			System.err.println("Error occurs while database updating!");
			e.printStackTrace();
		} finally {
			close();
		}
		return -1;
	}
	
	
	public static void updateDebrief(int id, String debrief){	

		// PreparedStatements can use variables and are more efficient
		try {
			//establish connection
			conn = DriverManager.getConnection("jdbc:mysql://localhost/sbs_db?"
					+ "user=root&password=");
			
			preparedStatement = conn
					.prepareStatement("UPDATE sbs_userdata SET debrief = ? WHERE id = ?");
			// Parameters start with 1
			preparedStatement.setString(1, debrief);
			preparedStatement.setInt(2, id);
	
			preparedStatement.executeUpdate();
		} catch (SQLException e) {
			System.err.println("Error occurs while database updating!");
			e.printStackTrace();
		} finally {
			close();
		}
	}
	public static void removeById(int Id){
		try {
			//establish connection
			conn = DriverManager.getConnection("jdbc:mysql://localhost/sbs_db?"
					+ "user=root&password=");
			
			preparedStatement = conn
					.prepareStatement("DELETE FROM sbs_userdata WHERE Id=" + Id);
			// Result set get the result of the SQL query
			preparedStatement.executeUpdate();
		} catch (SQLException e) {
			System.err.println("Error occurs while delete entry from database!");
			e.printStackTrace();
		} finally {
			close();
		}
	}

	//close the connection in finalize function
	private static void close() {
		try {
			if (resultSet != null) {
				resultSet.close();
			}

			if (statement != null) {
				statement.close();
			}

			if (conn != null) {
				conn.close();
			}
		} catch (Exception e) {
			System.err.println("Error occurs while close DB connection");
		}
	}

	@Override
	protected void finalize() throws Throwable {
		try {
			System.out.println("close databse connection");
			close();
			// release resources, perform cleanup ;
		} catch (Throwable t) {
			throw t;
		}
	}
		
	//test functions
	public static void main(String[] args) throws Exception {
//		DBHelper.insertResult("test1", 1, 10.0, "aaa");
//		DBHelper.insertResult("test2", 1, 10.0, "bbb");
//		DBHelper.removeById(2);
//		DBHelper.updateDebrief(17, "lalala");
//		DBHelper.displayDataBase();
	}
} public class Evaluator {

  private double score;

  private Path actual;

  private Path goldStandard;

  private Step currentStep;

  private ScoreDP scoreDP;

  private long baseTimeInMills = 0;
  
  private String userName;
  
  // private String report;
  public Evaluator(Engine engine) {
    this.engine = engine;
    actual = new Path();
    currentStep = new Step();
    actual.setTag("Actual");
    initGoden();
    scoreDP = new ScoreDP();
    baseTimeInMills = Calendar.getInstance().getTimeInMillis();
  }

  /**
   * Initialize Golden Standard Path
   * 
   */

  private void initGoden() {
    goldStandard = new Path();
    goldStandard.setTag("Gold Standard");
    goldStandard.add(new Step(new Patient(), new Prescription(), new Tool("codeblue", "Call Code",
            0), (int)Calendar.getInstance().getTimeInMillis()));
    goldStandard.add(new Step(new Patient(), new Prescription(), new Tool("OxygenMask",
            "Face Mask", 100), (int)Calendar.getInstance().getTimeInMillis()));
    goldStandard
            .add(new Step(new Patient(), new Prescription(new Drug("naloxone", "Naloxone", "naloxone"),
                    200.0, "mcg"), new Tool(), (int)Calendar.getInstance().getTimeInMillis()));
  }

  class Report {
    double score;

    String report;
  }

  /**
   * called by engine to receive the medPara
   * 
   * @param medPara
   *          , MedicalParameter is an interface in simulator package
   */
  public void receivePara(MedicalParameter medPara) {
    System.out.println("evaluator.ReceivePara called by engine!");
  }

  private Engine engine;

  // private String report;

  // overloading the constructor to support initialize with engine parameter

  public void receive(Patient patient, Calendar time) {
    currentStep.setPatient(patient);
    int curTime = (int)(Calendar.getInstance().getTimeInMillis() - baseTimeInMills);
    System.out.print(curTime);
    currentStep.setTime(curTime);
    System.out.println("Patient added");
    updateStep();
  }

  public void receive(Prescription prescription, Calendar time) {
    currentStep.setPrescription(prescription);
    int curTime = (int)(Calendar.getInstance().getTimeInMillis() - baseTimeInMills);
    currentStep.setTime(curTime);
    System.out.println("Evaluator: USER ACTION: USE DRUG:" + prescription.getDrug().getName());
    updateStep();
  }
  
  public void receive(String name){
    this.userName = name;
  }

  /**
   * called by engine to receive the Equipment variables
   * 
   * @param tool
   *          Equipment is a Class defined in gson package
   * @param time
   *          time used
   */

  public void receive(Tool tool, Calendar time) {
    currentStep.setTool(tool);
    int curTime = (int)(Calendar.getInstance().getTimeInMillis() - baseTimeInMills);
    currentStep.setTime(curTime);
    System.out.println("Evaluator: USER ACTION: USE TOOL:" + tool.getName());
    updateStep();
  }

  public void receive(Calendar time) {
    int curTime = (int)(Calendar.getInstance().getTimeInMillis() - baseTimeInMills);
    currentStep.setTime(curTime);
    updateStep();

  }

  public void regularUpdate(Patient p, Calendar time) {
    currentStep.setPatient(p);
    int curTime = (int)(Calendar.getInstance().getTimeInMillis() - baseTimeInMills);
    currentStep.setTime(curTime);
    if (isSimEnd()) {
      calculateScore();
      engine.simOver(score, generateReport());
    }
  }

  public State lastHealthyState(){
    int i = actual.size() - 1;
    while(actual.get(i).getPatient().isConditionBad()){
      i--;
      if (i < 0) return null;
    }
    Step lastHealthy = actual.get(i);
    State result = new State();
    result.setPatient(lastHealthy.getPatient());
    while(i >= 0){
      result.getPrescriptions().add(actual.get(i).getPrescription());
      result.getTools().add(actual.get(i).getTool());
    }
    return result;
  }
  
  public boolean isSimEnd() {
    if (actual.size() == 0) return false;
     int timeNow = currentStep.getTime();
     int timeLast = actual.get(actual.size() - 1).getTime();
     Patient p = currentStep.getPatient();
     return 60000 < timeNow-timeLast && (p.isConditionStable() || p.isConditionBad());
  }

  /**
   * called by engine to receive the Equipment variables
   *
   * @param tool
   *          Equipment is a Class defined in gson package
   * @param time
   *          time used
   */

  public void calculateScore() {
    scoreDP.scoreDP(goldStandard, actual);
    score = this.getPatientScore();
  }

  public void calculateScorePending() {
    scoreDP.scoreDPpending(goldStandard, actual);
    score = this.getPatientScore();
  }

  public double getScore() {
    return score;
  }
  
  public double getPatientScore(){
    return actual.patientScore();
  }

  public void updateStep() {
    if (currentStep.isComplete()) {
      actual.add(currentStep);
      currentStep = new Step();
    }
  }

  public String toString() {
    return "The score is " + score;
  }

  public void setInitialTime(Calendar initTime) {

  }

  private String generateReport() {
    PrintWriter writer = null;
    try {
      writer = new PrintWriter("src/test/resources/report.json", "UTF-8");
    } catch (FileNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    Gson gson = new Gson();
    Report r = new Report();
    r.report = this.toString();
    r.score = this.score;
    String report = gson.toJson(r);
    writer.println(report);
    writer.close();

    // Add the traceback information
    StringBuilder sb = new StringBuilder(report);
    sb.append("\n");
    sb.append("Patient score is: ");
    sb.append(getPatientScore());
    sb.append("\n");
    sb.append("The user's correct actions are :" + "\n");

    for (Step s : scoreDP.getBacktrack()) {
      sb.append(s.getStep(true));
      // sb.append("\n");
    }
    System.out.println(sb.toString());
    
    
    // generate the txt report
    String reportTxt = txtReportGenerator(score);
    
    
    // TODO: Set the patient score.
    // Where can I set the patie<nt score??
    return reportTxt;
  }

  public static Path loadGS(String filepath) throws Exception{
      String str;
      try {
	  File file = new File(filepath);
	  FileInputStream fis = new FileInputStream(file);
	  byte[] data = new byte[(int) file.length()];
	  fis.read(data);
	  fis.close();
	  str = new String(data, "UTF-8");
      } catch (Exception e) {
	  throw e;
      }      
      
      Gson gson = new Gson();
      Path gs;
      try {
	  gs = gson.fromJson(str, Path.class);
      } catch (JsonSyntaxException e) {
	  throw new Exception(e);
      }
      return gs;
  }

  private String txtReportGenerator(double score){
    String outputFile = "Report.txt";
    StringBuilder output = new StringBuilder();
    try {
      BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile, false));
      
      output.append("Here is the report for ");
      output.append(userName + ":" + "\n");
      output.append("The final score " + userName + " get is : " 
                      + String.format("%.2f\n", score));
      
//      output.append("The helpful steps and details "  
//                      + userName + " did is listed below : \n");
//      
//      output.append("Time\t Drug Used\t\t Drug Dose\t Drug Unit\t\t    Action\n");
//      
//      for (Step s : scoreDP.getBacktrack()) {
//        output.append(s.getStep(true));
//      }
//      
      
      output.append("\nThe actual steps and details "  
              + userName + " did are listed below : \n");

      output.append("Time\t\tDrug\t\tDose\t\tAction\n");
      
      for (Step s : actual) {
        output.append(s.getStep(true));
      }
      
      output.append("\n The suggested actions are listed below : \n");
      
      output.append("Drug\t\tDose\t\tAction\n");
      
      for (Step s : goldStandard) {
        output.append(s.getStep(false));
      }
      
      // Print the patient state
      if (actual.getBpHighTime()>0)
        output.append("The blood pressure is too high for " +actual.getBpHighTime()+ " seconds\n");
      if (actual.getBpLowTime()>0)
        output.append("The blood pressure is too low for " +actual.getBpLowTime()+ " seconds\n");
      if(actual.getHrHighTime()>0)
        output.append("The heart rate is too high for " +actual.getHrHighTime()+ " seconds\n");
      if(actual.getHrLowTime()>0)
        output.append("The heart rate is too low for " +actual.getHrLowTime()+ " seconds\n");
      if(actual.getOlTime()>0)
        output.append("The oxygen level is too low for " +actual.getOlTime()+ " seconds\n");
      if(actual.getRrHighTime()>0)
        output.append("The respiratory rate is too high for " +actual.getRrHighTime()+ " seconds\n");
      if(actual.getRrLowTime()>0)
        output.append("The respiratory rate is too low for " +actual.getRrLowTime()+ " seconds\n");

      
      bw.write(output.toString());
      System.out.println(output);
      
      bw.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return output.toString();
  }
  
  // Main method for testing
  public static void main(String[] args) {
      // Test for generate the path gson for Xing Sun to write the json file. 
      Gson gson = new Gson();
      ArrayList<Step> a = new ArrayList<Step>();
      a.add(new Step(new Patient(), new Prescription(), new Tool("codeblue", "Call Code",
	            0), (int)Calendar.getInstance().getTimeInMillis()));
      a.add(new Step(new Patient(), new Prescription(), new Tool("codeblue", "Call Code",
	            0), (int)Calendar.getInstance().getTimeInMillis()));
      a.add(new Step(new Patient(), new Prescription(), new Tool("codeblue", "Call Code",
	            0), (int)Calendar.getInstance().getTimeInMillis()));
      System.out.println(gson.toJson(a));
  }
} public class Drug {
  private String name = "";

  private String description = "";

  private String id = "";

  public Drug() {
    this.name = "-";
    this.description = "Default description";
    this.id = "Default id";
  }

  // constructor without does
  public Drug(String name, String description, String id) {
    super();
    this.name = name;
    this.description = description;
    this.id = id;
  }

  /**
   * @return the name
   */
  public String getName() {
    return name;
  }

  /**
   * @param name the name to set
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * @return the description
   */
  public String getDescription() {
    return description;
  }

  /**
   * @param description the description to set
   */
  public void setDescription(String description) {
    this.description = description;
  }

  /**
   * @return the id
   */
  public String getId() {
    return id;
  }

  /**
   * @param id the id to set
   */
  public void setId(String id) {
    this.id = id;
  }
} public class Report {
	Double score;
	String content;
	public Report(Double score, String content) {
		this.score = score;
		this.content = content;
	}
} public class Tool {
  private String name = "-";

  private String id = null;

  public Tool() {
    this("Default id", "-", 100);
  }

  private double value;

  public String getName() {
    return name;
  }

  public Tool setName(String toolName) {
    this.name = toolName;
    return this;
  }

  public String getId() {
    return id;
  }

  public String toString() {
    return name;
  }

  public Tool(String id, String name, double value) {

    this.id = id;
    this.name = name;
  }

  public double getValue() {
    return value;
  }

  public void setValue(double value) {
    this.value = value;
  }
} public class State {

	private Patient patient;

	private ArrayList<Prescription> prescriptions;

	private ArrayList<Tool> tools;

	public State() {
		prescriptions = new ArrayList<Prescription>();
		tools = new ArrayList<Tool>();
		patient = new Patient();
	}

	public Patient getPatient() {
		return patient;
	}

	public void setPatient(Patient p) {
		this.patient = p;
	}

	public ArrayList<Prescription> getPrescriptions() {
		return prescriptions;
	}

	public void setPrescriptions(ArrayList<Prescription> prescriptions) {
		this.prescriptions = prescriptions;
	}

	public ArrayList<Tool> getTools() {
		return tools;
	}

	public void setTools(ArrayList<Tool> tools) {
		this.tools = tools;
	}
	

} public class Prescription {
  private Drug drug;

  private Double dose;

  private String unit;

  // Default initializer that will ensure testing successfully using main method.
  public Prescription() {
    this(new Drug(), 0.0, "-");
  }

  public Prescription(Drug drug, double dose, String unit) {
    this.setDrug(drug);
    this.setDose(dose);
    this.setUnit(unit);
  }

  public String getUnit() {
    return unit;
  }

  public Prescription setUnit(String unit) {
    this.unit = unit;
    return this;
  }

  public Double getDose() {
    return dose;
  }

  public Prescription setDose(Double dose) {
    this.dose = dose;
    return this;
  }

  public Drug getDrug() {
    return drug;
  }

  public Prescription setDrug(Drug drug) {
    this.drug = drug;
    return this;
  }
  
  public String toString(){
  	if(dose == 0) {
  		return "-\t\t-";
  	}
    return drug.getName() + "\t" + dose + unit;
  }
} public class Patient implements Cloneable {
	private String basic; //eg: male, 35, white
	private String description; //eg: headache, vomit
	

	private Condition cd;
	//bloodPressure range from (60, 120) and (40, 80)
	private BloodPressure bloodPressure = new BloodPressure(90.0, 60.0, 120.0, 60.0, 40.0, 80.0);
	//heartRate range from (50, 100)
	private HeartRate heartRate = new HeartRate(70.0, 50.0, 100.0);
	//oxygenLevel range from (0.6, 1.0)
	private OxygenLevel oxygenLevel = new OxygenLevel(0.8, 0.6, 1.0);

	//respirationRate range from (0.6, 20)
	private RespirationRate respirationRate = new RespirationRate(0.6, 0.0, 20.0);
	
	GraphicDisplay graDisplay;
	Status status;
	
	public Patient() {
		
	}
	public Patient(String basic, String description){
		this.basic = basic;
		this.description = description;
	}
	public Patient(Condition cd) {
		super();
		this.cd = cd;
	}
	public String getBasic() {
		return basic;
	}
	public String getDescription() {
		return description;
	}
	public BloodPressure getBloodPressure() {
		return bloodPressure;
	}

	public void setBloodPressure(BloodPressure bloodPressure) {
		this.bloodPressure = bloodPressure;
	}

	public HeartRate getHeartRate() {
		return heartRate;
	}

	public void setHeartRate(HeartRate heartRate) {
		this.heartRate = heartRate;
	}

	public OxygenLevel getOxygenLevel() {
		return oxygenLevel;
	}

	public void setOxygenLevel(OxygenLevel oxygenLevel) {
		this.oxygenLevel = oxygenLevel;
	}

	public RespirationRate getRepirationRate() {
		return respirationRate;
	}

	public void setRespirationRate(RespirationRate respirationRate) {
		this.respirationRate = respirationRate;
	}
	
	/*
	 * This is the override function for clone()
	 * @see java.lang.Object#clone()
	 */
	public Patient clone(){
		try {
			return (Patient)super.clone();
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	public boolean isConditionStable(){
	  // check heart rate
	  boolean res = heartRate.getHrNum() > 60 && heartRate.getHrNum() < 151;
	  // check respiratory rate
	  res = res && respirationRate.getRrNum() > 12 && respirationRate.getRrNum() < 20.1;
	  // check blood pressure
	  res = res && (bloodPressure.getDiastolicBloodPressure() 
            + bloodPressure.getSystolicBloodPressure() < 261) &&
            (bloodPressure.getDiastolicBloodPressure()
            + bloodPressure.getSystolicBloodPressure() > 140);
	  // check oxygen level
	  res = res && oxygenLevel.getOlNum()>.90 ;
	  return res;
	}
	
	public boolean isConditionBad(){
	  // check heart rate
    boolean res = heartRate.getHrNum() < 10 || heartRate.getHrNum() > 180;
    // check respiratory rate
    res = res || respirationRate.getRrNum() > 50;
    // check blood pressure
    res = res || (bloodPressure.getDiastolicBloodPressure() 
            + bloodPressure.getSystolicBloodPressure() > 360) ||
            (bloodPressure.getDiastolicBloodPressure()
            + bloodPressure.getSystolicBloodPressure() < 40);
    // check oxygen level
    res = res || oxygenLevel.getOlNum()<.50 ;
    return res;
	}
} public class Simulator {

	// the model of patient
	private Patient patient;

	static boolean checkBloodPressure = false;
	static boolean checkHeartRate = false;
	static boolean checkOxygenLevel = false;
	static boolean checkRespirationRate = false;

	// toolList and pescriptionList to store the information of tool and
	// prescription which have effects on the patient.
	private ArrayList<Tool> toolList;
	private ArrayList<Prescription> prescriptionList;

	// dictionary of boundaries for each parameter
	private static HashMap<String, Double> lowerBoundDict = new HashMap<String, Double>();
	private static HashMap<String, Double> upperBoundDict = new HashMap<String, Double>();

	// the initialization function for engine to involve
	// adding default value for each parameters
	public Patient initialPatient() {
		return initialPatient(90.0, 60.0, 90.0, 0.8, 16.0);

		/*
		 * patient = new Patient(); //
		 * pt.getBloodPressure().setBpNum(defaultBp);
		 * patient.getBloodPressure().setSystolicBloodPressure(90.0);
		 * patient.getBloodPressure().setDiastolicBloodPressure(60.0);
		 * patient.getHeartRate().setHrNum(90.0);
		 * patient.getOxygenLevel().setOlNum(0.8);
		 * patient.getRepirationRate().setRrNum(16.0); return patient;
		 */
	}

	/*
	 * Initialize patient with given parameters
	 */
	public Patient initialPatient(double systolicBloodPressure,
			double diastolicBloodPressure, double heartRate,
			double oxygenLevel, double repirationRate) {
		patient = new Patient();

		patient.setBloodPressure(new BloodPressure(systolicBloodPressure,
				lowerBoundDict.get("systolicBloodPressure"), upperBoundDict
						.get("systolicBloodPressure"), diastolicBloodPressure,
				lowerBoundDict.get("diastolicBloodPressure"), upperBoundDict
						.get("diastolicBloodPressure")));

		patient.setHeartRate(new HeartRate(heartRate, lowerBoundDict
				.get("heartRate"), upperBoundDict.get("heartRate")));

		patient.setOxygenLevel(new OxygenLevel(oxygenLevel, lowerBoundDict
				.get("oxygenLevel"), upperBoundDict.get("oxygenLevel")));

		patient.setRespirationRate(new RespirationRate(repirationRate,
				lowerBoundDict.get("respirationRate"), upperBoundDict
						.get("respirationRate")));

		return patient;
	}

	private void initBound() {
		// set default boundaries for each parameters
		lowerBoundDict.put("respirationRate", 0.0);
		upperBoundDict.put("respirationRate", 20.0);

		lowerBoundDict.put("heartRate", 50.0);
		upperBoundDict.put("heartRate", 120.0);

		lowerBoundDict.put("diastolicBloodPressure", 40.0);
		upperBoundDict.put("diastolicBloodPressure", 100.0);

		lowerBoundDict.put("systolicBloodPressure", 70.0);
		upperBoundDict.put("systolicBloodPressure", 190.0);

		lowerBoundDict.put("oxygenLevel", 0.6);
		upperBoundDict.put("oxygenLevel", 1.0);
	}

	public Simulator() {
		super();
		initialPatient();
		initBound();
		toolList = new ArrayList<Tool>();
		prescriptionList = new ArrayList<Prescription>();
	}

	public Simulator(Patient pt) {
		super();
		initBound();
		this.patient = pt;
		toolList = new ArrayList<Tool>();
		prescriptionList = new ArrayList<Prescription>();
	}

	public Simulator(Patient pt, ArrayList<Prescription> prescriptions,
			ArrayList<Tool> tools) {
		super();
		initBound();
		this.patient = pt;
		toolList = tools;
		prescriptionList = prescriptions;
	}

	// the engine can get patient info from simulator
	public Patient simPatient() {

		// no tool and no prescription, invoke the downFunction for all four
		// parameters
		if (toolList.size() == 0 && prescriptionList.size() == 0) {
			// invoke the downFunction for all four parameters
			downFunctionOxygenLevel();
			downFunctionHeartRate();
			downFunctionBloodPressure();
			downFunctionBloodPressure();
			downFunctionRespirationRate();
			return this.patient;
		} else {

			if (!checkBloodPressure) {
				downFunctionBloodPressure();
			}
			if (!checkHeartRate) {
				downFunctionHeartRate();
			}
			if (!checkOxygenLevel) {
				downFunctionOxygenLevel();
			}
			if (!checkRespirationRate) {
				downFunctionRespirationRate();
			}

			// get current value for all four medical parameters
			double currentOxygenLevel = patient.getOxygenLevel().getOlNum();
			double currentHeartRate = patient.getHeartRate().getHrNum();
			double currentSystolicBloodPressure = patient.getBloodPressure()
					.getSystolicBloodPressure();
			double currentDiastolicBloodPressure = patient.getBloodPressure()
					.getDiastolicBloodPressure();
			double currentRespirationRate = patient.getRepirationRate()
					.getRrNum();
			if (toolList.size() > 0) {
				// get tool information
				Tool currentTool = toolList.get(toolList.size() - 1);
				double currentValue = currentTool.getValue();

				// check the tool according to the name of the tool
				if (currentTool.getId().equals("OxygenMask")) {
					double resultOxygenLevel = ytFunctionOxygenLevel(currentValue);
					// System.out.print("resultOL = " + resultOl);
					// reset the oxygenLevel of the patient

					patient.getOxygenLevel().setOlNum(resultOxygenLevel);
					checkOxygenLevel = true;
				}

				// the name of the tool do not correct, I just test the function
				if (currentTool.getName().equals("increase the heart rate")) {
					//
					double resultRatioForHeartRate = ytFunctionHeartRate(currentValue);
					System.out.print("resultHR = " + resultRatioForHeartRate);
					double resultHeartRate = (1 + resultRatioForHeartRate / 100)
							* currentHeartRate;

					// System.out.print("patient's heart rate:" +
					// resultHeartRate);
					patient.getHeartRate().setHrNum(resultHeartRate);
				}

				// the name of the tool do not correct, I just test the function
				if (currentTool.getName().equals("increase the blood pressure")) {
					//
					double resultRatioForBloodPressure = ytFunctionBloodPressure(currentValue);
					// System.out.print("resultBR = "
					// + resultRatioForBloodPressure);
					double resultSystolicBloodPressure = (1 + resultRatioForBloodPressure / 100)
							* currentSystolicBloodPressure;
					double resultDiastolicBloodPressure = (1 + resultRatioForBloodPressure / 100)
							* currentDiastolicBloodPressure;

					// System.out.print("patient's Systolic blood pressure:"
					// + resultSystolicBloodPressure);
					// System.out.println("patient's Diastolic blood pressure:"
					// + resultDiastolicBloodPressure);

					patient.getBloodPressure().setDiastolicBloodPressure(
							resultDiastolicBloodPressure);
					patient.getBloodPressure().setSystolicBloodPressure(
							resultSystolicBloodPressure);
				}

				// the name of the tool do not correct, I just test the function
				if (currentTool.getName().equals(
						"increase the Respiration Rate")) {
					//
					double resultRatioForRespirationRate = ytFunctionRespirationRate(currentValue);
					System.out.print("resultRR = "
							+ resultRatioForRespirationRate);
					double resultRespirationRate = (1 + resultRatioForRespirationRate / 100)
							* currentRespirationRate;

					// set boundery for respirationRate
					if (resultRespirationRate > 20) {
						resultRespirationRate = 20;
					}

					// System.out.print("patient's Systolic Respiration Rate:"
					// + resultRespirationRate);
					patient.getRepirationRate().setRrNum(resultRespirationRate);
				}
			}

			// check the prescription
			if (prescriptionList.size() > 0) {
				// get the current prescription
				Prescription currentPrescription = prescriptionList
						.get(prescriptionList.size() - 1);
				double currentDoes = currentPrescription.getDose() / 20;

				// get prescription information
				if (currentPrescription.getDrug().getId().equals("naloxone")) {
					double resultOxygenLevel = ytFunctionOxygenLevel(currentDoes * 10);
					// System.out.print("resultOL = " + resultOl);
					patient.setOxygenLevel(new OxygenLevel(resultOxygenLevel,
							lowerBoundDict.get("oxygenLevel"), upperBoundDict
									.get("oxygenLevel")));

					checkOxygenLevel = true;
				}

				// **********************the prescription
				// effect**************************************************

				if (currentPrescription.getDrug().getId().equals("naloxone")) {
					//
					double resultRatioForHeartRate = ytFunctionHeartRate(currentDoes);

					// System.out.println("current does = " + currentDoes);
					// System.out.println("resultRatioForHeartRate = " +
					// resultRatioForHeartRate);

					// System.out.print("resultHR = " +
					// resultRatioForHeartRate);

					double resultHeartRate = (1 + resultRatioForHeartRate / 100)
							* currentHeartRate;

					// System.out.print("patient's heart rate:" +
					// resultHeartRate);

					patient.getHeartRate().setHrNum(resultHeartRate);
					checkHeartRate = true;
				}

				// the name of the tool do not correct, I just test the function
				if (currentPrescription.getDrug().getId().equals("naloxone")) {
					//
					double resultRatioForBloodPressure = ytFunctionBloodPressure(currentDoes);

					// System.out.print("resultBR = " +
					// resultRatioForBloodPressure);

					double resultSystolicBloodPressure = (1 + resultRatioForBloodPressure / 100)
							* currentSystolicBloodPressure;
					double resultDiastolicBloodPressure = (1 + resultRatioForBloodPressure / 100)
							* currentDiastolicBloodPressure;

					// System.out.print("patient's Systolic blood pressure:"+
					// resultSystolicBloodPressure);
					// System.out.println("patient's Diastolic blood pressure:"
					// + resultDiastolicBloodPressure);

					patient.getBloodPressure().setSystolicBloodPressure(
							resultSystolicBloodPressure);
					patient.getBloodPressure().setDiastolicBloodPressure(
							resultDiastolicBloodPressure);

					checkBloodPressure = true;
				}

				// the name of the tool do not correct, I just test the function
				if (currentPrescription.getDrug().getId().equals("naloxone")) {
					//
					double resultRatioForRespirationRate = ytFunctionRespirationRate(currentDoes);

					// System.out.print("resultRR = "
					// + resultRatioForRespirationRate);

					System.out
							.println("patient's resultRatioForRespirationRate is:"
									+ resultRatioForRespirationRate);

					double resultRespirationRate = (1 + resultRatioForRespirationRate / 5.65)
							* currentRespirationRate;

					System.out.println("patient's Respiration Rate:"
							+ resultRespirationRate);

					patient.getRepirationRate().setRrNum(resultRespirationRate);
					checkRespirationRate = true;
				}
			}

			return patient;
		}
	}

	public Patient getPatient() {
		return patient;
	}

	// reset patient
	public void setPatient(Patient patient) {
		this.patient = patient;
		this.prescriptionList.clear();
		this.toolList.clear();
		System.out.println("reset pt in simulator!!!!!");
	}

	public ArrayList<Tool> getToolList() {
		return toolList;
	}

	public void setToolList(ArrayList<Tool> toolList) {
		this.toolList = toolList;
	}

	public ArrayList<Prescription> getPrescriptionList() {
		return prescriptionList;
	}

	public void setPrescriptionList(ArrayList<Prescription> prescriptionList) {
		this.prescriptionList = prescriptionList;
	}

	public void simulateWithTool(Tool eq) {
		// set the parameters according to the equipment from engine
		System.out.println("using equipments in the function simulateWithTool");

		// when the engine involve the function simulateWithTool, the simulator
		// add the tool to the toolList
		toolList.add(eq);
	}

	public void simWithDrugs(Prescription p) {
		// set the parameters according to the drug from engine
		System.out.println("using drug in the function simulateWithDrug");

		// when the engine involve the function simulateWithDrug, the simulator
		// add the Prescription to the prescriptionList
		prescriptionList.add(p);
	}

	// helper function
	double fFunction(double x) {
		double p1 = 1.667 * Math.pow(10, -6);
		double p2 = -0.0002536;
		double p3 = 0.01458;
		double p4 = -0.2743;

		double result;

		result = p1 * Math.pow(x, 3) + p2 * Math.pow(x, 2) + p3 * x + p4;

		return result;
	}

	// helper function
	public double t0Function(double x0) {
		double result;

		result = 1.0 * (120 - x0) / 2 - 1.0 / fFunction(x0)
				* Math.log(1.0 * (1 - patient.getOxygenLevel().getOlNum()) / //
						patient.getOxygenLevel().getOlNum());

		// System.out.println("1.0*(120 - x0)/2 = " + 1.0 * (120 - x0) / 2);
		// System.out.println("1.0/fFunction(x0) = " + 1.0 / fFunction(x0));
		// System.out.println("Math.log(1.0*(1-defaultOl) / defaultOl = " + //
		// Math.log(1.0 * (1 - patient.getOxygenLevel().getOlNum())
		// / patient.getOxygenLevel().getOlNum()));
		//
		// System.out.println("t0Function's result:" + result);

		return result;
	}

	// this function calculate the curve(oxygenLevel)
	public double ytFunctionOxygenLevel(double x0) {
		double result;
		// invoke two helper function
		result = 1.0 / (1 + Math.exp(-fFunction(x0)
				* ((t0Function(x0) + 1.0 / 10) - 1.0 * (120 - x0) / 2)));
		if (result < 1.00) {
			this.patient.setOxygenLevel(new OxygenLevel(result));
		}
		// set a bound to oxgen level
		else {
			result = 1.00;
			this.patient.setOxygenLevel(new OxygenLevel(result));
		}
		return result;
	}

	// this function calculate the curve(BloodPressure)
	public double ytFunctionBloodPressure(double x0) {
		double result;
		result = 1.0 / (1 + Math.exp(-fFunction(x0)
				* ((t0Function(x0) + 1.0 / 15) - 1.0 * (120 - x0) / 2)));
		return result;
	}

	// this function calculate the curve(HeartRate)
	public double ytFunctionHeartRate(double x0) {

		double result;
		result = 1.0 / (1 + Math.exp(-fFunction(x0)
				* ((t0Function(x0) + 1.0 / 17) - 1.0 * (120 - x0) / 2)));
		return result;
	}

	// this function calculate the curve(RespirationRate)
	public double ytFunctionRespirationRate(double x0) {

		double result;

		result = 1.0 / (1 + Math.exp(-fFunction(x0)
				* ((t0Function(x0) + 1.0 / 10) - 1.0 * (120 - x0) / 2)));

		return result;
	}

	// invoke the function OxygenLevel when there is no action
	public double downFunctionOxygenLevel() {
		double result;
		result = 1 - 0.0015 * Math.pow(1, 2);
		double currentOxygenLevel = patient.getOxygenLevel().getOlNum();

		double resultOxygenLevel = currentOxygenLevel * result;

		// set the boundery for the oxygenLevel
		if (resultOxygenLevel < 0.6) {
			resultOxygenLevel = 0.6;
		} else {
			// doing nothing
		}

		// reset the oxygenLevel of the patient
		this.patient.setOxygenLevel(new OxygenLevel(resultOxygenLevel));
		return result;
	}

	// invoke the function BloodPressure when there is no action
	public double downFunctionBloodPressure() {
		double result;
		result = 1 - 0.0025 * Math.pow(1, 2);
		double currentSystolicBloodPressure = patient.getBloodPressure()
				.getSystolicBloodPressure();
		double currentDiastolicBloodPressure = patient.getBloodPressure()
				.getDiastolicBloodPressure();

		double resultSystolicBloodPressure = result
				* currentSystolicBloodPressure;
		double resultDiastolicBloodPressure = result
				* currentDiastolicBloodPressure;

		// set the boundery for the bloodPressure
		if (resultSystolicBloodPressure < 70) {
			resultSystolicBloodPressure = 70;
		} else {
			// doing nothing
		}
		if (resultDiastolicBloodPressure < 40) {
			resultDiastolicBloodPressure = 40;
		} else {
			// doing nothing
		}

		// System.out.println("currentSystolicBloodPressure = "
		// + currentSystolicBloodPressure);
		// System.out.println("currentDiastolicBloodPressure = "
		// + currentDiastolicBloodPressure);

		// reset the SystolicBloodPressure and DiastolicBloodPressure of the
		// patient
		this.patient.setBloodPressure(new BloodPressure(
				resultSystolicBloodPressure, resultDiastolicBloodPressure));

		return result;
	}

	// invoke the function HeartRate when there is no action
	public double downFunctionHeartRate() {
		double result;
		result = 1 - 0.0025 * Math.pow(1, 2);
		double currentHeartRate = patient.getHeartRate().getHrNum();
		// reset the heartRate of the patient

		double resultHeartRate = currentHeartRate * result;

		// set the boundery
		if (resultHeartRate < 50) {
			resultHeartRate = 50;
		} else {
			// doing nothing
		}

		this.patient.setHeartRate(new HeartRate(resultHeartRate));
		return result;
	}

	// invoke the function RespirationRate when there is no action
	public double downFunctionRespirationRate() {
		double result;
		result = 1 - 0.001 * Math.pow(1, 2);
		double currentRespirationRate = patient.getRepirationRate().getRrNum();

		double resultRespirationRate = currentRespirationRate * result;

		if (resultRespirationRate < 0) {
			resultRespirationRate = 0;
		} else {
			// doing nothing
		}
		// reset the respirationRate of the patient
		this.patient.setRespirationRate(new RespirationRate(
				resultRespirationRate));
		return result;
	}

	/*
	 * public double newSimulationFunction(double value){ init_spo2 =
	 * linspace(1,0.7,6); init_resp = linspace(70,40,6); % f_hr = fit(m',
	 * init_hr','poly1'); % f_spo2 = fit(m', init_spo2','poly1'); % f_resp =
	 * fit(m', init_resp','poly1');
	 * 
	 * % simulate bp -> f(hr) hrr = linspace(120,50,30); bpp =
	 * linspace(170,80,30); f_bp_hr = fit(hrr',bpp','poly1');
	 * 
	 * % simulate bp -> f(resp) hrr = linspace(120,50,30); resp =
	 * linspace(30,10,30); f_resp_hr = fit(hrr',resp','poly1'); %% User specify:
	 * % morphin-->m [0 5] % naloxone-->n [0 10] % initial HR [120 50], 120 is
	 * normal % BP [170:60] % RESP [30:10]
	 * 
	 * clear;clc % When choosing naloxone, n can be 0-10. If both 5, naloxone
	 * will just % keep the status steady, but not increase the parameters m =
	 * 5; n = 9; init_hr = 89;
	 * 
	 * % Simulation delta_t = 1; % engine cycle time init_rate = 1/10*(n-m);
	 * mask = false; while true rate_next =
	 * sign(init_rate)*exp(log(abs(init_rate))-1/70*delta_t); hr =
	 * init_hr+rate_next*delta_t; init_rate = rate_next; init_hr = hr; if hr >
	 * 120 hr = 120; elseif hr < 50 % die hr = 50; end
	 * 
	 * % Simulate Blood Pressure and Respiration from HeartRate % range of HR
	 * [120:50]--> BP [170:60] % range of HR [120:50]--> RESP [30:10] bp =
	 * 1.286*hr-15.17;
	 * 
	 * if mask == true resp = 0.2875*hr-4.286+mask_rate*delta_t; else resp =
	 * 0.2875*hr-4.286; end % only round the parameters when display to UI!!!
	 * fprintf('Heart rate: %3.0f \n',floor(hr)); % report hr fprintf('Blood
	 * pressure: %3.0f \n',floor(bp)); % report bp fprintf('Respiration: %3.0f
	 * \n',floor(resp)); % report resp pause clc end
	 * 
	 * }
	 */

} public class UserInterface {
	private Engine decisionEngine;
	private MainWindow window;
	private ReportWindow reportWindow;
	private HashMap<String, Tool> toolMap;
	private HashMap<String, Drug> drugMap;
	private UserInterface ui = this;

	/**
	 * initialize the user interface controller and connect it with decision
	 * engine
	 * 
	 * @param decisionEngine
	 *          the decision engine it's connected to
	 * @throws Exception
	 */

	public UserInterface(Engine decisionEngine) throws Exception {
		this.decisionEngine = decisionEngine;
		this.toolMap = new HashMap<String, Tool>();
		this.drugMap = new HashMap<String, Drug>();

		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					window = new MainWindow(ui);
					window.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	public void callCode(String code) {
		// decisionEngine.callCode(code);
		ui.addPathography("Code Blue!");
		try {
			decisionEngine.useTool("codeblue");
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		try {
			Sound.play("alarm");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void connectMonitor() {
		decisionEngine.connectMonitor();
		ui.addPathography("Monitor connected!");
	}

	public void useTool(String id) {
		try {
			decisionEngine.useTool(id);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	

	public void useDrug(String id, Double dose, String unit) {
		assert (id != null);
		try {
			decisionEngine.useDrug(id, dose, unit);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		ui.addPathography("used a drug!");
		window.closeDrugWindow();
	}

	public void setPatientInfo(final Patient patient) {
		assert (patient != null);
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				window.setPatient(patient.getBasic(), patient.getDescription());
			}
		});

	}

	public void addDrug(final Drug drug) {
		assert (drug != null);
		drugMap.put(drug.getId(), drug);
	}
	public void addDrug(Drug[] drugMap){
	  
	  for(Drug d:drugMap){
	    addDrug(d);
	  }
	}

	public void addTool(Tool tool) {
		assert (tool != null);
		toolMap.put(tool.getId(), tool);
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				window.addTool(tool.getId(), tool.getName());
			}
		});
	}
	
	public void addTools(Tool[] tools) {
		for (Tool tool: tools) {
			addTool(tool);
		}
	}

	public void updateReport(Double score, String report) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					if (reportWindow == null)
						reportWindow = new ReportWindow();
					reportWindow.setScore(score.toString());
					reportWindow.setContent(report);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	public void updateMonitor(Patient p) {
		assert (p != null);
		assert (p.getBloodPressure() != null);
		assert (p.getHeartRate() != null);
		assert (p.getOxygenLevel() != null);
		assert (p.getRepirationRate() != null);
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				window.setMonitor(p.getBloodPressure().getDiastolicBloodPressure(), p
						.getBloodPressure().getSystolicBloodPressure(), p.getHeartRate()
						.getHrNum(), p.getOxygenLevel().getOlNum(), p.getRepirationRate()
						.getRrNum());
			}
		});
	}

	public void updateTime(Calendar time) {
		assert (time != null);
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				window.updateClock(time.get(Calendar.HOUR_OF_DAY),
						time.get(Calendar.MINUTE), time.get(Calendar.SECOND));
			}
		});
	}

	public void addPathography(String feedback) {
		assert (feedback != null);
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				window.addPathography(feedback);
			}
		});
	}
	
	public HashMap<String,Drug> getDrugMap(){
	  System.out.println(drugMap);
	  return drugMap;
	}

} public final class Setting {
	public static final boolean LOCAL_MODE = false;
}
import edu.cmu.lti.bic.sbs.db.DBHelper;
import edu.cmu.lti.bic.sbs.evaluator.Evaluator;
import edu.cmu.lti.bic.sbs.gson.Drug;
import edu.cmu.lti.bic.sbs.gson.Report;
import edu.cmu.lti.bic.sbs.gson.Tool;
import edu.cmu.lti.bic.sbs.evaluator.State;
import edu.cmu.lti.bic.sbs.gson.Prescription;
import edu.cmu.lti.bic.sbs.gson.Patient;
import edu.cmu.lti.bic.sbs.simulator.Simulator;
import edu.cmu.lti.bic.sbs.ui.UserInterface;
import edu.cmu.lti.bic.sbs.Setting;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Timer;
package edu.cmu.lti.bic.sbs.engine;


/**
 * The Engine Class
 * 
 * @author Xiaoxu Lu
 *
 */
public class Engine {
	
	UserInterface ui = null;
	Simulator simulator = null;
	Evaluator evaluator = null;
	Scenario scenario = null;

	Calendar time = Calendar.getInstance();
	Timer timer = new Timer();

	boolean isOver = false;
	private Report report = null;