/*
	Copyright 2012 to 2016 bigbiff/Dees_Troy TeamWin
	This file is part of TWRP/TeamWin Recovery Project.

	TWRP 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.

	TWRP 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 TWRP.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <pthread.h>
#include <time.h>
#include <string>
#include <sstream>
#include <fstream>
#include <cctype>
#include <cutils/properties.h>
#include <unistd.h>

#include "variables.h"
#include "data.hpp"
#include "partitions.hpp"
#include "twrp-functions.hpp"
#ifndef TW_NO_SCREEN_TIMEOUT
#include "gui/blanktimer.hpp"
#endif
#include "find_file.hpp"
#include "set_metadata.h"
#include "gui/gui.hpp"
#include "infomanager.hpp"

#define DEVID_MAX 64
#define HWID_MAX 32

extern "C"
{
	#include "twcommon.h"
	#include "gui/pages.h"
	void gui_notifyVarChange(const char *name, const char* value);
}
#include "minuitwrp/minui.h"

#define FILE_VERSION 0x00010010 // Do not set to 0

using namespace std;

string                                  DataManager::mBackingFile;
int                                     DataManager::mInitialized = 0;
InfoManager                             DataManager::mPersist;  // Data that that is not constant and will be saved to the settings file
InfoManager                             DataManager::mData;     // Data that is not constant and will not be saved to settings file
InfoManager                             DataManager::mConst;    // Data that is constant and will not be saved to settings file

extern bool datamedia;

#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
pthread_mutex_t DataManager::m_valuesLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
#else
pthread_mutex_t DataManager::m_valuesLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
#endif

// Device ID functions
void DataManager::sanitize_device_id(char* device_id) {
	const char* whitelist ="-._";
	char str[DEVID_MAX];
	char* c = str;

	snprintf(str, DEVID_MAX, "%s", device_id);
	memset(device_id, 0, strlen(device_id));
	while (*c) {
		if (isalnum(*c) || strchr(whitelist, *c))
			strncat(device_id, c, 1);
		c++;
	}
	return;
}

#define CMDLINE_SERIALNO		"androidboot.serialno="
#define CMDLINE_SERIALNO_LEN	(strlen(CMDLINE_SERIALNO))
#define CPUINFO_SERIALNO		"Serial"
#define CPUINFO_SERIALNO_LEN	(strlen(CPUINFO_SERIALNO))
#define CPUINFO_HARDWARE		"Hardware"
#define CPUINFO_HARDWARE_LEN	(strlen(CPUINFO_HARDWARE))

void DataManager::get_device_id(void) {
	FILE *fp;
	char line[2048];
	char hardware_id[HWID_MAX] = { 0 };
	char device_id[DEVID_MAX] = { 0 };
	char* token;

#ifdef TW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID
	// Use (product_model)_(hardware_id) as device id
	char model_id[PROPERTY_VALUE_MAX];
	property_get("ro.product.model", model_id, "error");
	if (strcmp(model_id, "error") != 0) {
		LOGINFO("=> product model: '%s'\n", model_id);
		// Replace spaces with underscores
		for (size_t i = 0; i < strlen(model_id); i++) {
			if (model_id[i] == ' ')
				model_id[i] = '_';
		}
		snprintf(device_id, DEVID_MAX, "%s", model_id);

		if (strlen(device_id) < DEVID_MAX) {
			fp = fopen("proc_cpuinfo.txt", "rt");
			if (fp != NULL) {
				while (fgets(line, sizeof(line), fp) != NULL) {
					if (memcmp(line, CPUINFO_HARDWARE,
							CPUINFO_HARDWARE_LEN) == 0) {
						// skip past "Hardware", spaces, and colon
						token = line + CPUINFO_HARDWARE_LEN;
						while (*token && (!isgraph(*token) || *token == ':'))
							token++;

						if (*token && *token != '\n'
								&& strcmp("UNKNOWN\n", token)) {
							snprintf(hardware_id, HWID_MAX, "%s", token);
							if (hardware_id[strlen(hardware_id)-1] == '\n')
								hardware_id[strlen(hardware_id)-1] = 0;
							LOGINFO("=> hardware id from cpuinfo: '%s'\n",
									hardware_id);
						}
						break;
					}
				}
				fclose(fp);
			}
		}

		if (hardware_id[0] != 0)
			snprintf(device_id, DEVID_MAX, "%s_%s", model_id, hardware_id);

		sanitize_device_id(device_id);
		mConst.SetValue("device_id", device_id);
		LOGINFO("=> using device id: '%s'\n", device_id);
		return;
	}
#endif

#ifndef TW_FORCE_CPUINFO_FOR_DEVICE_ID
	// Check the cmdline to see if the serial number was supplied
	fp = fopen("/proc/cmdline", "rt");
	if (fp != NULL) {
		fgets(line, sizeof(line), fp);
		fclose(fp); // cmdline is only one line long

		token = strtok(line, " ");
		while (token) {
			if (memcmp(token, CMDLINE_SERIALNO, CMDLINE_SERIALNO_LEN) == 0) {
				token += CMDLINE_SERIALNO_LEN;
				snprintf(device_id, DEVID_MAX, "%s", token);
				sanitize_device_id(device_id); // also removes newlines
				mConst.SetValue("device_id", device_id);
				return;
			}
			token = strtok(NULL, " ");
		}
	}
#endif
	// Check cpuinfo for serial number; if found, use as device_id
	// If serial number is not found, fallback to hardware_id for the device_id
	fp = fopen("/proc/cpuinfo", "rt");
	if (fp != NULL) {
		while (fgets(line, sizeof(line), fp) != NULL) {
			if (memcmp(line, CPUINFO_SERIALNO, CPUINFO_SERIALNO_LEN) == 0) {
				// skip past "Serial", spaces, and colon
				token = line + CPUINFO_SERIALNO_LEN;
				while (*token && (!isgraph(*token) || *token == ':'))
					token++;

				if (*token && *token != '\n') {
					snprintf(device_id, DEVID_MAX, "%s", token);
					sanitize_device_id(device_id); // also removes newlines
					LOGINFO("=> serial from cpuinfo: '%s'\n", device_id);
					mConst.SetValue("device_id", device_id);
					fclose(fp);
					return;
				}
			} else if (memcmp(line, CPUINFO_HARDWARE,
					CPUINFO_HARDWARE_LEN) == 0) {
				// skip past "Hardware", spaces, and colon
				token = line + CPUINFO_HARDWARE_LEN;
				while (*token && (!isgraph(*token) || *token == ':'))
					token++;

				if (*token && *token != '\n') {
					snprintf(hardware_id, HWID_MAX, "%s", token);
					if (hardware_id[strlen(hardware_id)-1] == '\n')
						hardware_id[strlen(hardware_id)-1] = 0;
					LOGINFO("=> hardware id from cpuinfo: '%s'\n", hardware_id);
				}
			}
		}
		fclose(fp);
	}

	if (hardware_id[0] != 0) {
		LOGINFO("\nusing hardware id for device id: '%s'\n", hardware_id);
		snprintf(device_id, DEVID_MAX, "%s", hardware_id);
		sanitize_device_id(device_id);
		mConst.SetValue("device_id", device_id);
		return;
	}

	strcpy(device_id, "serialno");
	LOGINFO("=> device id not found, using '%s'\n", device_id);
	mConst.SetValue("device_id", device_id);
	return;
}

int DataManager::ResetDefaults()
{
	pthread_mutex_lock(&m_valuesLock);
	mPersist.Clear();
	mData.Clear();
	mConst.Clear();
	pthread_mutex_unlock(&m_valuesLock);

	SetDefaultValues();
	return 0;
}

int DataManager::LoadValues(const string& filename)
{
	string dev_id;

	if (!mInitialized)
		SetDefaultValues();

	GetValue("device_id", dev_id);
	// Save off the backing file for set operations
	mBackingFile = filename;
	mPersist.SetFile(filename);
	mPersist.SetFileVersion(FILE_VERSION);

	// Read in the file, if possible
	pthread_mutex_lock(&m_valuesLock);
	mPersist.LoadValues();

#ifndef TW_NO_SCREEN_TIMEOUT
	blankTimer.setTime(mPersist.GetIntValue("tw_screen_timeout_secs"));
#endif

	pthread_mutex_unlock(&m_valuesLock);
	string current = GetCurrentStoragePath();
	TWPartition* Part = PartitionManager.Find_Partition_By_Path(current);
	if (!Part)
		Part = PartitionManager.Get_Default_Storage_Partition();
	if (Part && current != Part->Storage_Path && Part->Mount(false)) {
		LOGINFO("LoadValues setting storage path to '%s'\n", Part->Storage_Path.c_str());
		SetValue("tw_storage_path", Part->Storage_Path);
	} else {
		SetBackupFolder();
	}
	return 0;
}

int DataManager::LoadPersistValues(void)
{
	static bool loaded = false;
	string dev_id;

	// Only run this function once, and make sure normal settings file has not yet been read
	if (loaded || !mBackingFile.empty() || !TWFunc::Path_Exists(PERSIST_SETTINGS_FILE))
		return -1;

	LOGINFO("Attempt to load settings from /persist settings file...\n");

	if (!mInitialized)
		SetDefaultValues();

	GetValue("device_id", dev_id);
	mPersist.SetFile(PERSIST_SETTINGS_FILE);
	mPersist.SetFileVersion(FILE_VERSION);

	// Read in the file, if possible
	pthread_mutex_lock(&m_valuesLock);
	mPersist.LoadValues();

#ifndef TW_NO_SCREEN_TIMEOUT
	blankTimer.setTime(mPersist.GetIntValue("tw_screen_timeout_secs"));
#endif

	update_tz_environment_variables();
	TWFunc::Set_Brightness(GetStrValue("tw_brightness"));

	pthread_mutex_unlock(&m_valuesLock);

	/* Don't set storage nor backup paths this early */

	loaded = true;

	return 0;
}

