package com.springone.myrestaurants.web;

import static org.springframework.data.document.mongodb.query.Criteria.*;

import java.io.OutputStream;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.QueryBuilder;
import com.springone.myrestaurants.dao.RestaurantDao;
import com.springone.myrestaurants.domain.Restaurant;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.DefaultCategoryDataset;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.document.analytics.ControllerCounter;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.document.mongodb.query.Query;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@RequestMapping("/charts")
@Controller
public class ChartController {

	@Autowired
	private RestaurantDao restaurantDao;


	@RequestMapping("/favorites.png")
	public void renderChart(String variation, OutputStream stream)
			throws Exception {
		boolean rotate = "rotate".equals(variation); // add ?variation=rotate to
		// the URL to rotate the
		// chart
		JFreeChart chart = generateChart(rotate);
		ChartUtilities.writeChartAsPNG(stream, chart, 750, 400);
	}

	@RequestMapping("/controllers.png")
	public void renderControllers(String controllerName, OutputStream stream)
			throws Exception {
		JFreeChart chart = generateControllerChart(controllerName);
		ChartUtilities.writeChartAsPNG(stream, chart, 750, 400);
	}

	private JFreeChart generateControllerChart(String controllerName) {
		DefaultCategoryDataset dataset = getControllerData(controllerName);

		String xAxisLabel;
		String title;
		if (controllerName != null) {
			xAxisLabel = controllerName;
			title = controllerName + " Actions";
		} else {
			xAxisLabel = "Controllers";
			title = "Controller Invocations";
		}
		return ChartFactory.createBarChart(title,
				xAxisLabel,
				"Number of times invoked", // y-axis label
				dataset, PlotOrientation.VERTICAL, true, // legend displayed
				true, // tooltips displayed
				false); // no URLs*/
	}

	private JFreeChart generateChart(boolean rotate) {
		DefaultCategoryDataset dataset = getFavoritesData();

		return ChartFactory.createBarChart("Favorited Restaurants", // title
				"Restaurants", // x-axis label
				"Number of times recommended", // y-axis label
				dataset, rotate ? PlotOrientation.HORIZONTAL
				: PlotOrientation.VERTICAL, true, // legend displayed
				true, // tooltips displayed
				false); // no URLs*/
	}

	private DefaultCategoryDataset getControllerData(String controllerName) {
		MongoTemplate mongoTemplate;
		DefaultCategoryDataset ds = null;
		try {
			Mongo m = new Mongo();
			mongoTemplate = new MongoTemplate(m, "mvc");

			List<ControllerCounter> counters;
			ds = new DefaultCategoryDataset();

			if (controllerName != null) {
				counters = mongoTemplate.find(new Query(where("name").is(controllerName)), ControllerCounter.class, "counters");
				for (ControllerCounter controllerCounter : counters) {
					Map<String, Double> methodInvocations = controllerCounter.getMethods();
					Set<Entry<String, Double>> es = methodInvocations.entrySet();
					for (Entry<String, Double> entry : es) {
						ds.addValue(entry.getValue(), "invoked", entry.getKey());
					}
				}
			} else {
				counters = mongoTemplate.findAll(ControllerCounter.class, "counters");
				for (ControllerCounter controllerCounter : counters) {
					ds.addValue(controllerCounter.getCount(), "invoked (aggregate)", controllerCounter.getName());
				}
			}
			/*
			if (result instanceof BasicDBList) {
				BasicDBList dbList = (BasicDBList) result;
				for (Iterator iterator = dbList.iterator(); iterator.hasNext();) {
					DBObject dbo = (DBObject) iterator.next();
					System.out.println(dbo);
					Restaurant r = restaurantDao.findRestaurant(Long.parseLong(dbo.get("parameters.p1").toString()));
					ds.addValue(Double.parseDouble(dbo.get("count").toString()), "recommended", r.getName());
				}
			}*/
			return ds;


		} catch (Exception e) {
			e.printStackTrace();
		}
		return ds;
	}


	private DefaultCategoryDataset getFavoritesData() {
		MongoTemplate mongoTemplate;
		DefaultCategoryDataset ds = null;
		try {
			Mongo m = new Mongo();
			mongoTemplate = new MongoTemplate(m, "mvc");

			DBObject result = getTopRecommendedRestaurants(mongoTemplate);
			/* Example data.
			 * [ { "parameters.p1" : "1" , "count" : 5.0} , 
			 *   { "parameters.p1" : "2" , "count" : 6.0} , 
			 *   { "parameters.p1" : "3" , "count" : 3.0} , 
			 *   { "parameters.p1" : "4" , "count" : 8.0}]
			 */
			ds = new DefaultCategoryDataset();
			if (result instanceof BasicDBList) {
				BasicDBList dbList = (BasicDBList) result;
				for (Iterator iterator = dbList.iterator(); iterator.hasNext(); ) {
					DBObject dbo = (DBObject) iterator.next();
					System.out.println(dbo);
					Restaurant r = restaurantDao.findRestaurant(Long.parseLong(dbo.get("parameters.p1").toString()));
					ds.addValue(Double.parseDouble(dbo.get("count").toString()), "recommended", r.getName());
				}
			}
			return ds;


		} catch (Exception e) {
			e.printStackTrace();
		}
		return ds;


	}


	public DBObject getTopRecommendedRestaurants(MongoTemplate mongoTemplate) {
		//This circumvents exception translation
		DBCollection collection = mongoTemplate.getCollection("mvc");

		Date startDate = createDate(1, 5, 2010);
		Date endDate = createDate(1, 12, 2010);

		DBObject cond = QueryBuilder.start("date").greaterThanEquals(startDate).lessThan(endDate).and("action").is("addFavoriteRestaurant").get();
		DBObject key = new BasicDBObject("parameters.p1", true);

		DBObject intitial = new BasicDBObject("count", 0);
		DBObject result = collection.group(key, cond, intitial, "function(doc, out){ out.count++; }");


		//List<ParameterRanking> parameterRanking = mongoTemplate.queryForListGroupBy(groupQuery, ParameterRanking.class);

		return result;
	}

	private Date createDate(int day, int month, int year) {
		Calendar d = Calendar.getInstance();
		d.clear();
		d.set(Calendar.YEAR, year);
		d.set(Calendar.MONTH, month);
		d.set(Calendar.DATE, day);
		return d.getTime();
	}

}
