package me.moodcat.api;
import java.util.List;
import java.util.stream.Collectors;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import me.moodcat.api.filters.AwardPoints;
import me.moodcat.api.models.ChatMessageModel;
import me.moodcat.api.models.NowPlaying;
import me.moodcat.api.models.RoomModel;
import me.moodcat.api.models.SongModel;
import me.moodcat.backend.rooms.RoomBackend;
import me.moodcat.backend.rooms.RoomInstance;
import me.moodcat.database.controllers.RoomDAO;
import me.moodcat.database.embeddables.VAVector;
import me.moodcat.database.entities.Room;
import me.moodcat.database.entities.Song;
import me.moodcat.database.entities.User;
import me.moodcat.backend.Vote;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.name.Named;
import com.google.inject.persist.Transactional;
/**
* The API for the room.
*/
@Path("/api/rooms/")
@Produces(MediaType.APPLICATION_JSON)
public class RoomAPI {
private static final int MAXIMUM_CHAT_MESSAGE_LENGTH = 255;
private static final int VOTES_POINTS_AWARD = 2;
/**
* The backend of the room.
*/
private final RoomBackend backend;
/**
* The database access object for the rooms.
*/
private final RoomDAO roomDAO;
/**
* Current User provider.
*/
private final Provider<User> currentUserProvider;
@Inject
@VisibleForTesting
public RoomAPI(final RoomBackend backend, final RoomDAO roomDAO,
@Named("current.user") final Provider<User> currentUserProvider) {
this.backend = backend;
this.roomDAO = roomDAO;
this.currentUserProvider = currentUserProvider;
}
/**
* Get all the rooms that are sorted on how close they are to the provided moods.
*
* @param moods
* The moods we want to have rooms for.
* @param limit
* The number of rooms to retrieve.
* @return The list of rooms that are close to the provided moods.
*/
@GET
@Transactional
public List<RoomModel> getRooms(@QueryParam("mood") final List<String> moods,
@QueryParam("limit") @DefaultValue("5") final int limit) {
final VAVector targetVector = Mood.createTargetVector(moods);
return roomDAO.queryRooms(targetVector, limit)
.stream()
.map(this::resolveRoomInstance)
.map(RoomAPI::transform)
.collect(Collectors.toList());
}
private RoomInstance resolveRoomInstance(final Room room) {
return backend.getRoomInstance(room.getId());
}
/**
* Transform a {@link RoomInstance} into a roommodel.
*
* @param roomInstance
* The instance to create a roommodel from.
* @return The roommodel that represents the roominstance.
*/
@Transactional
public static RoomModel transform(final RoomInstance roomInstance) {
final RoomModel roomModel = new RoomModel();
final SongModel songModel = SongModel.transform(roomInstance.getCurrentSong());
final NowPlaying nowPlaying = new NowPlaying(roomInstance.getCurrentTime(), songModel);
roomModel.setId(roomInstance.getId());
roomModel.setName(roomInstance.getName());
roomModel.setNowPlaying(nowPlaying);
return roomModel;
}
/**
* Get the room according to the provided id.
*
* @param roomId
* The id of the room to find.
* @return The room according to the id.
* @throws IllegalArgumentException
* If the roomId is null.
*/
@GET
@Path("{id}")
public RoomModel getRoom(@PathParam("id") final int roomId) {
return transform(backend.getRoomInstance(roomId));
}
/**
* Get all the messages of the room.
*
* @param roomId
* The id of the room to retrieve messages from.
* @return The list of messages of the room.
*/
@GET
@Path("{id}/messages")
public List<ChatMessageModel> getMessages(@PathParam("id") final int roomId) {
return backend.getRoomInstance(roomId).getMessages();
}
/**
* Get all the message of the room that happened later than where posted after the chatmessage
* with the corresponding messageId.
*
* @param roomId
* The room the messages were placed in.
* @param chatMessageId
* The message id we want to obtain later messages of.
* @return A list of messages that happened later than the provided chatMessageId. Can be empty.
*/
@GET
@Path("{id}/messages/{chatMessageId}")
public List<ChatMessageModel> getMessages(@PathParam("id") final int roomId,
@PathParam("chatMessageId") final int chatMessageId) {
return backend.getRoomInstance(roomId).getMessages().stream()
.filter((message) -> message.getId() > chatMessageId)
.collect(Collectors.toList());
}
/**
* Post a message to a room.
*
* @param msg
* The message to post.
* @param roomId
* The id of the room.
* @return The chatmessage if storage was succesful.
*/
@POST
@Path("{id}/messages")
@Consumes(MediaType.APPLICATION_JSON)
public ChatMessageModel postChatMessage(final ChatMessageModel msg,
@PathParam("id") final int roomId) {
if (msg.getMessage().length() > MAXIMUM_CHAT_MESSAGE_LENGTH) {
throw new IllegalArgumentException("The chat message may not be longer than "
+ MAXIMUM_CHAT_MESSAGE_LENGTH + " characters.");
}
return backend.getRoomInstance(roomId).sendMessage(msg, currentUserProvider.get());
}
/**
* Retrieve whats playing now.
*
* @param roomId
* The id of the room.
* @return
* Whats currently playing in the room
*/
@GET
@Path("{id}/now-playing")
@Transactional
public NowPlaying getCurrentTime(@PathParam("id") final int roomId) {
final RoomInstance roomInstance = backend.getRoomInstance(roomId);
final Song song = roomInstance.getCurrentSong();
final NowPlaying nowPlaying = new NowPlaying();
nowPlaying.setSong(SongModel.transform(song));
nowPlaying.setTime(roomInstance.getCurrentTime());
return nowPlaying;
}
/**
* Process a vote to a song. A vote is either "like" or "dislike".
*
* @param vote
* The vote.
* @return The song object, if the process was succesful.
*/
@POST
@Path("{id}/vote/{vote}")
@Transactional
@AwardPoints(VOTES_POINTS_AWARD)
public RoomModel voteSong(@PathParam("id") final int roomId,
@PathParam("vote") final String vote) {
final RoomInstance roomInstance = this.backend.getRoomInstance(roomId);
Vote voteValue = Vote.valueOf(vote.toUpperCase());
roomInstance.addVote(currentUserProvider.get(), voteValue);
return transform(roomInstance);
}
}