int DataManager::Flush()
{
	return SaveValues();
}

int DataManager::SaveValues()
{
#ifndef TW_OEM_BUILD
	if (PartitionManager.Mount_By_Path("/persist", false)) {
		mPersist.SetFile(PERSIST_SETTINGS_FILE);
		mPersist.SetFileVersion(FILE_VERSION);
		pthread_mutex_lock(&m_valuesLock);
		mPersist.SaveValues();
		pthread_mutex_unlock(&m_valuesLock);
		LOGINFO("Saved settings file values to %s\n", PERSIST_SETTINGS_FILE);
	}

	if (mBackingFile.empty())
		return -1;

	string mount_path = GetSettingsStoragePath();
	PartitionManager.Mount_By_Path(mount_path.c_str(), 1);

	mPersist.SetFile(mBackingFile);
	mPersist.SetFileVersion(FILE_VERSION);
	pthread_mutex_lock(&m_valuesLock);
	mPersist.SaveValues();
	pthread_mutex_unlock(&m_valuesLock);

	tw_set_default_metadata(mBackingFile.c_str());
	LOGINFO("Saved settings file values to '%s'\n", mBackingFile.c_str());
#endif // ifdef TW_OEM_BUILD
	return 0;
}

int DataManager::GetValue(const string& varName, string& value)
{
	string localStr = varName;
	int ret = 0;

	if (!mInitialized)
		SetDefaultValues();

	// Strip off leading and trailing '%' if provided
	if (localStr.length() > 2 && localStr[0] == '%' && localStr[localStr.length()-1] == '%')
	{
		localStr.erase(0, 1);
		localStr.erase(localStr.length() - 1, 1);
	}

	// Handle magic values
	if (GetMagicValue(localStr, value) == 0)
		return 0;

	// Handle property
	if (localStr.length() > 9 && localStr.substr(0, 9) == "property.") {
		char property_value[PROPERTY_VALUE_MAX];
		property_get(localStr.substr(9).c_str(), property_value, "");
		value = property_value;
		return 0;
	}

	pthread_mutex_lock(&m_valuesLock);
	ret = mConst.GetValue(localStr, value);
	if (ret == 0)
		goto exit;

	ret = mPersist.GetValue(localStr, value);
	if (ret == 0)
		goto exit;

	ret = mData.GetValue(localStr, value);
exit:
	pthread_mutex_unlock(&m_valuesLock);
	return ret;
}

