package com.therandomlabs.utils.systemproperty;

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collection;
import com.therandomlabs.utils.collection.TRLList;
import static com.therandomlabs.utils.logging.Logging.getLogger;

public abstract class SystemProperty<E> {
	private final String key;
	private final boolean editable;

	protected SystemProperty(String key, boolean editable) {
		this.key = key;
		this.editable = editable;
		AllSystemProperties.properties.add(this);
	}

	@Override
	public String toString() {
		return String.valueOf(get());
	}

	public String getKey() {
		return key;
	}

	public final boolean isEditable() {
		return editable;
	}

	public boolean hasValue() {
		return getRaw() != null;
	}

	public boolean hasValidValue() {
		return getRaw() == null || get() != null;
	}

	public final String getRaw() {
		return getProperty(key);
	}

	public abstract E get();

	public final E set(E value) {
		if(!editable) {
			throw new UnsupportedOperationException("Cannot set value of uneditable property");
		}

		final E previous = get();
		setProperty(key, getAsRawString(value));
		return previous;
	}

	public void forceSet(String newValue) {
		setProperty(key, newValue);
	}

	public boolean valueEquals(E anotherValue) {
		final E value = get();

		if(value == null) {
			return anotherValue == null;
		}

		return value.equals(anotherValue);
	}

	public boolean stringEquals(String string) {
		return toString().equals(string);
	}

	protected String getAsRawString(E value) {
		return value.toString();
	}

	public static String getProperty(String key) {
		return getProperty(key, null);
	}

	public static String getProperty(String key, String defaultValue) {
		try {
			return AccessController.doPrivileged(
					(PrivilegedAction<String>) () -> System.getProperty(key, defaultValue)
			);
		} catch(SecurityException ex) {
			getLogger().severe("Could not retrieve system property: " + key);
			getLogger().printStackTrace(ex);
		}

		return null;
	}

	public static String setProperty(String key, String value) {
		try {
			return AccessController.doPrivileged(
					(PrivilegedAction<String>) () -> System.setProperty(key, value)
			);
		} catch(SecurityException ex) {
			getLogger().severe("Could not set system property: " + key);
			getLogger().printStackTrace(ex);
		}

		return null;
	}

	public static TRLList<String> getKeys(Collection<SystemProperty<?>> properties) {
		final TRLList<String> keys = new TRLList<>(properties.size());
		properties.forEach(property -> keys.add(property.getKey()));
		return keys;
	}
}
