package me.moodcat.api;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import lombok.Getter;
import me.moodcat.database.embeddables.VAVector;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* A mood represents a vector in the valence-arousal plane which will be attached to song.
*/
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Mood {
// CHECKSTYLE:OFF
ANGRY(new VAVector(-0.6, 0.6), "Angry"),
CALM(new VAVector(0.3, -0.9), "Calm"),
EXCITING(new VAVector(0.4, 0.8), "Exciting"),
HAPPY(new VAVector(0.7, 0.6), "Happy"),
NERVOUS(new VAVector(-0.7, 0.4), "Nervous"),
PLEASING(new VAVector(0.6, 0.3), "Pleasing"),
PEACEFUL(new VAVector(0.5, -0.7), "Peaceful"),
RELAXED(new VAVector(0.6, -0.3), "Relaxed"),
SAD(new VAVector(-0.7, -0.2), "Sad"),
SLEEPY(new VAVector(-0.2, -0.9), "Sleepy");
// CHECKSTYLE:ON
/**
* List of all names that represent moods. Used in {@link #nameRepresentsMood(String)}.
* By storing this once, we save a lot of unnecessary list creations.
*/
private static final List<String> MOOD_NAMES = Arrays.asList(Mood.values()).stream()
.map(moodValue -> moodValue.getName())
.collect(Collectors.toList());
/**
* The vector that represents this mood.
*
* @return The vector of this mood.
*/
@Getter
@JsonIgnore
private final VAVector vector;
/**
* Readable name for the frontend.
*
* @return The readable name of this mood.
*/
@Getter
private final String name;
private Mood(final VAVector vector, final String name) {
this.vector = vector;
this.name = name;
}
/**
* Get the mood that is closest to the given vector.
*
* @param vector
* The vector to determine the mood for.
* @return The Mood that is closest to the vector.
*/
public static Mood closestTo(final VAVector vector) {
double distance = Double.MAX_VALUE;
Mood mood = null;
for (final Mood m : Mood.values()) {
final double moodDistance = m.vector.distance(vector);
if (moodDistance < distance) {
distance = moodDistance;
mood = m;
}
}
return mood;
}
/**
* Get the vector that represents the average of the provided list of moods.
*
* @param moods
* The textual list of moods.
* @return The average vector, or the zero-vector if no moods were found.
*/
public static VAVector createTargetVector(final List<String> moods) {
final List<VAVector> actualMoods = moods.stream()
.filter(Mood::nameRepresentsMood)
.map(mood -> Mood.valueOf(mood.toUpperCase(Locale.ROOT)))
.map(mood -> mood.getVector())
.collect(Collectors.toList());
return VAVector.average(actualMoods);
}
private static boolean nameRepresentsMood(final String mood) {
return MOOD_NAMES.contains(mood);
}
}