package so.tio.inlearn.server.session;
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import so.tio.inlearn.common.testing.TaskItem;
import so.tio.inlearn.common.testing.TestItem;
import so.tio.inlearn.common.testing.TestVariant;
import so.tio.inlearn.common.utils.XmlTools;
import so.tio.inlearn.server.testing.TestsFacility;
public class Session {
private User user = new User();
private long[] availableTests;
private long[] testsTimeOut;
private TestAnswersHandler answers = null;
private long inTest = 0; // TODO: When testing done set to 0 again
private SessionCallback cb = new SessionCallback();
public Session(long[] availableTests, long[] testsTimeOut) {
this.availableTests = availableTests;
this.testsTimeOut = testsTimeOut;
}
public void setSessionCallback(SessionCallback cb) {
this.cb = cb;
}
public long isInTest() {
return inTest;
}
private String buildResponsePacket(Map<String, String> params) {
Document doc = XmlTools.getDocBuilder().newDocument();
Element root = doc.createElement("response");
for (Entry<String, String> param : params.entrySet()) {
root.setAttribute(param.getKey(), param.getValue());
}
doc.appendChild(root);
return XmlTools.docToString(doc);
}
private String buildErrorPacket(String msg) {
Map<String, String> params = new HashMap<String, String>();
params.put("type", "error");
params.put("message", msg);
return buildResponsePacket(params);
}
private String parseAuth(Node xmlNode) {
NamedNodeMap attrs = xmlNode.getAttributes();
try {
Map<String, String> got = new HashMap<String, String>();
try {
// Pass all params into map
for (String key : new String[] { "firstName", "lastName", "imei", "phoneNumber" }) {
got.put(key, attrs.getNamedItem(key).getNodeValue());
}
} catch (NullPointerException e) {
throw new IOException("Not enough params");
}
// Check mandatory params in map
if (got.get("firstName").isEmpty() || got.get("lastName").isEmpty() || got.get("imei").isEmpty()) {
throw new IOException("Not enough information in params");
}
// Creating user
user.setFirstName(got.get("firstName"));
user.setLastName(got.get("lastName"));
user.setImei(got.get("imei"));
user.setPhoneNumber(got.get("phoneNumber"));
user.setAuthed(true);
System.out.println("Authed: " + user);
// Responding: auth ok
Map<String, String> params = new HashMap<String, String>();
params.put("type", "auth");
params.put("message", "ok");
return buildResponsePacket(params);
} catch (DOMException | NullPointerException | IOException e) {
System.out.println("Auth rejected. " + e.getMessage());
return buildErrorPacket("Auth rejected. " + e.getMessage());
}
}
private String parseTestsList(Node xmlNode) {
if (!user.isAuthed()) {
return buildErrorPacket("Not authorized");
}
// Show only selected tests
List<TestItem> tests = new ArrayList<TestItem>();
int availTestId = 0;
for (TestItem test : TestsFacility.getTests()) {
if (isTestAvailable(test.getTestId())) {
test.setTimeOut(testsTimeOut[availTestId++]);
tests.add(test);
}
}
Document answerDoc = XmlTools.getDocBuilder().newDocument();
Element answerRoot = answerDoc.createElement("response");
answerRoot.setAttribute("type", "testsList");
for (TestItem test : tests) {
Element answerItem = answerDoc.createElement("test");
answerItem.setAttribute("name", test.getName());
answerItem.setAttribute("maximumMark", String.valueOf(test.getMaximumMark()));
answerItem.setAttribute("id", String.valueOf(test.getTestId()));
answerItem.setAttribute("timeOut", String.valueOf(test.getTimeOut()));
answerRoot.appendChild(answerItem);
}
answerDoc.appendChild(answerRoot);
return XmlTools.docToString(answerDoc);
}
private String parseTest(Node xmlNode) {
if (!user.isAuthed()) {
return buildErrorPacket("Not authorized");
}
if (inTest != 0) {
return buildErrorPacket("You can't do more than 1 test at once");
}
NamedNodeMap requestAttrs = xmlNode.getAttributes();
try {
long testId = Long.parseLong(requestAttrs.getNamedItem("id").getNodeValue());
if (!isTestAvailable(testId)) {
return buildErrorPacket("Test is not available now");
}
TestVariant variant = TestsFacility.getTestVariant(testId, user.toString());
if (variant == null) {
return buildErrorPacket("Test doesn't exist");
}
Document answerDoc = XmlTools.getDocBuilder().newDocument();
Element answerRoot = answerDoc.createElement("response");
answerRoot.setAttribute("type", "test");
answerRoot.setAttribute("testId", String.valueOf(variant.getTestId()));
answerRoot.setAttribute("testName", variant.getName());
answerRoot.setAttribute("maximumMark", String.valueOf(variant.getMaximumMark()));
answerRoot.setAttribute("variantId", String.valueOf(variant.getVariantId()));
answerRoot.setAttribute("timeOut", String.valueOf(variant.getTimeOut()));
for (TaskItem item : variant.getTasks()) {
Element taskItem = answerDoc.createElement("task");
taskItem.setAttribute("cost", String.valueOf(item.getCost()));
taskItem.setAttribute("taskId", String.valueOf(item.getTaskId()));
Element taskItemQuestion = answerDoc.createElement("question");
taskItemQuestion.appendChild(answerDoc.createCDATASection(item.getQuestion()));
taskItem.appendChild(taskItemQuestion);
Element taskItemAnswerForm = answerDoc.createElement("answerForm");
taskItemAnswerForm.appendChild(answerDoc.createCDATASection(item.getAnswerForm()));
taskItem.appendChild(taskItemAnswerForm);
answerRoot.appendChild(taskItem);
}
answerDoc.appendChild(answerRoot);
inTest = variant.getTestId();
if(answers == null){
answers = new TestAnswersHandler(TestsFacility.getTestVariant(testId, user.toString()));
}
cb.onClientStartTesting(variant.getTestId());
return XmlTools.docToString(answerDoc);
} catch (DOMException | NullPointerException | NumberFormatException e) {
System.out.println("Wrong test request " + e.getMessage());
return buildErrorPacket("Wrong test request.");
}
}
public String parseTestAnswer(Node xmlNode) {
if (!user.isAuthed()) {
return buildErrorPacket("Not authorized");
}
if (inTest == 0) {
return buildErrorPacket("You should start testing before posting answers.");
}
NamedNodeMap requestAttrs = xmlNode.getAttributes();
long testId = Long.parseLong(requestAttrs.getNamedItem("testId").getNodeValue());
if (!isTestAvailable(testId)) {
return buildErrorPacket("Test is not available now");
}
if (!TestsFacility.variantExist(testId, user.toString())) {
return buildErrorPacket("Variant is not generated yet. Maybe you not requested test?");
}
long taskId = Long.parseLong(requestAttrs.getNamedItem("taskId").getNodeValue());
String rawAnswers = requestAttrs.getNamedItem("answers").getNodeValue();
answers.putAnswers(taskId, getQueryParams(rawAnswers));
System.out.print("task " + taskId + " is: " + answers.isTaskValid(taskId) + "\n");
return buildErrorPacket("Not implemented yet.");
}
public String parseEndTest(Node xmlNode) {
Map<String, String> params = new HashMap<String, String>();
params.put("type", "endtest");
params.put("message", "ok");
System.out.println("testing done");
cb.onClientDoneTesting(user, answers);
return buildResponsePacket(params);
}
public String parseRequest(String request) {
try {
Document doc = XmlTools.getDocBuilder().parse(new InputSource(new StringReader(request)));
Node root = doc.getDocumentElement();
if (root.getNodeName().toLowerCase().equals("request")) {
NamedNodeMap attrs = root.getAttributes();
String requestType;
try {
requestType = attrs.getNamedItem("type").getNodeValue();
} catch (DOMException | NullPointerException e) {
return buildErrorPacket("Can't recognize request");
}
switch (requestType.toLowerCase()) {
case "auth":
return parseAuth(root);
case "testslist":
return parseTestsList(root);
case "test":
return parseTest(root);
case "postanswer":
return parseTestAnswer(root);
case "endtest":
return parseEndTest(root);
default:
return buildErrorPacket("Unknown request type");
}
} else {
return buildErrorPacket("Can't recognize packet");
}
} catch (IOException e) {
e.printStackTrace();
return buildErrorPacket("Unknown error");
} catch (SAXException e) {
return buildErrorPacket("Can't parse XML");
}
}
private boolean isTestAvailable(long testId) {
for (long id : availableTests) {
if (testId == id)
return true;
}
return false;
}
public static class SessionCallback {
public void onClientConnected() {
System.out.println("Session.SessionCallback.onClientConnected()");
}
public void onClientDisconnected() {
System.out.println("Session.SessionCallback.onClientDisconnected()");
}
public void onClientStartTesting(long testId) {
System.out.println("Session.SessionCallback.onClientStartTesting()");
}
public void onClientDoneTesting(User user, TestAnswersHandler handler) {
System.out.println("Session.SessionCallback.onClientDoneTesting()");
}
}
public static Map<String, List<String>> getQueryParams(String url) {
try {
Map<String, List<String>> params = new HashMap<String, List<String>>();
String query = url;
for (String param : query.split("&")) {
String[] pair = param.split("=");
String key = URLDecoder.decode(pair[0], "UTF-8");
String value = "";
if (pair.length > 1) {
value = URLDecoder.decode(pair[1], "UTF-8");
}
List<String> values = params.get(key);
if (values == null) {
values = new ArrayList<String>();
params.put(key, values);
}
values.add(value);
}
return params;
} catch (UnsupportedEncodingException ex) {
throw new AssertionError(ex);
}
}
}