package com.kuxhausen.huemore.net; import android.util.Pair; import com.kuxhausen.huemore.state.BulbState; import com.kuxhausen.huemore.state.Event; import com.kuxhausen.huemore.state.Group; import com.kuxhausen.huemore.state.Mood; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * used to store activity data about an ongoing mood and format the data for consumption by * visualizations/notifications */ public class PlayingMood { private Mood mMood; private String mMoodName; private Group mGroup; /** * In elapsed realtime milliseconds */ private long mStartTime; /** * In elapsed realtime milliseconds */ private long mLastTickedTime; /** * @param startTime in elapsed realtime milliseconds (may be negative) * @param dayStartTime in elapsed realtime milliseconds (may be negative) */ public PlayingMood(Mood m, String moodName, Group g, long startTime, long dayStartTime, Long internalProgress) { if (m == null || g == null) { throw new IllegalArgumentException(); } mMood = m; if (moodName != null) { mMoodName = moodName; } else { mMoodName = "?"; } mGroup = g; if (m.getTimingPolicy() == Mood.TimingPolicy.DAILY) { mStartTime = dayStartTime; long[] lastTickedTimePerChannel = new long[m.getNumChannels()]; Arrays.fill(lastTickedTimePerChannel, dayStartTime - 1); for (int numCycles = -1; numCycles < 1; numCycles++) { for (Event e : m.getEvents()) { long adjustedEventTime = e.getMilliTime() + dayStartTime + (numCycles * m.getLoopMilliTime()); if (adjustedEventTime < lastTickedTimePerChannel[e.getChannel()]) { lastTickedTimePerChannel[e.getChannel()] = adjustedEventTime; } } } mLastTickedTime = dayStartTime - 1; for (long i : lastTickedTimePerChannel) { mLastTickedTime = Math.min(mLastTickedTime - 1, i); } } else { mStartTime = startTime; mLastTickedTime = startTime - 1; } if (internalProgress != null) { mLastTickedTime = internalProgress; } } public long getStartTime() { return mStartTime; } public long getInternalProgress() { return mLastTickedTime; } private List<Long> getChannelBulbIds(int channelNum) { ArrayList<Long> channel = new ArrayList<Long>(); List<Long> bulbBaseIds = mGroup.getNetworkBulbDatabaseIds(); for (int i = 0; i < bulbBaseIds.size(); i++) { if (i % mMood.getNumChannels() == channelNum) { channel.add(bulbBaseIds.get(i)); } } return channel; } public boolean hasFutureEvents() { if (mMood.getEvents().length == 0) { return false; } switch (mMood.getTimingPolicy()) { case DAILY: return true; case LOOPING: return true; } if ((mMood.getEvents()[mMood.getEvents().length - 1].getMilliTime() + mStartTime) > mLastTickedTime) { return true; } return false; } /** * @return anticipated tme of the next event, calculated in elapsed realtime milliseconds */ public long getNextEventInCurrentMillis() { if (!hasFutureEvents()) { throw new IllegalStateException(); } if (mMood.getTimingPolicy() == Mood.TimingPolicy.DAILY || mMood.getTimingPolicy() == Mood.TimingPolicy.LOOPING) { long cycleStart = mStartTime + ((mLastTickedTime - mStartTime) / mMood.getLoopMilliTime()) * mMood.getLoopMilliTime(); for (int numCycles = 0; numCycles < 2; numCycles++) { for (Event e : mMood.getEvents()) { if (e.getMilliTime() + cycleStart + (numCycles * mMood.getLoopMilliTime()) > mLastTickedTime) { return e.getMilliTime() + cycleStart + (numCycles * mMood.getLoopMilliTime()); } } } } else { for (Event e : mMood.getEvents()) { if (e.getMilliTime() + mStartTime > mLastTickedTime) { return e.getMilliTime() + mStartTime; } } } throw new IllegalStateException(); } /** * @param sinceTime in elapsed realtime milliseconds * @param throughTime in elapsed realtime milliseconds */ public List<Pair<List<Long>, BulbState>> getEventsSinceThrough(long sinceTime, long throughTime) { List<Pair<List<Long>, BulbState>> result = new ArrayList<Pair<List<Long>, BulbState>>(); if (mMood.getTimingPolicy() == Mood.TimingPolicy.DAILY) { int priorLoops = (int) Math.floor(((double) (sinceTime - mStartTime)) / mMood.getLoopMilliTime()); for (int numCycles = priorLoops; mStartTime + (numCycles * mMood.getLoopMilliTime()) <= throughTime; numCycles++) { for (Event e : mMood.getEvents()) { if (sinceTime < (e.getMilliTime() + mStartTime + (numCycles * mMood.getLoopMilliTime())) && (e.getMilliTime() + mStartTime + (numCycles * mMood.getLoopMilliTime())) <= throughTime) { result.add(new Pair<List<Long>, BulbState>(getChannelBulbIds(e.getChannel()), e.getBulbState())); } } } } else if (mMood.getTimingPolicy() == Mood.TimingPolicy.LOOPING) { int priorLoops = (int) Math.max(0, (sinceTime - mStartTime) / mMood.getLoopMilliTime()); for (int numCycles = priorLoops; mStartTime + (numCycles * mMood.getLoopMilliTime()) <= throughTime; numCycles++) { for (Event e : mMood.getEvents()) { if (sinceTime < (e.getMilliTime() + mStartTime + (numCycles * mMood.getLoopMilliTime())) && (e.getMilliTime() + mStartTime + (numCycles * mMood.getLoopMilliTime())) <= throughTime) { result.add(new Pair<List<Long>, BulbState>(getChannelBulbIds(e.getChannel()), e.getBulbState())); } } } } else { for (Event e : mMood.getEvents()) { if (sinceTime < (e.getMilliTime() + mStartTime) && (e.getMilliTime() + mStartTime) <= throughTime) { result.add( new Pair<List<Long>, BulbState>(getChannelBulbIds(e.getChannel()), e.getBulbState())); } } } return result; } /** * @param throughTime in elapsed realtime milliseconds */ public List<Pair<List<Long>, BulbState>> tick(long throughTime) { if (throughTime < mLastTickedTime) { throw new IllegalArgumentException(throughTime + ",'" + mLastTickedTime); } long sinceT = mLastTickedTime; long throughT = throughTime; mLastTickedTime = throughTime; return getEventsSinceThrough(sinceT, throughT); } public String getMoodName() { return mMoodName; } public String getGroupName() { return mGroup.getName(); } public String toString() { return getGroupName() + " \u2190 " + getMoodName(); } public Group getGroup() { return mGroup; } public Mood getMood() { return mMood; } }