package ru.yandex.wmconsole.servantlet.graphics;

import java.awt.Dimension;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;
import org.springframework.beans.factory.annotation.Required;

import ru.yandex.common.framework.core.ServRequest;
import ru.yandex.common.framework.core.ServResponse;
import ru.yandex.wmconsole.data.info.DateCountInfo;
import ru.yandex.wmconsole.service.IWMCUserInfoService;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.error.InternalProblem;
import ru.yandex.wmtools.common.servantlet.AuthenticationServantlet;

/**
 * Created by IntelliJ IDEA.
 * Date: 18.02.2008
 * Time: 15:43:50
 * <p>
 * Returns JPEG of graph <quote>Number of true users</quote>,
 * i.e. users who visit our service at least <code>num_of_visits</code> (3) times
 * during last <code>num_of_days</code> (7) days
 *
 * <br>
 * Parameters:
 * <br>
 * reg_from optional Date (format "yyyy-MM-dd"): show from this date
 * <br>
 * reg_to optional Date (format "yyyy-MM-dd"): show till this date
 * <br>
 * show_diff optional boolean (default <code>false</code>):
 * <br>
 * If <code>true</code> show derivative, i.e. number of new true users per day
 * <br>
 * num_of_days optional integer (default <code>7</code>)
 * <br>
 * num_of_visits optional integer (default <code>3</code>)
 * </p>
 */
public class TrueUsersGraphServantlet extends AuthenticationServantlet {
    private static final String PARAM_REG_FROM = "reg_from";
    private static final String PARAM_REG_TO = "reg_to";
    private static final String PARAM_SHOW_DIFF = "show_diff";
    private static final String PARAM_NUM_OF_DAYS = "num_of_days";
    private static final String PARAM_NUM_OF_VISITS = "num_of_visits";

    private IWMCUserInfoService userInfoService;

    public byte[] createTestChart(boolean showDerivative, Date fromDate, Date toDate, int numOfDays, int numOfVisits) throws InternalException {
        final String chartTitle = "Число постоянных пользователей сервиса Яндекс.Вебмастер";
        final XYDataset dataset = getUsersDataset(showDerivative, fromDate, toDate, numOfDays, numOfVisits);

        final JFreeChart chart = ChartFactory.createTimeSeriesChart(chartTitle, "Дата", "Число пользователей", dataset, true, true, false);

        final XYPlot plot = chart.getXYPlot();
        final XYItemRenderer renderer = plot.getRenderer();
        renderer.setToolTipGenerator(StandardXYToolTipGenerator.getTimeSeriesInstance());
        if (renderer instanceof StandardXYItemRenderer) {
            final StandardXYItemRenderer rr = (StandardXYItemRenderer) renderer;
            rr.setShapesFilled(true);
        }

        final DateAxis axis = (DateAxis) plot.getDomainAxis();
        axis.setDateFormatOverride(new SimpleDateFormat("dd-MM-yy"));

        final ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setPreferredSize(new Dimension(800, 570));
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            ChartUtilities.writeChartAsJPEG(outputStream, chart, 800, 600);
        }
        catch (IOException e) {
            throw new InternalException(InternalProblem.PROCESSING_ERROR, "Cannot create writeChartAsJPEG!", e);
        }

        return outputStream.toByteArray();
    }

    private XYDataset getUsersDataset(boolean showDerivative, Date fromDate, Date toDate, int numOfDays, int numOfVisits) throws InternalException {
        TimeSeries timeSeries = new TimeSeries("Число пользователей", Day.class);
        List<DateCountInfo> date2count = userInfoService.getTrueUsersCount(fromDate, toDate, numOfDays, numOfVisits);
        Integer prev = null;
        for (DateCountInfo info: date2count) {
            if (showDerivative) {
                int diff = info.getCount();
                if (prev != null) {
                    diff -= prev;
                    timeSeries.add(new Day(info.getDate()), diff);
                }
                prev = info.getCount();
            }
            else {
                timeSeries.add(new Day(info.getDate()), info.getCount());
            }
        }
        final TimeSeriesCollection dataset = new TimeSeriesCollection();
        dataset.addSeries(timeSeries);

        return dataset;
    }

    @Override
    public void doProcess(ServRequest req, ServResponse res, long userId) throws InternalException {
        Boolean showDerivative = req.getParamAsBoolean(PARAM_SHOW_DIFF, false);
        String fromDateStr = req.getParam(PARAM_REG_FROM, true);
        Date fromDate = new Date(0);
        Date toDate = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        if (fromDateStr != null) {
            try {
                fromDate = dateFormat.parse(fromDateStr);
            } catch (ParseException e) {
                // use default value
            }
        }
        String toDateStr = req.getParam(PARAM_REG_TO, true);
        if (toDateStr != null) {
            try {
                toDate = dateFormat.parse(toDateStr);
            } catch (ParseException e) {
                // use default value
            }
        }
        int numOfDays = req.getParamAsInt(PARAM_NUM_OF_DAYS, 7);
        int numOfVisits = req.getParamAsInt(PARAM_NUM_OF_VISITS, 3);
        res.write(createTestChart(showDerivative, fromDate, toDate, numOfDays, numOfVisits));
    }

    @Required
    public void setUserInfoService(final IWMCUserInfoService userInfoService) {
        this.userInfoService = userInfoService;
    }
}