int DataManager::GetValue(const string& varName, int& value)
{
	string data;

	if (GetValue(varName,data) != 0)
		return -1;

	value = atoi(data.c_str());
	return 0;
}

int DataManager::GetValue(const string& varName, float& value)
{
	string data;

	if (GetValue(varName,data) != 0)
		return -1;

	value = atof(data.c_str());
	return 0;
}

int DataManager::GetValue(const string& varName, unsigned long long& value)
{
	string data;

	if (GetValue(varName,data) != 0)
		return -1;

	value = strtoull(data.c_str(), NULL, 10);
	return 0;
}

// This function will return an empty string if the value doesn't exist
string DataManager::GetStrValue(const string& varName)
{
	string retVal;

	GetValue(varName, retVal);
	return retVal;
}

// This function will return 0 if the value doesn't exist
int DataManager::GetIntValue(const string& varName)
{
	string retVal;

	GetValue(varName, retVal);
	return atoi(retVal.c_str());
}

int DataManager::SetValue(const string& varName, const string& value, const int persist /* = 0 */)
{
	if (!mInitialized)
		SetDefaultValues();

	// Handle property
	if (varName.length() > 9 && varName.substr(0, 9) == "property.") {
		int ret = property_set(varName.substr(9).c_str(), value.c_str());
		if (ret)
			LOGERR("Error setting property '%s' to '%s'\n", varName.substr(9).c_str(), value.c_str());
		return ret;
	}

	// Don't allow empty values or numerical starting values
	if (varName.empty() || (varName[0] >= '0' && varName[0] <= '9'))
		return -1;

	string test;
	pthread_mutex_lock(&m_valuesLock);
	int constChk = mConst.GetValue(varName, test);
	if (constChk == 0) {
		pthread_mutex_unlock(&m_valuesLock);
		return -1;
	}

	if (persist) {
		mPersist.SetValue(varName, value);
	} else {
		int persistChk = mPersist.GetValue(varName, test);
		if (persistChk == 0) {
			mPersist.SetValue(varName, value);
		} else {
			mData.SetValue(varName, value);
		}
	}

	pthread_mutex_unlock(&m_valuesLock);

#ifndef TW_NO_SCREEN_TIMEOUT
	if (varName == "tw_screen_timeout_secs") {
		blankTimer.setTime(atoi(value.c_str()));
	} else
#endif
	if (varName == "tw_storage_path") {
		SetBackupFolder();
	}
	gui_notifyVarChange(varName.c_str(), value.c_str());
	return 0;
}

int DataManager::SetValue(const string& varName, const int value, const int persist /* = 0 */)
{
	ostringstream valStr;
	valStr << value;
	return SetValue(varName, valStr.str(), persist);
}

int DataManager::SetValue(const string& varName, const float value, const int persist /* = 0 */)
{
	ostringstream valStr;
	valStr << value;
	return SetValue(varName, valStr.str(), persist);;
}

int DataManager::SetValue(const string& varName, const unsigned long long& value, const int persist /* = 0 */)
{
	ostringstream valStr;
	valStr << value;
	return SetValue(varName, valStr.str(), persist);
}

int DataManager::SetProgress(const float Fraction) {
	return SetValue("ui_progress", (float) (Fraction * 100.0));
}

