/**
 *  Copyright 2013 Diego Ceccarelli
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package it.cnr.isti.hpc.dexter.rest;

import it.cnr.isti.hpc.dexter.StandardTagger;
import it.cnr.isti.hpc.dexter.Tagger;
import it.cnr.isti.hpc.dexter.article.ArticleServer;
import it.cnr.isti.hpc.dexter.common.ArticleDescription;
import it.cnr.isti.hpc.dexter.common.Field;
import it.cnr.isti.hpc.dexter.common.FlatDocument;
import it.cnr.isti.hpc.dexter.common.MultifieldDocument;
import it.cnr.isti.hpc.dexter.disambiguation.Disambiguator;
import it.cnr.isti.hpc.dexter.entity.Entity;
import it.cnr.isti.hpc.dexter.entity.EntityMatch;
import it.cnr.isti.hpc.dexter.entity.EntityMatchList;
import it.cnr.isti.hpc.dexter.graph.CategoryNodeFactory;
import it.cnr.isti.hpc.dexter.graph.EntityCategoryNodeFactory;
import it.cnr.isti.hpc.dexter.graph.IncomingNodes;
import it.cnr.isti.hpc.dexter.graph.NodeFactory;
import it.cnr.isti.hpc.dexter.graph.OutcomingNodes;
import it.cnr.isti.hpc.dexter.label.IdHelper;
import it.cnr.isti.hpc.dexter.label.IdHelperFactory;
import it.cnr.isti.hpc.dexter.relatedness.Relatedness;
import it.cnr.isti.hpc.dexter.relatedness.RelatednessFactory;
import it.cnr.isti.hpc.dexter.rest.domain.AnnotatedDocument;
import it.cnr.isti.hpc.dexter.rest.domain.AnnotatedSpot;
import it.cnr.isti.hpc.dexter.rest.domain.CandidateEntity;
import it.cnr.isti.hpc.dexter.rest.domain.CandidateSpot;
import it.cnr.isti.hpc.dexter.rest.domain.EntityRelatedness;
import it.cnr.isti.hpc.dexter.rest.domain.EntitySpots;
import it.cnr.isti.hpc.dexter.rest.domain.SpottedDocument;
import it.cnr.isti.hpc.dexter.rest.domain.Tagmeta;
import it.cnr.isti.hpc.dexter.spot.Spot;
import it.cnr.isti.hpc.dexter.spot.SpotMatch;
import it.cnr.isti.hpc.dexter.spot.SpotMatchList;
import it.cnr.isti.hpc.dexter.spot.ram.EntityToSpotListMap;
import it.cnr.isti.hpc.dexter.spotter.Spotter;
import it.cnr.isti.hpc.dexter.util.DexterLocalParams;
import it.cnr.isti.hpc.dexter.util.DexterParams;
import it.cnr.isti.hpc.wikipedia.article.Article;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;

import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.sun.jersey.api.representation.Form;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;

/**
 * @author Diego Ceccarelli <[email protected]>
 * 
 *         Created on Feb 2, 2013
 */

@Path("rest")
@Api(value = "rest", description = "Dexter Rest Service")
public class RestService {

	private static Gson gson = new GsonBuilder()
			.serializeSpecialFloatingPointValues().create();
	private final ArticleServer server = new ArticleServer();

	private final static int STATUS_ERROR = 500;

	public static final DexterParams params = DexterParams.getInstance();
	public static final IdHelper helper = IdHelperFactory.getStdIdHelper();

	private static final Logger logger = LoggerFactory
			.getLogger(RestService.class);

	private DexterLocalParams getLocalParams(UriInfo ui) {
		MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
		DexterLocalParams params = new DexterLocalParams();
		for (String key : queryParams.keySet()) {
			params.addParam(key, queryParams.getFirst(key));
		}
		return params;
	}

	private Response error(String msg) {
		Response r = Response.status(STATUS_ERROR)
				.entity("{\"error\":\"" + msg + "\"}").build();
		return r;
	}

	private Response ok(Object o) {
		Response r = Response.ok(gson.toJson(o)).build();
		return r;
	}

	/**
	 * Performs the entity linking on a given text, annotating maximum n
	 * entities.
	 * 
	 * @param text
	 *            the text to annotate
	 * @param n
	 *            the maximum number of entities to annotate
	 * @returns an annotated document, containing the annotated text, and a list
	 *          entities detected.
	 */

