package org.greengin.sciencetoolkit.model;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Vector;
import org.greengin.sciencetoolkit.R;
import org.greengin.sciencetoolkit.common.model.AbstractModelManager;
import org.greengin.sciencetoolkit.common.model.Model;
import org.greengin.sciencetoolkit.common.model.SettingsManager;
import org.greengin.sciencetoolkit.common.model.events.ModelNotificationListener;
import org.greengin.sciencetoolkit.common.model.events.NotificationListenerAggregator;
import org.greengin.sciencetoolkit.logic.datalogging.DataLogger;
import org.greengin.sciencetoolkit.logic.sensors.SensorWrapperManager;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.util.Log;
public class ProfileManager extends AbstractModelManager implements ModelNotificationListener {
public static final String DEFAULT_PROFILE_ID = "1";
private static ProfileManager instance;
public static void init(Context applicationContext) {
instance = new ProfileManager(applicationContext);
}
public static ProfileManager get() {
return instance;
}
NotificationListenerAggregator listeners;
Model settings;
Model appSettings;
Comparator<String> profileIdComparator;
private ProfileManager(Context applicationContext) {
super(applicationContext, "profiles.xml", 500);
settings = SettingsManager.get().get("profiles");
appSettings = SettingsManager.get().get("app");
listeners = new NotificationListenerAggregator(applicationContext, "profiles:notifications");
SettingsManager.get().registerDirectListener("profiles", this);
initDefaultProfile();
checkDataConsistency();
ModelNotificationListener sensorListener = new ModelNotificationListener() {
@Override
public void modelNotificationReceived(String msg) {
if (msg.startsWith("sensor:")) {
sensorModified(msg.substring(7));
}
}
};
for (String sensorId : SensorWrapperManager.get().getSensorsIds()) {
SettingsManager.get().registerDirectListener("sensor:" + sensorId, sensorListener);
}
profileIdComparator = new Comparator<String>() {
@Override
public int compare(String lhs, String rhs) {
try {
return Integer.parseInt(rhs) - Integer.parseInt(lhs);
} catch (Exception e) {
return 0;
}
}
};
}
private void initDefaultProfile() {
if (!items.containsKey(ProfileManager.DEFAULT_PROFILE_ID)) {
Model defaultProfile = createEmptyProfile(ProfileManager.DEFAULT_PROFILE_ID);
defaultProfile.setString("title", applicationContext.getString(R.string.default_profile_title), true);
modelModified(defaultProfile);
listeners.fireEvent("list");
settings.setString("current_profile", defaultProfile.getString("id"));
}
if (!items.containsKey(getActiveProfileId())) {
settings.setString("current_profile", ProfileManager.DEFAULT_PROFILE_ID);
}
}
public boolean profileIdIsActive(String id) {
return id != null && id.equals(getActiveProfileId());
}
public boolean profileIsActive(Model profile) {
return profile != null && profileIdIsActive(profile.getString("id"));
}
public boolean profileIdIsDefault(String id) {
return ProfileManager.DEFAULT_PROFILE_ID.equals(id);
}
public boolean profileIsDefault(Model profile) {
return profile != null && profileIdIsDefault(profile.getString("id"));
}
public boolean activeProfileIsDefault() {
return profileIdIsDefault(getActiveProfileId());
}
private void checkDataConsistency() {
for (Entry<String, Model> entry : items.entrySet()) {
String id = entry.getKey();
Model model = entry.getValue();
if (id.length() == 0) {
Log.d("stk profiles", "empty id");
} else {
if (!id.equals(model.getString("id"))) {
Log.d("stk profiles", "conflicting ids: " + id + " " + model.getString("id"));
}
}
}
}
private Model createEmptyProfile(String id) {
Model profile = new Model(this);
items.put(id, profile);
profile.setString("id", id, true);
profile.getModel("sensors", true, true);
profile.setBool("initial_edit", true);
return profile;
}
public void createProfile(String title, boolean makeActive) {
Model newProfile = createEmptyProfile(getNewId());
newProfile.setString("title", title, true);
super.modelModified(null);
listeners.fireEvent("list");
if (makeActive && DataLogger.get().isIdle()) {
switchActiveProfile(newProfile.getString("id"));
}
}
public void deleteProfile(String profileId) {
this.remove(profileId);
listeners.fireEvent("list");
DataLogger.get().deleteData(profileId);
}
public void switchActiveProfile(String profileId) {
if (profileId != null && !profileId.equals(settings.getString("current_profile"))) {
if (DataLogger.get().isRunning()) {
DataLogger.get().stopSeries();
}
settings.setString("current_profile", profileId);
updateGlobalSensors();
}
}
public boolean sensorSelectedInExplore(String sensorId) {
Model profile = getActiveProfile();
if (profile != null && profile.getBool("initial_edit", false) && !profile.getBool("is_remote") && DataLogger.get().isIdle()) {
profile.getModel("sensors").clearAll(true);
addSensor(profile, sensorId, false);
return true;
} else {
return false;
}
}
public void removeSensorFromActiveProfile(String sensorId) {
Model profile = getActiveProfile();
if (profile != null) {
removeSensor(profile, sensorId);
}
profile.setBool("initial_edit", profile.getModel("sensors", true).getModels().size() == 0);
}
private Model addSensor(Model profile, String sensorId, boolean suppressSave) {
Model profileSensors = profile.getModel("sensors", true, true);
String id = null;
for (int i = 0;; i++) {
id = String.valueOf(i);
if (profileSensors.getModel(id) == null) {
break;
}
}
return addSensor(profile, id, sensorId, suppressSave);
}
private Model addSensor(Model profile, String profileSensorId, String sensorId, boolean suppressSave) {
Model profileSensors = profile.getModel("sensors", true, true);
int weight = profileSensors.getModels().size();
if (!sensorInProfile(sensorId, profile)) {
Model profileSensor = profileSensors.getModel(profileSensorId, true, true);
profileSensor.setString("id", profileSensorId, true);
profileSensor.setString("sensorid", sensorId, true);
profileSensor.setInt("weight", weight, true);
Model sensorSettings = SettingsManager.get().get("sensor:" + sensorId);
Model profileSensorSettings = profileSensor.getModel("sensor_settings", true, true);
profileSensorSettings.copyPrimitives(sensorSettings, true);
if (!suppressSave) {
this.modelModified(profile);
}
return profileSensor;
} else {
return null;
}
}
public void addSensors(Model profile, Vector<String> sensorIds) {
addSensors(profile, sensorIds, false);
profile.setBool("initial_edit", false);
}
private void addSensors(Model profile, Vector<String> sensorIds, boolean suppressSave) {
for (String sensorId : sensorIds) {
addSensor(profile, sensorId, true);
}
if (sensorIds.size() > 0 && !suppressSave) {
this.modelModified(profile);
}
}
public void removeSensor(Model profile, String sensorId) {
Model profileSensors = profile.getModel("sensors", true);
for (Model profileSensor : profileSensors.getModels()) {
if (sensorId.equals(profileSensor.getString("sensorid"))) {
profileSensors.clear(profileSensor.getString("id"));
}
}
}
public Model get(String key) {
return get(key, false);
}
public Model getActiveProfile() {
return get(getActiveProfileId());
}
public String getActiveProfileId() {
return settings.getString("current_profile");
}
public Vector<String> getProfileIds() {
Vector<String> profiles = new Vector<String>();
for (String id : items.keySet()) {
profiles.add(id);
}
Collections.sort(profiles, profileIdComparator);
return profiles;
}
private String getNewId() {
for (int i = 1;; i++) {
String test = Integer.toString(i);
if (!items.containsKey(test)) {
return test;
}
}
}
public int getProfileCount() {
return this.items.size();
}
public boolean sensorInActiveProfile(String sensorId) {
return sensorInProfile(sensorId, getActiveProfile());
}
public boolean sensorInProfile(String sensorId, Model profile) {
return getSensorProfileId(sensorId, profile) != null;
}
public String getSensorProfileIdInActiveProfile(String sensorId) {
return getSensorProfileId(sensorId, getActiveProfile());
}
public String getSensorProfileId(String sensorId, Model profile) {
if (sensorId != null && profile != null) {
for (Model profileSensor : profile.getModel("sensors", true).getModels()) {
if (sensorId.equals(profileSensor.getString("sensorid"))) {
return profileSensor.getString("id");
}
}
}
return null;
}
public Vector<String> getSensorsInActiveProfile() {
return getSensorsInProfile(getActiveProfile());
}
public Vector<String> getSensorsInProfile(Model profile) {
Vector<String> sensorIds = new Vector<String>();
if (profile != null) {
for (Model profileSensor : profile.getModel("sensors", true).getModels()) {
String sensorId = profileSensor.getString("sensorid", null);
if (sensorId != null) {
sensorIds.add(profileSensor.getString("sensorid"));
}
}
}
return sensorIds;
}
@Override
public void modelModified(Model model) {
super.modelModified(model);
if (model != null) {
Model profile = model.getRootParent();
String profileId = profile.getString("id", null);
if (profileId.equals(getActiveProfileId())) {
updateGlobalSensors();
}
listeners.fireEvent(profileId);
}
}
private void updateGlobalSensors() {
Model profile = getActiveProfile();
if (profile != null) {
boolean modified = false;
for (Model profileSensor : profile.getModel("sensors", true).getModels()) {
String sensorId = profileSensor.getString("sensorid");
Model profileSensorSettings = profileSensor.getModel("sensor_settings");
Model globalSensorSettings = SettingsManager.get().get("sensor:" + sensorId);
if (globalSensorSettings.copyPrimitives(profileSensorSettings, true)) {
modified = true;
}
}
if (modified) {
SettingsManager.get().modelModified(null);
}
}
}
private void sensorModified(String sensorId) {
Model profile = this.getActiveProfile();
if (profile != null) {
Model profileSensor = profile.getModel("sensors", true).getModel(sensorId, false);
if (profileSensor != null) {
Model globalSettings = SettingsManager.get().get("sensor:" + sensorId);
if (profileSensor.getModel("sensor_settings", true).copyPrimitives(globalSettings, true)) {
modelModified(null);
}
}
}
}
@Override
public void modelNotificationReceived(String msg) {
listeners.fireEvent("switch");
}
public void registerUIListener(ModelNotificationListener listener) {
listeners.addUIListener(listener);
}
public void unregisterUIListener(ModelNotificationListener listener) {
listeners.removeUIListener(listener);
}
public void registerDirectListener(ModelNotificationListener listener) {
listeners.addDirectListener(listener);
}
public void unregisterDirectListener(ModelNotificationListener listener) {
listeners.removeDirectListener(listener);
}
public void updateRemoteProfiles(JSONObject remoteData) {
try {
Iterator<?> projectIt = remoteData.keys();
while (projectIt.hasNext()) {
String jsonProjectId = (String) projectIt.next();
JSONArray jsonProjectArray = remoteData.getJSONArray(jsonProjectId);
for (int i = 0; i < jsonProjectArray.length(); i++) {
JSONObject jsonProfileContainerObj = jsonProjectArray.getJSONObject(i);
String jsonProfileTitle = jsonProfileContainerObj.getString("title");
JSONObject jsonProfileObj = jsonProfileContainerObj.getJSONObject("obj");
String jsonProfileId = jsonProfileObj.getString("id");
String profileId = String.format("r.%s.%s", jsonProjectId, jsonProfileId);
Model profile = get(profileId);
if (profile == null) {
profile = createEmptyProfile(profileId);
}
profile.setString("title", jsonProfileTitle, true);
profile.setBool("initial_edit", false);
profile.setBool("is_remote", true, true);
profile.setBool("requires_location", jsonProfileObj.getBoolean("geolocated"), true);
Model remoteInfo = profile.getModel("remote_info", true, true);
remoteInfo.setString("project", jsonProjectId);
remoteInfo.setString("profile", jsonProfileId);
profile.clear("sensors", true);
JSONArray jsonInputsArray = jsonProfileObj.getJSONArray("sensorInputs");
for (int j = 0; j < jsonInputsArray.length(); j++) {
JSONObject jsonInputObj = jsonInputsArray.getJSONObject(j);
String jsonInputId = jsonInputObj.getString("id");
double rate = jsonInputObj.getDouble("rate");
String sensorType = jsonInputObj.getString("sensor");
String sensorId = sensorType + ":0";
if (SensorWrapperManager.get().getSensors().containsKey(sensorId)) {
Model profileSensor = addSensor(profile, jsonInputId, sensorId, true);
profileSensor.setString("sensor_type", sensorType);
profileSensor.setDouble("sample_rate", rate, true);
}
}
}
}
this.forceSave();
listeners.fireEvent("list");
} catch (JSONException e) {
Log.d("stk remote update", e.getMessage());
e.printStackTrace();
}
}
@Override
public int getCurrentVersion() {
// 1: sensor sample period -> sample rate
return 1;
}
@Override
public void updateRootModel(String key, Model model, int version) {
if (version < 1) {
// sensor sample period -> sample rate
for (Model sensorModel : model.getModel("sensors", true).getModels()) {
if (sensorModel.containsKey("period")) {
if (!sensorModel.containsKey("sample_rate")) {
int period = sensorModel.getInt("period");
double rate = Math.min(period > 0 ? 1000 / period : 0, SenseItModelDefaults.DATA_LOGGING_RATE_MAX);
sensorModel.setDouble("sample_rate", rate);
sensorModel.setInt("sample_rate_ux", 0);
}
sensorModel.clear("period");
}
}
}
}
}