int DataManager::ShowProgress(const float Portion, const float Seconds)
{
	float Starting_Portion;
	GetValue("ui_progress_portion", Starting_Portion);
	if (SetValue("ui_progress_portion", (float)(Portion * 100.0) + Starting_Portion) != 0)
		return -1;
	if (SetValue("ui_progress_frames", Seconds * 30) != 0)
		return -1;
	return 0;
}

void DataManager::update_tz_environment_variables(void)
{
	setenv("TZ", GetStrValue(TW_TIME_ZONE_VAR).c_str(), 1);
	tzset();
}

void DataManager::SetBackupFolder()
{
	string str = GetCurrentStoragePath();
	TWPartition* partition = PartitionManager.Find_Partition_By_Path(str);
	str += "/TWRP/BACKUPS/";

	string dev_id;
	GetValue("device_id", dev_id);

	str += dev_id;
	LOGINFO("Backup folder set to '%s'\n", str.c_str());
	SetValue(TW_BACKUPS_FOLDER_VAR, str, 0);
	if (partition != NULL) {
		SetValue("tw_storage_display_name", partition->Storage_Name);
		char free_space[255];
		sprintf(free_space, "%llu", partition->Free / 1024 / 1024);
		SetValue("tw_storage_free_size", free_space);
		string zip_path, zip_root, storage_path;
		GetValue(TW_ZIP_LOCATION_VAR, zip_path);
		if (partition->Has_Data_Media && !partition->Symlink_Mount_Point.empty())
			storage_path = partition->Symlink_Mount_Point;
		else
			storage_path = partition->Storage_Path;
		if (zip_path.size() < storage_path.size()) {
			SetValue(TW_ZIP_LOCATION_VAR, storage_path);
		} else {
			zip_root = TWFunc::Get_Root_Path(zip_path);
			if (zip_root != storage_path) {
				LOGINFO("DataManager::SetBackupFolder zip path was %s changing to %s, %s\n", zip_path.c_str(), storage_path.c_str(), zip_root.c_str());
				SetValue(TW_ZIP_LOCATION_VAR, storage_path);
			}
		}
	} else {
		if (PartitionManager.Fstab_Processed() != 0) {
			LOGINFO("Storage partition '%s' not found\n", str.c_str());
			gui_err("unable_locate_storage=Unable to locate storage device.");
		}
	}
}