	@GET
	@Path("/annotate")
	@ApiOperation(value = "Annotate a document with Wikipedia entities", response = AnnotatedDocument.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public Response annotateGet(@Context UriInfo ui,
			@QueryParam("text") String text,
			@QueryParam("n") @DefaultValue("50") String n,
			@QueryParam("spt") String spotter,
			@QueryParam("dsb") String disambiguator,
			@QueryParam("wn") @DefaultValue("false") String wikiNames,
			@QueryParam("debug") @DefaultValue("false") String dbg,
			@QueryParam("format") @DefaultValue("text") String format,
			@QueryParam("min-conf") @DefaultValue("0.5") String minConfidence) {
		DexterLocalParams requestParams = getLocalParams(ui);
		return annotate(requestParams, text, n, spotter, disambiguator,
				wikiNames, dbg, format, minConfidence);

	}

	@GET
	@Path("/relatedness")
	@ApiOperation(value = "Return the semantic relatedness between two entities", response = Relatedness.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public String relatedness(@Context UriInfo ui, @QueryParam("e1") String e1,
			@QueryParam("e2") String e2,
			@QueryParam("rel") @DefaultValue("milnewitten") String rel,
			@QueryParam("wn") @DefaultValue("false") String wikiNames,
			@QueryParam("debug") @DefaultValue("false") String dbg) {

		int x = Integer.parseInt(e1);
		int y = Integer.parseInt(e2);
		EntityRelatedness relatedness = new EntityRelatedness(x, y, rel);
		RelatednessFactory rf = new RelatednessFactory(rel);
		double r = rf.getRelatedness(x, y).getScore();
		relatedness.setRelatedness(r);
		boolean addWikinames = new Boolean(wikiNames);
		if (addWikinames) {
			relatedness.setEntity1Wikiname(helper.getLabel(x));
			relatedness.setEntity2Wikiname(helper.getLabel(y));
		}
		return gson.toJson(relatedness);

	}

	private List<Integer> parseEntities(String e) {
		List<Integer> list = new ArrayList<Integer>();
		Scanner scanner = new Scanner(e).useDelimiter(",");
		while (scanner.hasNextInt()) {
			list.add(scanner.nextInt());
		}
		return list;
	}

	@GET
	@Path("/spot-relatedness")
	@ApiOperation(value = "Return the semantic relatedness between two entities", response = Relatedness.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public String spotRelatedness(@Context UriInfo ui,
			@QueryParam("s1") String s1id, @QueryParam("s2") String s2id,
			@QueryParam("e1") String s1candidates,
			@QueryParam("e2") String s2candidates,
			@QueryParam("rel") @DefaultValue("milnewitten") String rel,
			@QueryParam("wn") @DefaultValue("false") String wikiNames,
			@QueryParam("debug") @DefaultValue("false") String dbg) {

		List<Integer> e1list = parseEntities(s1candidates);
		List<Integer> e2list = parseEntities(s2candidates);

		double max = 0;
		int maxi = -1;
		int maxj = -1;
		RelatednessFactory rf = new RelatednessFactory(rel);
		for (int i = 0; i < e1list.size(); i++) {
			for (int j = 0; j < e2list.size(); j++) {
				double r = rf.getRelatedness(e1list.get(i), e2list.get(j))
						.getScore();
				if (r > max) {
					maxi = e1list.get(i);
					maxj = e2list.get(j);
					max = r;
				}
			}
		}

		EntityRelatedness relatedness = new EntityRelatedness(
				Integer.parseInt(s1id), Integer.parseInt(s2id), rel);
		relatedness.setRelatedness(max);

		boolean addWikinames = new Boolean(wikiNames);
		if (addWikinames) {
			if (maxi > 0)
				relatedness.setEntity1Wikiname(helper.getLabel(maxi));
			if (maxj > 0)
				relatedness.setEntity2Wikiname(helper.getLabel(maxj));
		}
		return gson.toJson(relatedness);

	}

	@POST
	@Path("/annotate")
	@ApiOperation(value = "Annotate a document with Wikipedia entities", response = AnnotatedDocument.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public Response annotatePost(Form form, @FormParam("text") String text,
			@FormParam("n") @DefaultValue("50") String n,
			@FormParam("spt") String spotter,
			@FormParam("dsb") String disambiguator,
			@FormParam("wn") @DefaultValue("false") String wikiNames,
			@FormParam("debug") @DefaultValue("false") String dbg,
			@FormParam("format") @DefaultValue("text") String format,
			@QueryParam("min-conf") @DefaultValue("0.5") String minConfidence) {

		DexterLocalParams requestParams = getLocalParams(form);
		return annotate(requestParams, text, n, spotter, disambiguator,
				wikiNames, dbg, format, minConfidence);

	}

	private DexterLocalParams getLocalParams(Form form) {
		MultivaluedMap<String, String> queryParams = form;
		DexterLocalParams params = new DexterLocalParams();
		for (String key : queryParams.keySet()) {
			params.addParam(key, queryParams.getFirst(key));
		}
		return params;
	}

	private MultifieldDocument parseDocument(String text, String format) {
		Tagmeta.DocumentFormat df = Tagmeta.getDocumentFormat(format);
		MultifieldDocument doc = null;
		if (df == Tagmeta.DocumentFormat.TEXT) {
			doc = new FlatDocument(text);
		}
		if (df == Tagmeta.DocumentFormat.JSON) {
			doc = gson.fromJson(text, MultifieldDocument.class);

		}
		return doc;

	}

	public Response annotate(DexterLocalParams requestParams, String text,
			String n, String spotter, String disambiguator, String wikiNames,
			String dbg, String format, String minConfidenceStr) {
		if (text == null) {
			return error("text parameter is null");
		}

		Spotter s = params.getSpotter(spotter);

		Disambiguator d = params.getDisambiguator(disambiguator);
		Tagger tagger = new StandardTagger("std", s, d);

		Boolean debug = new Boolean(dbg);
		boolean addWikinames = new Boolean(wikiNames);

		Integer entitiesToAnnotate = Integer.parseInt(n);
		double minConfidence = Double.parseDouble(minConfidenceStr);
		MultifieldDocument doc = null;
		try {
			doc = parseDocument(text, format);
		} catch (IllegalArgumentException e) {
			logger.error(e.getMessage());
			return error(e.getMessage());
		}

		EntityMatchList eml = tagger.tag(requestParams, doc);

		AnnotatedDocument adoc = new AnnotatedDocument(doc);

		if (debug) {
			Tagmeta meta = new Tagmeta();
			meta.setDisambiguator(d.getClass().toString());
			meta.setSpotter(s.getClass().toString());
			meta.setFormat(format);
			meta.setRequestParams(requestParams.getParams());

			adoc.setMeta(meta);

		}
		annotate(adoc, eml, entitiesToAnnotate, addWikinames, minConfidence);

		// logger.info("annotate: {}", annotated);
		return ok(adoc);
	}

	public void annotate(AnnotatedDocument adoc, EntityMatchList eml,
			boolean addWikiNames, double minConfidence) {

		annotate(adoc, eml, eml.size(), addWikiNames, minConfidence);
	}

	public void annotate(AnnotatedDocument adoc, EntityMatchList eml,
			int nEntities, boolean addWikiNames, double minConfidence) {
		eml.sort();
		EntityMatchList emlSub = new EntityMatchList();
		int size = Math.min(nEntities, eml.size());
		List<AnnotatedSpot> spots = adoc.getSpots();
		spots.clear();
		for (int i = 0; i < size; i++) {
			EntityMatch em = eml.get(i);
			if (em.getScore() < minConfidence) {
				logger.debug("remove entity {}, confidence {} to low",
						em.getId(), em.getScore());
				continue;
			}
			emlSub.add(em);
			AnnotatedSpot spot = new AnnotatedSpot(em.getMention(),
					em.getSpotLinkProbability(), em.getStart(), em.getEnd(), em
							.getSpot().getLinkFrequency(), em.getSpot()
							.getFrequency(), em.getId(), em.getFrequency(),
					em.getCommonness(), em.getScore());
			spot.setField(em.getSpot().getField().getName());
			if (addWikiNames) {
				spot.setWikiname(helper.getLabel(em.getId()));
			}

			spots.add(spot);
		}
		MultifieldDocument annotatedDocument = getAnnotatedDocument(adoc,
				emlSub);
		adoc.setAnnotatedDocument(annotatedDocument);
	}

	private MultifieldDocument getAnnotatedDocument(AnnotatedDocument adoc,
			EntityMatchList eml) {
		Collections.sort(eml, new EntityMatch.SortByPosition());

		Iterator<Field> iterator = adoc.getDocument().getFields();
		MultifieldDocument annotated = new MultifieldDocument();
		while (iterator.hasNext()) {
			int pos = 0;
			StringBuffer sb = new StringBuffer();
			Field field = iterator.next();
			String currentField = field.getName();
			String currentText = field.getValue();

			for (EntityMatch em : eml) {
				if (!em.getSpot().getField().getName().equals(currentField)) {
					continue;
				}
				assert em.getStart() >= 0;
				assert em.getEnd() >= 0;
				try {
					sb.append(currentText.substring(pos, em.getStart()));
				} catch (java.lang.StringIndexOutOfBoundsException e) {
					logger.warn(
							"error annotating text output of bound for range {} - {} ",
							pos, em.getStart());
					logger.warn("text: \n\n {}\n\n", currentText);
				}
				// the spot has been normalized, i want to retrieve the real one
				String realSpot = "none";
				try {
					realSpot = currentText
							.substring(em.getStart(), em.getEnd());
				} catch (java.lang.StringIndexOutOfBoundsException e) {
					logger.warn(
							"error annotating text output of bound for range {} - {} ",
							pos, em.getStart());
					logger.warn("text: \n\n {}\n\n", currentText);
				}
				sb.append(
						"<a href=\"#\" onmouseover='manage(" + em.getId()
								+ ")' >").append(realSpot).append("</a>");
				pos = em.getEnd();
			}
			if (pos < currentText.length()) {
				try {
					sb.append(currentText.substring(pos));
				} catch (java.lang.StringIndexOutOfBoundsException e) {
					logger.warn(
							"error annotating text output of bound for range {} - end ",
							pos);
					logger.warn("text: \n\n {}\n\n", currentText);
				}

			}
			annotated.addField(new Field(field.getName(), sb.toString()));

		}

		return annotated;
	}

	/**
	 * Given the Wiki-id of an entity, returns a snippet containing some
	 * sentences that describe the entity.
	 * 
	 * @param id
	 *            the Wiki-id of the entity
	 * @param title
	 *            (optional, false by default) "true" if the function only
	 *            should return the label of the entity for the Wiki-id,
	 *            otherwise it will return all the metadata available.
	 * 
	 * @returns a short description of the entity represented by the Wiki-id
	 */

	@GET
	@Path("/get-desc")
	@ApiOperation(value = "Provides the description of an entity", response = ArticleDescription.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public String getDescription(
			@QueryParam("id") @DefaultValue("11983070") String id,
			@QueryParam("title-only") @DefaultValue("false") String titleonly) {

		int i = Integer.parseInt(id);
		boolean titleOnly = new Boolean(titleonly);
		if (titleOnly) {
			ArticleDescription desc = server.getOnlyEntityLabel(i);
			desc.setDescription(null);
			desc.setImage(null);
			return desc.toJson();

		}

		ArticleDescription desc = server.get(i);
		if (desc == null) {
			logger.warn("description for id {} is null ", i);
			desc = ArticleDescription.EMPTY;
		}
		// desc.setImage("");
		// desc.setInfobox(new HashMap<String, String>());
		desc.setId(i);
		String description = desc.toJson();

		logger.info("getDescription: {}", description);
		return description;

	}

	@GET
	@Path("/get-id")
	@ApiOperation(value = "Provides the wiki-id of an entity", response = ArticleDescription.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public String getDescription(
			@QueryParam("title") @DefaultValue("Johnny_Cash") String title) {
		try {
			title = URLDecoder.decode(title, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		String label = Article.getTitleInWikistyle(title);
		int id = helper.getId(label);

		ArticleDescription desc = new ArticleDescription();
		desc.setTitle(label);
		desc.setId(id);
		desc.setDescription(null);
		desc.setImage(null);
		String description = desc.toJson();
		logger.info("getId: {}", description);
		return description;

	}

	@GET
	@Path("/get-spots")
	@ApiOperation(value = "Provides all the spots that could refer to the given entity", response = EntitySpots.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public String getEntitySpots(
			@QueryParam("id") @DefaultValue("11983070") String id,
			@QueryParam("title") @DefaultValue("Johnny_Cash") String title,
			@QueryParam("wn") @DefaultValue("false") String wikiNames) {
		int wid = 0;
		boolean addWikinames = new Boolean(wikiNames);
		if (title != null) {
			String label = Article.getTitleInWikistyle(title);
			wid = helper.getId(label);
		}
		if (id != null) {
			wid = Integer.parseInt(id);
		}
		if (wid == 0) {
			return "{ \"error\":\"retrieving the spot for the given entity\"}";
		}
		EntityToSpotListMap map = EntityToSpotListMap.getInstance();
		List<Spot> spots = map.getSpots(wid);
		List<CandidateSpot> cspots = new LinkedList<CandidateSpot>();
		for (Spot spot : spots) {
			CandidateSpot s = new CandidateSpot();
			s.setMention(spot.getMention());
			s.setLinkProbability(spot.getLinkProbability());
			s.setLinkFrequency(spot.getLink());
			s.setDocumentFrequency(spot.getFrequency());
			List<CandidateEntity> candidates = new ArrayList<CandidateEntity>();
			boolean found = false;
			for (Entity entity : spot.getEntities()) {
				if (entity.getId() == wid)
					found = true;

				CandidateEntity c = new CandidateEntity(entity.getId(),
						entity.getFrequency(), spot.getEntityCommonness(entity));
				if (addWikinames) {
					c.setWikiname(helper.getLabel(entity.getId()));
				}
				candidates.add(c);
			}
			if (!found)
				continue;
			Collections.sort(candidates);
			s.setCandidates(candidates);
			cspots.add(s);
		}
		EntitySpots entitySpots = new EntitySpots();
		entitySpots.setEntity(wid);
		if (addWikinames) {
			entitySpots.setWikiname(helper.getLabel(wid));
		}
		entitySpots.setSpots(cspots);
		return gson.toJson(entitySpots);

	}

	/**
	 * It only performs the first step of the entity linking process, i.e., find
	 * all the mentions that could refer to an entity.
	 * 
	 * @param text
	 *            the text to spot
	 * @return all the spots detected in the text together with their link
	 *         probability. For each spot it also returns the list of candidate
	 *         entities associated with it, together with their commonness.
	 */
	private Response spot(DexterLocalParams requestParams, String text,
			String spt, String wikiNames, String dbg, String format) {
		long start = System.currentTimeMillis();
		Spotter spotter = params.getSpotter(spt);
		boolean debug = new Boolean(dbg);
		MultifieldDocument doc = null;

		try {
			doc = parseDocument(text, format);
		} catch (IllegalArgumentException e) {
			logger.error(e.getMessage());
			return error(e.getMessage());
		}

		boolean addWikinames = new Boolean(wikiNames);
		SpotMatchList sml = spotter.match(requestParams, doc);
		List<CandidateSpot> spots = new ArrayList<CandidateSpot>();
		List<CandidateEntity> candidates;

		for (SpotMatch spot : sml) {
			CandidateSpot s = new CandidateSpot();
			s.setMention(spot.getMention());
			s.setStart(spot.getStart());
			s.setField(spot.getField().getName());
			s.setEnd(spot.getEnd());
			s.setLinkProbability(spot.getLinkProbability());
			s.setLinkFrequency(spot.getLinkFrequency());
			s.setDocumentFrequency(spot.getFrequency());
			candidates = new ArrayList<CandidateEntity>();
			for (EntityMatch entity : spot.getEntities()) {
				CandidateEntity c = new CandidateEntity(entity.getId(),
						entity.getFrequency(), entity.getCommonness());
				if (addWikinames) {
					c.setWikiname(helper.getLabel(entity.getId()));
				}
				candidates.add(c);

			}
			Collections.sort(candidates);
			s.setCandidates(candidates);
			spots.add(s);
		}
		SpottedDocument sd = new SpottedDocument(doc, spots, spots.size(),
				System.currentTimeMillis() - start);

		if (debug) {

			Tagmeta meta = new Tagmeta();
			meta.setSpotter(spotter.getClass().toString());
			meta.setRequestParams(requestParams.getParams());
			meta.setFormat(format);
			sd.setMeta(meta);

		}

		// String spotted = gson.toJson(sd);
		// logger.info("spot: {}", spotted);
		return ok(sd);
	}

	@GET
	@Path("/spot")
	@ApiOperation(value = "Detects all the mentions that could refer to an entity in the text", response = EntitySpots.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public Response spotGet(
			@Context UriInfo ui,
			@QueryParam("text") @DefaultValue("Bob Dylan and Johnny Cash had formed a mutual admiration society even before they met in the early 1960s") String text,
			@QueryParam("spt") String spt,
			@QueryParam("wn") @DefaultValue("false") String wikiNames,
			@QueryParam("debug") @DefaultValue("false") String dbg,
			@QueryParam("format") @DefaultValue("text") String format) {
		DexterLocalParams requestParams = getLocalParams(ui);
		return spot(requestParams, text, spt, wikiNames, dbg, format);
	}

	@POST
	@Path("/spot")
	@ApiOperation(value = "Detects all the mentions that could refer to an entity in the text", response = EntitySpots.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public Response spotPost(
			Form form,
			@FormParam("text") @DefaultValue("Bob Dylan and Johnny Cash had formed a mutual admiration society even before they met in the early 1960s") String text,
			@FormParam("spt") @DefaultValue("wiki-dictionary") String spt,
			@FormParam("wn") @DefaultValue("false") String wikiNames,
			@FormParam("debug") @DefaultValue("false") String dbg,
			@FormParam("format") @DefaultValue("text") String format) {
		DexterLocalParams requestParams = getLocalParams(form);
		return spot(requestParams, text, spt, wikiNames, dbg, format);
	}

	@GET
	@Path("/get-candidates")
	@ApiOperation(value = "Given a query, returns a list of candidates entities represented by the query", response = EntitySpots.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public Response queryLucene(@Context UriInfo ui,
			@QueryParam("field") @DefaultValue("title") String field,
			@QueryParam("n") @DefaultValue("10") String results,
			@QueryParam("query") @DefaultValue("johnny cash") String query) {
		Integer n = Integer.parseInt(results);
		int status = 500;
		List<ArticleDescription> rankedArticles = Collections.emptyList();
		if (n > 0) {
			rankedArticles = server.getEntities(query, field, n);

		}
		if (rankedArticles.size() > 0) {
			status = 200;
		}
		String json = gson.toJson(rankedArticles);
		Response r = Response.status(status).entity(json).build();
		return r;
	}

	// graph

	private List<ArticleDescription> getNodes(int id, ArticleDescription e,
			List<ArticleDescription> list, int[] array, boolean addWikinames) {
		List<ArticleDescription> nodes = new ArrayList<ArticleDescription>(
				array.length);
		e.setId(id);
		e.setDescription(null);
		e.setImage(null);
		e.setInfobox(null);

		for (int entity : array) {
			ArticleDescription desc = new ArticleDescription();
			desc.setId(entity);
			desc.setImage(null);
			desc.setDescription(null);
			desc.setInfobox(null);
			if (addWikinames) {
				desc.setTitle(helper.getLabel(entity));
				e.setTitle(helper.getLabel(id));
			} else {
				desc.setTitle(null);
				e.setTitle(null);
			}
			nodes.add(desc);
		}
		return nodes;
	}

	@GET
	@Path("/get-target-entities")
	@ApiOperation(value = "Given an entity, returns the entities linked by given entity", response = ArticleDescription.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public String getTargetEntities(
			@QueryParam("id") @DefaultValue("11983070") String wikiId,
			@QueryParam("wn") @DefaultValue("false") String asWikiNames) {
		boolean addWikinames = new Boolean(asWikiNames);

		ArticleDescription e = new ArticleDescription();

		int id = Integer.parseInt(wikiId);

		if (addWikinames) {

		}
		OutcomingNodes entityOutcomingNodes = NodeFactory
				.getOutcomingNodes(NodeFactory.STD_TYPE);
		int[] out = entityOutcomingNodes.getNeighbours(id);
		e.setOutcomingEntities(getNodes(id, e,
				new ArrayList<ArticleDescription>(out.length), out,
				addWikinames));
		return gson.toJson(e);
	}

	@GET
	@Path("/get-source-entities")
	@ApiOperation(value = "Given an entity, returns the entities that link to the given entity", response = ArticleDescription.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public String getSourceEntities(
			@QueryParam("id") @DefaultValue("11983070") String wikiId,
			@QueryParam("wn") @DefaultValue("false") String asWikiNames) {
		boolean addWikinames = new Boolean(asWikiNames);
		ArticleDescription e = new ArticleDescription();
		int id = Integer.parseInt(wikiId);

		IncomingNodes entityIncomingNodes = NodeFactory
				.getIncomingNodes(NodeFactory.STD_TYPE);
		int[] in = entityIncomingNodes.getNeighbours(id);
		e.setIncomingEntities(getNodes(id, e,
				new ArrayList<ArticleDescription>(in.length), in, addWikinames));
		return gson.toJson(e);
	}

	@GET
	@Path("/get-entity-categories")
	@ApiOperation(value = "Given an entity, returns its categories", response = ArticleDescription.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public String getEntityCategories(
			@QueryParam("id") @DefaultValue("11983070") String wikiId,
			@QueryParam("wn") @DefaultValue("false") String asWikiNames) {
		boolean addWikinames = new Boolean(asWikiNames);

		int id = Integer.parseInt(wikiId);
		OutcomingNodes entityOutcomingNodes = EntityCategoryNodeFactory
				.getOutcomingNodes(EntityCategoryNodeFactory.STD_TYPE);
		int[] out = entityOutcomingNodes.getNeighbours(id);
		ArticleDescription e = new ArticleDescription();
		e.setParentCategories(getNodes(id, e,
				new ArrayList<ArticleDescription>(out.length), out,
				addWikinames));
		return gson.toJson(e);

	}

	@GET
	@Path("/get-belonging-entities")
	@ApiOperation(value = "Given a category, returns the entities that belong to the category", response = ArticleDescription.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public String getBelongingEntities(
			@QueryParam("id") @DefaultValue("30061715") String wikiId,
			@QueryParam("wn") @DefaultValue("false") String asWikiNames) {
		boolean addWikinames = new Boolean(asWikiNames);
		int id = Integer.parseInt(wikiId);
		IncomingNodes entityIncomingNodes = EntityCategoryNodeFactory
				.getIncomingNodes(EntityCategoryNodeFactory.STD_TYPE);
		int[] in = entityIncomingNodes.getNeighbours(id);
		ArticleDescription e = new ArticleDescription();
		e.setOutcomingEntities(getNodes(id, e,
				new ArrayList<ArticleDescription>(in.length), in, addWikinames));
		return gson.toJson(e);

	}

	@GET
	@Path("/get-parent-categories")
	@ApiOperation(value = "Given a category, returns its parent categories", response = ArticleDescription.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public String getParentCategories(
			@QueryParam("id") @DefaultValue("30061715") String wikiId,
			@QueryParam("wn") @DefaultValue("false") String asWikiNames) {
		boolean addWikinames = new Boolean(asWikiNames);
		int id = Integer.parseInt(wikiId);
		IncomingNodes categoryIncomingNodes = CategoryNodeFactory
				.getIncomingNodes(CategoryNodeFactory.STD_TYPE);
		int[] in = categoryIncomingNodes.getNeighbours(id);
		ArticleDescription e = new ArticleDescription();
		e.setParentCategories(getNodes(id, e,
				new ArrayList<ArticleDescription>(in.length), in, addWikinames));
		return gson.toJson(e);
	}

	@GET
	@Path("/get-child-categories")
	@ApiOperation(value = "Given a category, returns its child categories", response = ArticleDescription.class)
	@Produces({ MediaType.APPLICATION_JSON })
	public String getChildCategories(
			@QueryParam("id") @DefaultValue("8251471") String wikiId,
			@QueryParam("wn") @DefaultValue("false") String asWikiNames) {
		boolean addWikinames = new Boolean(asWikiNames);
		int id = Integer.parseInt(wikiId);
		OutcomingNodes categoryOutcomingNodes = CategoryNodeFactory
				.getOutcomingNodes(CategoryNodeFactory.STD_TYPE);
		int[] out = categoryOutcomingNodes.getNeighbours(id);
		ArticleDescription e = new ArticleDescription();
		e.setChildCategories(getNodes(id, e, new ArrayList<ArticleDescription>(
				out.length), out, addWikinames));
		return gson.toJson(e);
	}
}