/*
* Copyright 2015 Daniel Dittmar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package dan.dit.whatsthat.achievement;
import java.util.HashMap;
import java.util.Map;
import dan.dit.whatsthat.util.compaction.CompactedDataCorruptException;
import dan.dit.whatsthat.util.compaction.Compacter;
/**
* A special AchievementData that maps keys to long values. Every
* change will notify the associated listeners if no silent changes are currently
* enabled and the update policy is met and an actual change happened.
* Created by daniel on 12.05.15.
*/
public class AchievementProperties extends AchievementData {
/**
* The update policy that means that the data is always set, no matter the previous data.
* This will still only notify listeners if the data changed.
*/
public static final long UPDATE_POLICY_ALWAYS = 0L;
private final Map<String, Long> mValues = new HashMap<>();
private boolean mSilentChangeMode;
private AchievementDataEvent mCurrEvent;
protected AchievementProperties(String name, Compacter data) throws CompactedDataCorruptException {
super(name);
unloadData(data);
}
protected boolean isSilentChangeMode() {
return mSilentChangeMode;
}
public void enableSilentChanges(int eventType) {
if (!mSilentChangeMode) {
mSilentChangeMode = true;
mCurrEvent = obtainNewEvent();
mCurrEvent.init(this, eventType, null);
}
}
public void disableSilentChanges() {
if (mSilentChangeMode) {
mSilentChangeMode = false;
AchievementDataEvent event = mCurrEvent;
mCurrEvent = null;
notifyListeners(event);
}
}
protected AchievementProperties(String name) {
super(name);
}
public boolean removeKey(String key) {
return key != null && mValues.remove(key) != null;
}
@Override
protected synchronized void resetData() {
mValues.clear();
notifyListeners(obtainNewEvent().initReset(this));
}
public synchronized void putValue(String key, Long value) {
putValue(key, value, UPDATE_POLICY_ALWAYS);
}
public synchronized void putValueIfBigger(String key, Long value) {
putValue(key, value, 1L);
}
public synchronized void putValueIfSmaller(String key, Long value) {
putValue(key, value, -1L);
}
/**
* Associates the given key with the given value. If the key is null this does nothing. If
* silent changes are currently not enabled and the value changed, listeners will be notified
* of the update. If given value is null, the association will be removed.
* @param key The key to use.
* @param value The value to associate to the key.
* @param requiredValueToOldDelta The required minimum delta to the old value that must
* exceeded for the value to update. If zero the
* value will always be updated, though not always notify
* listeners. If positive the new value needs to be bigger
* than the old value.
*/
public synchronized void putValue(String key, Long value, long requiredValueToOldDelta) {
if (key == null) {
return;
}
Long old;
if (value == null) {
old = mValues.remove(key);
} else {
old = mValues.put(key, value);
if (old != null) {
if (requiredValueToOldDelta > 0L) {
if (value - old < requiredValueToOldDelta) {
value = old;
mValues.put(key, value);
}
} else if (requiredValueToOldDelta < 0L) {
if (value - old > requiredValueToOldDelta) {
value = old;
mValues.put(key, value);
}
}
}
}
if (mCurrEvent != null) {
mCurrEvent.addChangedKey(key);
}
// if there previously was no value or the value changed, notify listeners
if (!mSilentChangeMode && ((value != null && old == null) || (old != null && !old.equals(value)))) {
AchievementDataEvent event = obtainNewEvent().init(this, AchievementDataEvent.EVENT_TYPE_DATA_UPDATE, key);
notifyListeners(event);
}
}
public synchronized Long increment(String key, long delta, long baseValue) {
if (key == null) {
return null;
}
Long value = mValues.get(key);
if (value == null) {
value = baseValue + delta;
} else {
value += delta;
}
mValues.put(key, value);
if (mCurrEvent != null) {
mCurrEvent.addChangedKey(key);
}
if (!mSilentChangeMode) {
AchievementDataEvent event = obtainNewEvent().init(this, AchievementDataEvent.EVENT_TYPE_DATA_UPDATE, key);
notifyListeners(event);
}
return value;
}
/**
* Returns the value saved for the given key. If there is no value associated to this key,
* the given default value will be returned instead.
* @param key The valid key to use.
* @param defaultValue The default value to use.
* @return The associated value or the default value.
*/
public synchronized Long getValue(String key, long defaultValue) {
Long value = mValues.get(key);
if (value == null) {
return defaultValue;
}
return value;
}
@Override
public String compact() {
Compacter cmp = new Compacter(mValues.size() * 2);
for (String key : mValues.keySet()) {
cmp.appendData(key);
cmp.appendData(mValues.get(key));
}
return cmp.compact();
}
@Override
public void unloadData(Compacter compactedData) throws CompactedDataCorruptException {
if (compactedData == null) {
return;
}
for (int i = 0; i + 1 < compactedData.getSize(); i+=2) {
mValues.put(compactedData.getData(i), compactedData.getLong(i + 1));
}
}
@Override
public String toString() {
return mValues.toString();
}
}