void DataManager::SetDefaultValues()
{
	string str, path;

	mConst.SetConst();

	get_device_id();

	pthread_mutex_lock(&m_valuesLock);

	mInitialized = 1;

	mConst.SetValue("true", "1");
	mConst.SetValue("false", "0");

	mConst.SetValue(TW_VERSION_VAR, TW_VERSION_STR);
	mPersist.SetValue("tw_button_vibrate", "80");
	mPersist.SetValue("tw_keyboard_vibrate", "40");
	mPersist.SetValue("tw_action_vibrate", "160");

	TWPartition *store = PartitionManager.Get_Default_Storage_Partition();
	if (store)
		mPersist.SetValue("tw_storage_path", store->Storage_Path);
	else
		mPersist.SetValue("tw_storage_path", "/");

#ifdef TW_FORCE_CPUINFO_FOR_DEVICE_ID
	printf("TW_FORCE_CPUINFO_FOR_DEVICE_ID := true\n");
#endif

#ifdef BOARD_HAS_NO_REAL_SDCARD
	printf("BOARD_HAS_NO_REAL_SDCARD := true\n");
	mConst.SetValue(TW_ALLOW_PARTITION_SDCARD, "0");
#else
	mConst.SetValue(TW_ALLOW_PARTITION_SDCARD, "1");
#endif

#ifdef TW_INCLUDE_DUMLOCK
	printf("TW_INCLUDE_DUMLOCK := true\n");
	mConst.SetValue(TW_SHOW_DUMLOCK, "1");
#else
	mConst.SetValue(TW_SHOW_DUMLOCK, "0");
#endif

	str = GetCurrentStoragePath();
	mPersist.SetValue(TW_ZIP_LOCATION_VAR, str);
	str += "/TWRP/BACKUPS/";

	string dev_id;
	mConst.GetValue("device_id", dev_id);

	str += dev_id;
	mData.SetValue(TW_BACKUPS_FOLDER_VAR, str);

	mConst.SetValue(TW_REBOOT_SYSTEM, "1");
#ifdef TW_NO_REBOOT_RECOVERY
	printf("TW_NO_REBOOT_RECOVERY := true\n");
	mConst.SetValue(TW_REBOOT_RECOVERY, "0");
#else
	mConst.SetValue(TW_REBOOT_RECOVERY, "1");
#endif
	mConst.SetValue(TW_REBOOT_POWEROFF, "1");
#ifdef TW_NO_REBOOT_BOOTLOADER
	printf("TW_NO_REBOOT_BOOTLOADER := true\n");
	mConst.SetValue(TW_REBOOT_BOOTLOADER, "0");
#else
	mConst.SetValue(TW_REBOOT_BOOTLOADER, "1");
#endif
#ifdef RECOVERY_SDCARD_ON_DATA
	printf("RECOVERY_SDCARD_ON_DATA := true\n");
	mConst.SetValue(TW_HAS_DATA_MEDIA, "1");
	datamedia = true;
#else
	mData.SetValue(TW_HAS_DATA_MEDIA, "0");
#endif
#ifdef TW_NO_BATT_PERCENT
	printf("TW_NO_BATT_PERCENT := true\n");
	mConst.SetValue(TW_NO_BATTERY_PERCENT, "1");
#else
	mConst.SetValue(TW_NO_BATTERY_PERCENT, "0");
#endif
#ifdef TW_NO_CPU_TEMP
	printf("TW_NO_CPU_TEMP := true\n");
	mConst.SetValue("tw_no_cpu_temp", "1");
#else
	string cpu_temp_file;
#ifdef TW_CUSTOM_CPU_TEMP_PATH
	cpu_temp_file = EXPAND(TW_CUSTOM_CPU_TEMP_PATH);
#else
	cpu_temp_file = "/sys/class/thermal/thermal_zone0/temp";
#endif
	if (TWFunc::Path_Exists(cpu_temp_file)) {
		mConst.SetValue("tw_no_cpu_temp", "0");
	} else {
		LOGINFO("CPU temperature file '%s' not found, disabling CPU temp.\n", cpu_temp_file.c_str());
		mConst.SetValue("tw_no_cpu_temp", "1");
	}
#endif
#ifdef TW_CUSTOM_POWER_BUTTON
	printf("TW_POWER_BUTTON := %s\n", EXPAND(TW_CUSTOM_POWER_BUTTON));
	mConst.SetValue(TW_POWER_BUTTON, EXPAND(TW_CUSTOM_POWER_BUTTON));
#else
	mConst.SetValue(TW_POWER_BUTTON, "0");
#endif
#ifdef TW_ALWAYS_RMRF
	printf("TW_ALWAYS_RMRF := true\n");
	mConst.SetValue(TW_RM_RF_VAR, "1");
#endif
#ifdef TW_NEVER_UNMOUNT_SYSTEM
	printf("TW_NEVER_UNMOUNT_SYSTEM := true\n");
	mConst.SetValue(TW_DONT_UNMOUNT_SYSTEM, "1");
#else
	mConst.SetValue(TW_DONT_UNMOUNT_SYSTEM, "0");
#endif
#ifdef TW_NO_USB_STORAGE
	printf("TW_NO_USB_STORAGE := true\n");
	mConst.SetValue(TW_HAS_USB_STORAGE, "0");
#else
	char lun_file[255];
	string Lun_File_str = CUSTOM_LUN_FILE;
	size_t found = Lun_File_str.find("%");
	if (found != string::npos) {
		sprintf(lun_file, CUSTOM_LUN_FILE, 0);
		Lun_File_str = lun_file;
	}
	if (!TWFunc::Path_Exists(Lun_File_str)) {
		LOGINFO("Lun file '%s' does not exist, USB storage mode disabled\n", Lun_File_str.c_str());
		mConst.SetValue(TW_HAS_USB_STORAGE, "0");
	} else {
		LOGINFO("Lun file '%s'\n", Lun_File_str.c_str());
		mData.SetValue(TW_HAS_USB_STORAGE, "1");
	}
#endif
#ifdef TW_INCLUDE_INJECTTWRP
	printf("TW_INCLUDE_INJECTTWRP := true\n");
	mConst.SetValue(TW_HAS_INJECTTWRP, "1");
	mPersist(TW_INJECT_AFTER_ZIP, "1");
#else
	mConst.SetValue(TW_HAS_INJECTTWRP, "0");
#endif
#ifdef TW_HAS_DOWNLOAD_MODE
	printf("TW_HAS_DOWNLOAD_MODE := true\n");
	mConst.SetValue(TW_DOWNLOAD_MODE, "1");
#endif
#ifdef TW_INCLUDE_CRYPTO
	mConst.SetValue(TW_HAS_CRYPTO, "1");
	printf("TW_INCLUDE_CRYPTO := true\n");
#endif
#ifdef TW_SDEXT_NO_EXT4
	printf("TW_SDEXT_NO_EXT4 := true\n");
	mConst.SetValue(TW_SDEXT_DISABLE_EXT4, "1");
#else
	mConst.SetValue(TW_SDEXT_DISABLE_EXT4, "0");
#endif

#ifdef TW_HAS_NO_BOOT_PARTITION
	mPersist.SetValue("tw_backup_list", "/system;/data;");
#else
	mPersist.SetValue("tw_backup_list", "/system;/data;/boot;");
#endif
	mConst.SetValue(TW_MIN_SYSTEM_VAR, TW_MIN_SYSTEM_SIZE);
	mData.SetValue(TW_BACKUP_NAME, "(Auto Generate)");

	mPersist.SetValue(TW_INSTALL_REBOOT_VAR, "0");
	mPersist.SetValue(TW_SIGNED_ZIP_VERIFY_VAR, "0");
	mPersist.SetValue(TW_DISABLE_FREE_SPACE_VAR, "0");
	mPersist.SetValue(TW_FORCE_DIGEST_CHECK_VAR, "0");
	mPersist.SetValue(TW_USE_COMPRESSION_VAR, "0");
	mPersist.SetValue(TW_TIME_ZONE_VAR, "CST6CDT,M3.2.0,M11.1.0");
	mPersist.SetValue(TW_GUI_SORT_ORDER, "1");
	mPersist.SetValue(TW_RM_RF_VAR, "0");
	mPersist.SetValue(TW_SKIP_DIGEST_CHECK_VAR, "0");
	mPersist.SetValue(TW_SKIP_DIGEST_GENERATE_VAR, "0");
	mPersist.SetValue(TW_SDEXT_SIZE, "0");
	mPersist.SetValue(TW_SWAP_SIZE, "0");
	mPersist.SetValue(TW_SDPART_FILE_SYSTEM, "ext3");
	mPersist.SetValue(TW_TIME_ZONE_GUISEL, "CST6;CDT,M3.2.0,M11.1.0");
	mPersist.SetValue(TW_TIME_ZONE_GUIOFFSET, "0");
	mPersist.SetValue(TW_TIME_ZONE_GUIDST, "1");
	mData.SetValue(TW_ACTION_BUSY, "0");
	mData.SetValue("tw_wipe_cache", "0");
	mData.SetValue("tw_wipe_dalvik", "0");
	mData.SetValue(TW_ZIP_INDEX, "0");
	mData.SetValue(TW_ZIP_QUEUE_COUNT, "0");
	mData.SetValue(TW_FILENAME, "/sdcard");
	mData.SetValue(TW_SIMULATE_ACTIONS, "0");
	mData.SetValue(TW_SIMULATE_FAIL, "0");
	mData.SetValue(TW_IS_ENCRYPTED, "0");
	mData.SetValue(TW_IS_DECRYPTED, "0");
	mData.SetValue(TW_CRYPTO_PASSWORD, "0");
	mData.SetValue("tw_terminal_state", "0");
	mData.SetValue("tw_background_thread_running", "0");
	mData.SetValue(TW_RESTORE_FILE_DATE, "0");
	mPersist.SetValue("tw_military_time", "0");

#ifdef TW_INCLUDE_CRYPTO
	mPersist.SetValue(TW_USE_SHA2, "1");
	mPersist.SetValue(TW_NO_SHA2, "0");
#else
	mPersist.SetValue(TW_NO_SHA2, "1");
#endif

#ifdef TW_NO_SCREEN_TIMEOUT
	mConst.SetValue("tw_screen_timeout_secs", "0");
	mConst.SetValue("tw_no_screen_timeout", "1");
#else
	mPersist.SetValue("tw_screen_timeout_secs", "60");
	mPersist.SetValue("tw_no_screen_timeout", "0");
#endif
	mData.SetValue("tw_gui_done", "0");
	mData.SetValue("tw_encrypt_backup", "0");
	mData.SetValue("tw_sleep_total", "5");
	mData.SetValue("tw_sleep", "5");

	// Brightness handling
	string findbright;
#ifdef TW_BRIGHTNESS_PATH
	findbright = EXPAND(TW_BRIGHTNESS_PATH);
	LOGINFO("TW_BRIGHTNESS_PATH := %s\n", findbright.c_str());
	if (!TWFunc::Path_Exists(findbright)) {
		LOGINFO("Specified brightness file '%s' not found.\n", findbright.c_str());
		findbright = "";
	}
#endif
	if (findbright.empty()) {
		// Attempt to locate the brightness file
		findbright = Find_File::Find("brightness", "/sys/class/backlight");
		if (findbright.empty()) findbright = Find_File::Find("brightness", "/sys/class/leds/lcd-backlight");
	}
	if (findbright.empty()) {
		LOGINFO("Unable to locate brightness file\n");
		mConst.SetValue("tw_has_brightnesss_file", "0");
	} else {
		LOGINFO("Found brightness file at '%s'\n", findbright.c_str());
		mConst.SetValue("tw_has_brightnesss_file", "1");
		mConst.SetValue("tw_brightness_file", findbright);
		string maxBrightness;
#ifdef TW_MAX_BRIGHTNESS
		ostringstream maxVal;
		maxVal << TW_MAX_BRIGHTNESS;
		maxBrightness = maxVal.str();
#else
		// Attempt to locate the max_brightness file
		string maxbrightpath = findbright.insert(findbright.rfind('/') + 1, "max_");
		if (TWFunc::Path_Exists(maxbrightpath)) {
			ifstream maxVal(maxbrightpath.c_str());
			if (maxVal >> maxBrightness) {
				LOGINFO("Got max brightness %s from '%s'\n", maxBrightness.c_str(), maxbrightpath.c_str());
			} else {
				// Something went wrong, set that to indicate error
				maxBrightness = "-1";
			}
		}
		if (atoi(maxBrightness.c_str()) <= 0)
		{
			// Fallback into default
			ostringstream maxVal;
			maxVal << 255;
			maxBrightness = maxVal.str();
		}
#endif
		mConst.SetValue("tw_brightness_max", maxBrightness);
		mPersist.SetValue("tw_brightness", maxBrightness);
		mPersist.SetValue("tw_brightness_pct", "100");
#ifdef TW_SECONDARY_BRIGHTNESS_PATH
		string secondfindbright = EXPAND(TW_SECONDARY_BRIGHTNESS_PATH);
		if (secondfindbright != "" && TWFunc::Path_Exists(secondfindbright)) {
			LOGINFO("Will use a second brightness file at '%s'\n", secondfindbright.c_str());
			mConst.SetValue("tw_secondary_brightness_file", secondfindbright);
		} else {
			LOGINFO("Specified secondary brightness file '%s' not found.\n", secondfindbright.c_str());
		}
#endif
#ifdef TW_DEFAULT_BRIGHTNESS
		int defValInt = TW_DEFAULT_BRIGHTNESS;
		int maxValInt = atoi(maxBrightness.c_str());
		// Deliberately int so the % is always a whole number
		int defPctInt = ( ( (double)defValInt / maxValInt ) * 100 );
		ostringstream defPct;
		defPct << defPctInt;
		mPersist.SetValue("tw_brightness_pct", defPct.str());

		ostringstream defVal;
		defVal << TW_DEFAULT_BRIGHTNESS;
		mPersist.SetValue("tw_brightness", defVal.str());
		TWFunc::Set_Brightness(defVal.str());
#else
		TWFunc::Set_Brightness(maxBrightness);
#endif
	}

#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
	mConst.SetValue("tw_include_encrypted_backup", "1");
#else
	LOGINFO("TW_EXCLUDE_ENCRYPTED_BACKUPS := true\n");
	mConst.SetValue("tw_include_encrypted_backup", "0");
#endif
#ifdef TW_HAS_MTP
	mConst.SetValue("tw_has_mtp", "1");
	mPersist.SetValue("tw_mtp_enabled", "1");
	mPersist.SetValue("tw_mtp_debug", "0");
#else
	LOGINFO("TW_EXCLUDE_MTP := true\n");
	mConst.SetValue("tw_has_mtp", "0");
	mConst.SetValue("tw_mtp_enabled", "0");
#endif
	mPersist.SetValue("tw_mount_system_ro", "2");
	mPersist.SetValue("tw_never_show_system_ro_page", "0");
	mPersist.SetValue("tw_language", EXPAND(TW_DEFAULT_LANGUAGE));
	LOGINFO("LANG: %s\n", EXPAND(TW_DEFAULT_LANGUAGE));

	mData.SetValue("tw_has_adopted_storage", "0");

#ifdef AB_OTA_UPDATER
	LOGINFO("AB_OTA_UPDATER := true\n");
	mConst.SetValue("tw_has_boot_slots", "1");
#else
	mConst.SetValue("tw_has_boot_slots", "0");
#endif

#ifdef TW_NO_LEGACY_PROPS
	LOGINFO("TW_NO_LEGACY_PROPS := true\n");
#endif

#ifdef TW_OEM_BUILD
	LOGINFO("TW_OEM_BUILD := true\n");
	mConst.SetValue("tw_oem_build", "1");
#else
	mConst.SetValue("tw_oem_build", "0");
	mPersist.SetValue("tw_app_prompt", "1");
	mPersist.SetValue("tw_app_install_system", "1");
	mData.SetValue("tw_app_install_status", "0"); // 0 = no status, 1 = not installed, 2 = already installed
#endif

        mData.SetValue("tw_enable_adb_backup", "0");

	pthread_mutex_unlock(&m_valuesLock);
}

