package de.tud.kom.socom.components.content;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.json.JSONException;
import org.json.JSONObject;
import de.tud.kom.socom.SocomComponent;
import de.tud.kom.socom.SocomCore;
import de.tud.kom.socom.GlobalConfig;
import de.tud.kom.socom.database.content.GameContentDatabase;
import de.tud.kom.socom.database.content.HSQLGameContentDatabase;
import de.tud.kom.socom.database.game.GameDatabase;
import de.tud.kom.socom.database.game.HSQLGameDatabase;
import de.tud.kom.socom.util.SocomRequest;
import de.tud.kom.socom.util.JSONUtils;
import de.tud.kom.socom.util.NumberParser;
import de.tud.kom.socom.util.datatypes.GameContent;
import de.tud.kom.socom.util.enums.ContentCategory;
import de.tud.kom.socom.util.exceptions.ContentNotAvailableException;
import de.tud.kom.socom.util.exceptions.SocomException;
import de.tud.kom.socom.util.exceptions.IllegalParameterException;
/**
*
* @author Rhaban Hark
*
* Game-Content-Manager is used to handle Game-Content e.g. Hints,
* Videos, Level and more. In this manager are methods to upload,
* download and fetch information of game-content.
*
*/
public class ContentManager extends SocomComponent {
private static final String URL_PATTERN = "content";
private static GameContentDatabase db;
private static ContentManager instance = new ContentManager();
private ContentManager() {
db = HSQLGameContentDatabase.getInstance();
}
public static ContentManager getInstance() {
return instance;
}
/**
* With this method you can allocate space for your game-content which you
* can upload after allocating the space. Here you have to specify all
* information for the content.
*
* The method puts a "contentident"-cookie in the response, containing a key
* you need when uploading the real content.
*
* @param visibility
* @param title
* @param description
* @param type
* (text | image | audio | binary)
* @param category: (QUESTION, INFORMATION, HINT, SOLUTION)
* @param Optional
* : contextid To which context do this content relate
* @param all
* other parameters will be handled as metadata for this content
* @return success boolean with contentident cookie
* @throws SQLException
* @throws JSONException
*/
public int createUserContent(SocomRequest req) throws SQLException, JSONException, SocomException {
try {
int visibility = Integer.parseInt(req.getParam("visibility"));
return createGameContent(req, visibility);
} catch (NumberFormatException e) {
throw new IllegalParameterException("visibility must be of type integer between 0 and 4.");
}
}
private int createGameContent(SocomRequest req, int visibility) throws SQLException, JSONException, SocomException {
long uid = req.getUid();
long gameinstid = req.getCurrentGameInst();
long contextid = HSQLGameDatabase.getInstance().getGameContextId(req.getParam("contextid"), gameinstid);
String title = req.getParam("title");
String description = req.getParam("description");
// type must be either 'text' or 'binary'
String type = req.getParam("type");
String cat = req.getParam("category");
ContentCategory category = null;
try {
category = ContentCategory.valueOf(cat.toUpperCase());
} catch (IllegalArgumentException e) {
throw new IllegalParameterException("category");
}
if (!(type.equals("text") || type.equals("binary") || type.equals("audio") || type.equals("image")))
return 10;
Map<String, String> attributes = new HashMap<String, String>(req.getParams());
// Remove known values
attributes.remove("contextid");
attributes.remove("type");
attributes.remove("title");
attributes.remove("description");
attributes.remove("visibility");
attributes.remove("category");
String ident = db.createGameContent(uid, contextid, title, description, category, attributes, type, visibility);
req.addCookie("contentident", ident);
req.addOutput(JSONUtils.getSuccessJsonString());
return 0;
}
/**
* Equal method to createUserContent. Difference is the visibility is
* restricted to the game (non-user content).
*
* @see(createUserContent)
*
* @param title
* @param description
* @param type
* (text | image | audio | binary)
* @param category
* @param Optional
* : contextid To which context do this content relate
* @param all
* other parameters will be handled as metadata for this content
* @return success boolean with contentident cookie
* @throws SQLException
* @throws JSONException
*/
public int createGameContent(SocomRequest req) throws SQLException, JSONException, SocomException {
int visibility = GlobalConfig.VISIBILITY_NON_USER;
return createGameContent(req, visibility);
}
/**
* HTTP-POST-Method
*
* After allocating space for a content you can upload the content using the
* key sent in the "contentident" cookie. The cookie must be sent back (as
* cookie) here for identification.
*
* @param contentident
* as cookie
* @return The new contentid
* @throws JSONException
* @throws IOException
* @throws SQLException
*/
public int uploadContent(SocomRequest req) throws JSONException, IOException, SQLException, SocomException {
long uid = req.getUid();
String identifier = req.getCookieVal("contentident");
InputStream is = req.getInputStream();
long contentid = db.uploadGameContent(uid, identifier, is);
req.addOutput(JSONUtils.JSONToString(new JSONObject().put("contentid", contentid)));
return 0;
}
/**
* Fetch information about content in the given context.
*
* @param context
* The context-id where the content should be related to
* @param Optional
* : since Either a date in the form "yyyy-MM-dd HH:mm:ss" or the
* date in ms from 01.01.1970 00:00
* @return List of Content
* @throws IOException
* @throws JSONException
* @throws SQLException
*/
public int getContentInfoForContext(SocomRequest req) throws IOException, JSONException, SQLException, SocomException {
long uid = req.getUid();
long gameinstid = req.getCurrentGameInst();
long contextid = HSQLGameDatabase.getInstance().getGameContextId(req.getParam("context"), gameinstid);
Date sinceDate = null;
if (req.getParams().containsKey("since")) {
sinceDate = determineSinceDate(req.getParam("since"));
}
List<GameContent> contentList = db.fetchContent(uid, contextid, sinceDate);
req.addOutput(JSONUtils.JSONToString(new JSONObject().put("content", contentList)));
return 0;
}
/**
* Fetch content-informations for the given filter options.
*
* @param Optional
* : contexts Comma-Separated list of contextids where the
* content must relate to
* @param Optional
* : sincle Either a date in the form "yyyy-MM-dd HH:mm:ss" or
* the date in ms from 01.01.1970 00:00
* @param Optional
* : type Comma-Separated list of types the should could be of
* one of it
* @param Optional
* : keywords Comma-Separated list of keywords the description
* must contain
* @param Optional
* : metadata Form: key1:value1,key2:value2,... which the content
* MUST contain, escape double-points with /: and commatas with
* /,
* @return List of contents
* @throws SQLException
* @throws JSONException
*/
public int getContentInfo(SocomRequest req) throws SQLException, JSONException, SocomException {
long uid = req.getUid();
long gameinstid = req.getCurrentGameInst();
String[] contexts = req.containsParam("contexts") ? req.getParam("contexts").split(",") : null;
String[] contextids = null;
if (contexts != null) {
contextids = new String[contexts.length];
GameDatabase gamedb = HSQLGameDatabase.getInstance();
for (int i = 0; i < contexts.length; i++) {
contextids[i] = String.valueOf(gamedb.getGameContextId(contexts[i], gameinstid));
}
}
Date since = req.containsParam("since") ? determineSinceDate(req.getParam("since")) : null;
String[] types = req.containsParam("type") ? req.getParam("type").split(",") : null;
String[] titles = req.containsParam("title") ? req.getParam("title").split(",") : null;
String[] keywords = req.containsParam("keywords") ? req.getParam("keywords").split(",") : null;
String[] metadata = req.containsParam("metadata") ? req.getParam("metadata").split("(?<!/),") : null;
// regex "(?<!/)," explains that a comma (,) but not a comma with a
// leading / (/,) is selected (negative lookbehind)
List<GameContent> contentList = db.fetchContent(uid, gameinstid, contextids, since, types, titles, keywords, metadata);
req.addOutput(JSONUtils.JSONToString(new JSONObject().put("content", contentList)));
return 0;
}
private Date determineSinceDate(String time) throws SocomException {
Date sinceDate;
try {
long sinceInMillis = Long.parseLong(time);
sinceDate = new Date(sinceInMillis);
} catch (NumberFormatException e) {
try {
sinceDate = SocomCore.getDateFormat().parse(time);
} catch (ParseException e1) {
e1.printStackTrace();
throw new IllegalParameterException("since");
}
}
return sinceDate;
}
/**
* Download the content specified with its id, you could have received using
* the getContentInfo method.
*
* @param contentid
* @return a File
* @throws IOException
* @throws SQLException
*/
public int downloadContent(SocomRequest req) throws IOException, SQLException, SocomException {
try {
long uid = req.getUid();
long contentid = Long.valueOf(req.getParam("contentid"));
byte[] result = db.downloadContent(uid, contentid, true);
if (result == null)
throw new ContentNotAvailableException();
String type = db.getType(contentid);
String extension = type.equalsIgnoreCase("image") ? ".png" : type.equalsIgnoreCase("audio") ? ".ogg" :
type.equalsIgnoreCase("text") ? ".txt" : "";
// TODO what about other download file types
req.setOutputFilename("socom_" + contentid + extension);
req.getOutputStream().write(result);
req.getOutputStream().flush();
req.getOutputStream().close();
} catch (NumberFormatException e) {
throw new IllegalParameterException("contentid must be of type long");
}
return 0;
}
/**
* Rates a content.
*
* @param contentid
* @param rating
* (Double between 0 and 1)
* @return success boolean
* @throws SQLException
* @throws JSONException
*/
public int rateContent(SocomRequest req) throws JSONException, SQLException, SocomException {
String param = "contentid", type = "long";
try {
long uid = req.getUid();
long contentid = Long.valueOf(req.getParam("contentid"));
param = "rating";
type = "double between 0 and 1";
double rating = NumberParser.parseDouble(req.getParam("rating"));
if (rating < 0 || rating > 1) {
throw new IllegalParameterException("rating has to be between 0 and 1 or");
}
db.rateContent(uid, contentid, rating);
req.addOutput(JSONUtils.getSuccessJsonString());
} catch (NumberFormatException e) {
throw new IllegalParameterException(param + " (of type " + type + ") has wrong format or");
}
return 0;
}
/**
* Adds a comment to a specific content
*
* @param contentid
* @param message
* @return the commentid
* @throws SQLException
* @throws JSONException
*/
public int addComment(SocomRequest req) throws SQLException, JSONException, SocomException {
try {
long uid = req.getUid();
long contentid = Long.valueOf(req.getParam("contentid"));
String message = req.getParam("message");
long commentid = db.addComment(uid, contentid, message);
req.addOutput(JSONUtils.JSONToString(new JSONObject().put("commentid", commentid)));
} catch (NumberFormatException e) {
throw new IllegalParameterException("contentid must be of type long.");
}
return 0;
}
/**
* Deletes a specific comment
*
* @param commentid
* @param Optional
* : delete value
* @return success boolean
* @throws SQLException
* @throws JSONException
*/
public int deleteComment(SocomRequest req) throws SQLException, JSONException, SocomException {
String param = "commentid", type = "long";
try {
long uid = req.getUid();
long contentid = Long.valueOf(req.getParam("commentid"));
param = "delete";
type = "integer between 0 and 2";
int delete = req.getParams().containsKey("delete") ? Integer.parseInt(req.getParam("delete")) : 1;
boolean success = db.deleteComment(uid, contentid, delete);
req.addOutput(JSONUtils.getSuccessJsonString(success));
} catch (NumberFormatException e) {
throw new IllegalParameterException(param + " must be of type " + type + ".");
}
return 0;
}
/**
* URL PATTERN IS "content"
*/
@Override
public String getUrlPattern() {
return URL_PATTERN;
}
}