/*
* ShootOFF - Software for Laser Dry Fire Training
* Copyright (C) 2016 phrack
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.shootoff.session;
import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import com.shootoff.camera.shot.DisplayShot;
import com.shootoff.targets.Target;
import javafx.geometry.Dimension2D;
import javafx.geometry.Point2D;
public class SessionRecorder {
private final long startTime;
private final String sessionName;
private final Map<String, List<Event>> events = new HashMap<>();
private final Map<String, Set<Target>> seenTargets = new HashMap<>();
private final AtomicBoolean ignoreTargetCheck = new AtomicBoolean(false);
public SessionRecorder() {
final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH.mm.ss");
sessionName = dateFormat.format(new Date());
startTime = System.currentTimeMillis();
}
public void addEvents(Map<String, List<Event>> events) {
this.events.putAll(events);
}
public Map<String, List<Event>> getEvents() {
return events;
}
public String getSessionName() {
return sessionName;
}
public List<Event> getCameraEvents(String cameraName) {
if (events.containsKey(cameraName)) {
return events.get(cameraName);
} else {
final List<Event> eventList = new ArrayList<>();
events.put(cameraName, eventList);
return eventList;
}
}
// This method ensures we have an add event for a target that is being used,
// if not the user started recording after adding the target so we should
// artificially add the target add event then ensure it gets moved and
// resized
// to wherever it already is and to however big it already is.
private void checkTarget(String cameraName, Target target) {
if (!seenTargets.containsKey(cameraName)) {
seenTargets.put(cameraName, new HashSet<Target>());
}
if (!seenTargets.get(cameraName).contains(target)) {
ignoreTargetCheck.set(true);
recordTargetAdded(cameraName, target);
final Point2D p = target.getPosition();
recordTargetMoved(cameraName, target, (int) p.getX(), (int) p.getY());
final Dimension2D d = target.getDimension();
recordTargetResized(cameraName, target, d.getWidth(), d.getHeight());
ignoreTargetCheck.set(false);
}
}
public void recordShot(String cameraName, DisplayShot shot, boolean isMalfunction, boolean isReload,
Optional<Target> target, Optional<Integer> hitRegionIndex, Optional<String> videoString) {
Optional<Integer> targetIndex = Optional.empty();
if (target.isPresent()) {
targetIndex = Optional.of(target.get().getTargetIndex());
if (!ignoreTargetCheck.get()) checkTarget(cameraName, target.get());
}
final long timestamp = System.currentTimeMillis() - startTime;
getCameraEvents(cameraName).add(new ShotEvent(cameraName, timestamp, shot, isMalfunction, isReload, targetIndex,
hitRegionIndex, videoString));
}
public void recordTargetAdded(String cameraName, Target target) {
if (!seenTargets.containsKey(cameraName)) {
seenTargets.put(cameraName, new HashSet<Target>());
}
seenTargets.get(cameraName).add(target);
String targetName;
if (target.getTargetFile().isAbsolute()) {
targetName = target.getTargetFile().getPath()
.replace(System.getProperty("shootoff.home") + File.separator + "targets" + File.separator, "");
} else {
targetName = target.getTargetFile().getPath().replace("targets" + File.separator, "");
}
getCameraEvents(cameraName)
.add(new TargetAddedEvent(cameraName, System.currentTimeMillis() - startTime, targetName));
}
public void recordTargetRemoved(String cameraName, Target target) {
if (!ignoreTargetCheck.get()) checkTarget(cameraName, target);
getCameraEvents(cameraName).add(
new TargetRemovedEvent(cameraName, System.currentTimeMillis() - startTime, target.getTargetIndex()));
}
private void collapseTargetEvents(String cameraName, EventType type, Target target) {
final ListIterator<Event> it = getCameraEvents(cameraName).listIterator(getCameraEvents(cameraName).size());
while (it.hasPrevious()) {
final Event e = it.previous();
if (e.getType() != EventType.TARGET_RESIZED && e.getType() != EventType.TARGET_MOVED) {
break;
}
if (e.getType() == type) {
if (type == EventType.TARGET_RESIZED
&& ((TargetResizedEvent) e).getTargetIndex() == target.getTargetIndex()) {
it.remove();
} else if (type == EventType.TARGET_MOVED
&& ((TargetMovedEvent) e).getTargetIndex() == target.getTargetIndex()) {
it.remove();
}
}
}
}
public void recordTargetResized(String cameraName, Target target, double newWidth, double newHeight) {
if (!ignoreTargetCheck.get()) checkTarget(cameraName, target);
// Remove all resize events immediately before this one
collapseTargetEvents(cameraName, EventType.TARGET_RESIZED, target);
getCameraEvents(cameraName).add(new TargetResizedEvent(cameraName, System.currentTimeMillis() - startTime,
target.getTargetIndex(), newWidth, newHeight));
}
public void recordTargetMoved(String cameraName, Target target, int newX, int newY) {
if (!ignoreTargetCheck.get()) checkTarget(cameraName, target);
// Remove all move events immediately before this one
collapseTargetEvents(cameraName, EventType.TARGET_MOVED, target);
getCameraEvents(cameraName).add(new TargetMovedEvent(cameraName, System.currentTimeMillis() - startTime,
target.getTargetIndex(), newX, newY));
}
public void recordExerciseFeedMessage(String message) {
// Add an event for this message to each camera
for (final String cameraName : seenTargets.keySet()) {
getCameraEvents(cameraName)
.add(new ExerciseFeedMessageEvent(cameraName, System.currentTimeMillis() - startTime, message));
}
}
}