// Magic Values
int DataManager::GetMagicValue(const string& varName, string& value)
{
	// Handle special dynamic cases
	if (varName == "tw_time")
	{
		char tmp[32];

		struct tm *current;
		time_t now;
		int tw_military_time;
		now = time(0);
		current = localtime(&now);
		GetValue(TW_MILITARY_TIME, tw_military_time);
		if (current->tm_hour >= 12)
		{
			if (tw_military_time == 1)
				sprintf(tmp, "%d:%02d", current->tm_hour, current->tm_min);
			else
				sprintf(tmp, "%d:%02d PM", current->tm_hour == 12 ? 12 : current->tm_hour - 12, current->tm_min);
		}
		else
		{
			if (tw_military_time == 1)
				sprintf(tmp, "%d:%02d", current->tm_hour, current->tm_min);
			else
				sprintf(tmp, "%d:%02d AM", current->tm_hour == 0 ? 12 : current->tm_hour, current->tm_min);
		}
		value = tmp;
		return 0;
	}
	else if (varName == "tw_cpu_temp")
	{
		int tw_no_cpu_temp;
		GetValue("tw_no_cpu_temp", tw_no_cpu_temp);
		if (tw_no_cpu_temp == 1) return -1;

		string cpu_temp_file;
		static unsigned long convert_temp = 0;
		static time_t cpuSecCheck = 0;
		struct timeval curTime;
		string results;

		gettimeofday(&curTime, NULL);
		if (curTime.tv_sec > cpuSecCheck)
		{
#ifdef TW_CUSTOM_CPU_TEMP_PATH
			cpu_temp_file = EXPAND(TW_CUSTOM_CPU_TEMP_PATH);
			if (TWFunc::read_file(cpu_temp_file, results) != 0)
				return -1;
#else
			cpu_temp_file = "/sys/class/thermal/thermal_zone0/temp";
			if (TWFunc::read_file(cpu_temp_file, results) != 0)
				return -1;
#endif
			convert_temp = strtoul(results.c_str(), NULL, 0) / 1000;
			if (convert_temp <= 0)
				convert_temp = strtoul(results.c_str(), NULL, 0);
			if (convert_temp >= 150)
				convert_temp = strtoul(results.c_str(), NULL, 0) / 10;
			cpuSecCheck = curTime.tv_sec + 5;
		}
		value = TWFunc::to_string(convert_temp);
		return 0;
	}
	else if (varName == "tw_battery")
	{
		char tmp[16];
		static char charging = ' ';
		static int lastVal = -1;
		static time_t nextSecCheck = 0;
		struct timeval curTime;
		gettimeofday(&curTime, NULL);
		if (curTime.tv_sec > nextSecCheck)
		{
			char cap_s[4];
#ifdef TW_CUSTOM_BATTERY_PATH
			string capacity_file = EXPAND(TW_CUSTOM_BATTERY_PATH);
			capacity_file += "/capacity";
			FILE * cap = fopen(capacity_file.c_str(),"rt");
#else
			FILE * cap = fopen("/sys/class/power_supply/battery/capacity","rt");
#endif
			if (cap) {
				fgets(cap_s, 4, cap);
				fclose(cap);
				lastVal = atoi(cap_s);
				if (lastVal > 100)	lastVal = 101;
				if (lastVal < 0)	lastVal = 0;
			}
#ifdef TW_CUSTOM_BATTERY_PATH
			string status_file = EXPAND(TW_CUSTOM_BATTERY_PATH);
			status_file += "/status";
			cap = fopen(status_file.c_str(),"rt");
#else
			cap = fopen("/sys/class/power_supply/battery/status","rt");
#endif
			if (cap) {
				fgets(cap_s, 2, cap);
				fclose(cap);
				if (cap_s[0] == 'C')
					charging = '+';
				else
					charging = ' ';
			}
			nextSecCheck = curTime.tv_sec + 60;
		}

		sprintf(tmp, "%i%%%c", lastVal, charging);
		value = tmp;
		return 0;
	}
	return -1;
}

void DataManager::Output_Version(void)
{
#ifndef TW_OEM_BUILD
	string Path;
	char version[255];

	if (!PartitionManager.Mount_By_Path("/cache", false)) {
		LOGINFO("Unable to mount '%s' to write version number.\n", Path.c_str());
		return;
	}
	if (!TWFunc::Path_Exists("/cache/recovery/.")) {
		LOGINFO("Recreating /cache/recovery folder.\n");
		if (mkdir("/cache/recovery", S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP) != 0) {
			LOGERR("DataManager::Output_Version -- Unable to make /cache/recovery\n");
			return;
		}
	}
	Path = "/cache/recovery/.version";
	if (TWFunc::Path_Exists(Path)) {
		unlink(Path.c_str());
	}
	FILE *fp = fopen(Path.c_str(), "w");
	if (fp == NULL) {
		gui_msg(Msg(msg::kError, "error_opening_strerr=Error opening: '{1}' ({2})")(Path)(strerror(errno)));
		return;
	}
	strcpy(version, TW_VERSION_STR);
	fwrite(version, sizeof(version[0]), strlen(version) / sizeof(version[0]), fp);
	fclose(fp);
	TWFunc::copy_file("/etc/recovery.fstab", "/cache/recovery/recovery.fstab", 0644);
	PartitionManager.Output_Storage_Fstab();
	sync();
	LOGINFO("Version number saved to '%s'\n", Path.c_str());
#endif
}

void DataManager::ReadSettingsFile(void)
{
#ifndef TW_OEM_BUILD
	// Load up the values for TWRP - Sleep to let the card be ready
	char mkdir_path[255], settings_file[255];
	int is_enc, has_data_media;

	GetValue(TW_IS_ENCRYPTED, is_enc);
	GetValue(TW_HAS_DATA_MEDIA, has_data_media);
	if (is_enc == 1 && has_data_media == 1) {
		LOGINFO("Cannot load settings -- encrypted.\n");
		return;
	}

	memset(mkdir_path, 0, sizeof(mkdir_path));
	memset(settings_file, 0, sizeof(settings_file));
	sprintf(mkdir_path, "%s/TWRP", GetSettingsStoragePath().c_str());
	sprintf(settings_file, "%s/.twrps", mkdir_path);

	if (!PartitionManager.Mount_Settings_Storage(false))
	{
		usleep(500000);
		if (!PartitionManager.Mount_Settings_Storage(false))
			gui_msg(Msg(msg::kError, "unable_to_mount=Unable to mount {1}")(settings_file));
	}

	mkdir(mkdir_path, 0777);

	LOGINFO("Attempt to load settings from settings file...\n");
	LoadValues(settings_file);
	Output_Version();
#endif // ifdef TW_OEM_BUILD
	PartitionManager.Mount_All_Storage();
	update_tz_environment_variables();
	TWFunc::Set_Brightness(GetStrValue("tw_brightness"));
}

string DataManager::GetCurrentStoragePath(void)
{
	return GetStrValue("tw_storage_path");
}

string DataManager::GetSettingsStoragePath(void)
{
	return GetStrValue("tw_settings_path");
}

void DataManager::Vibrate(const string& varName)
{
	int vib_value = 0;
	GetValue(varName, vib_value);
	if (vib_value) {
		vibrate(vib_value);
	}
}
