package eu.dnetlib.client.statistics;

import com.github.gwtbootstrap.client.ui.*;
import com.github.gwtbootstrap.client.ui.constants.AlertType;
import com.github.gwtbootstrap.client.ui.constants.ToggleType;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.i18n.client.TimeZone;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import com.googlecode.gwt.charts.client.ChartLoader;
import com.googlecode.gwt.charts.client.ChartPackage;
import com.googlecode.gwt.charts.client.ColumnType;
import com.googlecode.gwt.charts.client.DataTable;
import com.googlecode.gwt.charts.client.geochart.GeoChart;
import com.googlecode.gwt.charts.client.geochart.GeoChartColorAxis;
import com.googlecode.gwt.charts.client.geochart.GeoChartOptions;
import com.googlecode.gwt.charts.client.table.Table;
import com.googlecode.gwt.charts.client.table.TableOptions;
import eu.dnetlib.client.*;
import eu.dnetlib.goldoa.domain.stats.*;
import eu.dnetlib.shared.DateSeriesData;
import eu.dnetlib.shared.StatsData;
import org.moxieapps.gwt.highcharts.client.*;
import org.moxieapps.gwt.highcharts.client.Legend;
import org.moxieapps.gwt.highcharts.client.Series;
import org.moxieapps.gwt.highcharts.client.labels.*;
import org.moxieapps.gwt.highcharts.client.plotOptions.*;

import java.util.*;

/**
 * Created by stefania on 11/19/15.
 */
public class StatisticsWidget implements MyWidget {

    private String token = "";

    private FlowPanel statisticsPagePanel = new FlowPanel();
    private Label statisticsTitleLabel = new Label();
    private HTML statisticsInfoLabel = new HTML();
    private HTML statisticsForDOIInfoLabel = new HTML();

    private Alert errorLabel = new Alert();

    private FlowPanel graphsPanel = new FlowPanel();

    private DataServiceAsync dataService = GWT.create(DataService.class);

    private String countriesActiveButton = "bar";
    private String organisationsActiveButton = "bar";
    private String publishersActiveButton = "bar";

    private String numberOfRequestsOverviewButton = "timeline";
    private String amountFundedOverviewButton = "timeline";

    private DateSeriesData numberDateSeriesData;
    private DateSeriesData amountDateSeriesData;

    private DateSeriesToolbarWidget numberOfRequestsToolbar;
    private DateSeriesToolbarWidget amountFundedToolbar;

    public StatisticsWidget() {

        statisticsPagePanel.addStyleName("content");
//        statisticsPagePanel.addStyleName("statisticsPage");

        statisticsTitleLabel.setText("Statistics");
        statisticsTitleLabel.addStyleName("contentTitleLabel");

        statisticsInfoLabel.addStyleName("contentInfoLabel");

        statisticsForDOIInfoLabel.addStyleName("contentInfoLabel");

        errorLabel.addStyleName("alertLabel");
        errorLabel.setType(AlertType.ERROR);
        errorLabel.setVisible(false);
        errorLabel.setClose(false);

        statisticsPagePanel.add(statisticsTitleLabel);
        statisticsPagePanel.add(errorLabel);
        statisticsPagePanel.add(statisticsInfoLabel);
        if(GoldOAPortal.currentUser!=null && Utils.currentUserHasRoleApproved("moderator"))
            statisticsPagePanel.add(statisticsForDOIInfoLabel);
        statisticsPagePanel.add(graphsPanel);
    }

    @Override
    public Widget asWidget() {
        return statisticsPagePanel;
    }

    @Override
    public void clear() {
        errorLabel.setVisible(false);
        graphsPanel.clear();
        statisticsInfoLabel.setHTML("");

        countriesActiveButton = "bar";
        organisationsActiveButton = "bar";
        publishersActiveButton = "bar";

        numberOfRequestsOverviewButton = "timeline";
        amountFundedOverviewButton = "timeline";

        numberDateSeriesData = null;
        amountDateSeriesData = null;

        numberOfRequestsToolbar = null;
        amountFundedToolbar = null;
    }

    @Override
    public void reload() {

        MyWidgetHelper.hideSidebar();

        SidebarPanel helpPanel = new SidebarPanel("Help");
        MyWidgetHelper.loadHelp(helpPanel, token.split("\\.")[0]);
    }

    @Override
    public void setToken(String token) {
        this.token = token;
    }

    @Override
    public void afterAdditionToRootPanel() {

        final HTML loadingWheel = new HTML("<div class=\"loader-big\"></div><div class=\"whiteFilm\"></div>");
        graphsPanel.addStyleName("loading");
        graphsPanel.add(loadingWheel);

        dataService.getStatsData(new AsyncCallback<StatsData>() {

            @Override
            public void onFailure(Throwable throwable) {

                graphsPanel.remove(loadingWheel);
                graphsPanel.removeStyleName("loading");

                errorLabel.setText("System error retrieving data");
                errorLabel.setVisible(true);
            }

            @Override
            public void onSuccess(final StatsData statsData) {

                ChartLoader chartLoader = new ChartLoader(ChartPackage.TABLE);
                chartLoader.loadApi(new Runnable() {

                    @Override
                    public void run() {

                        numberDateSeriesData = statsData.getDateSeriesData();
                        amountDateSeriesData = statsData.getDateSeriesData();

                        numberOfRequestsToolbar = new DateSeriesToolbarWidget(statsData.getBrowse());
                        amountFundedToolbar = new DateSeriesToolbarWidget(statsData.getBrowse());

                        graphsPanel.remove(loadingWheel);
                        graphsPanel.removeStyleName("loading");

                        if(statsData.getNumbers()!=null) {
                            statisticsInfoLabel.setHTML("<strong>" + statsData.getNumbers().getNumber(Numbers.Category.REQUESTS) +
                                    "</strong> publications from <strong>" + statsData.getNumbers().getNumber(Numbers.Category.PROJECTS) +
                                    "</strong> projects have been approved for funding since 1/1/2015.<br>" +
                                    "A total of <strong>" + NumberFormat.getFormat("###,###,##0").format(statsData.getNumbers().getNumber(Numbers.Category.TOTAL_AMOUNT)) +
                                    " EUR</strong> has been paid for APCs, with an average of <strong>" +
                                    NumberFormat.getFormat("###,###,##0").format(statsData.getNumbers().getNumber(Numbers.Category.AVERAGE_AMOUNT)) +
                                    " EUR</strong> per publication: " +
                                    "<p><ul><li><strong>" + statsData.getNumbers().getNumber(Numbers.Category.PAID_ARTICLE_REQUESTS) +
                                    "</strong> articles with an average of <strong>" +
                                    NumberFormat.getFormat("###,###,##0").format(statsData.getNumbers().getNumber(Numbers.Category.AVERAGE_PER_ARTICLE)) +
                                    " EUR</strong></li><li><strong>" + statsData.getNumbers().getNumber(Numbers.Category.PAID_MONOGRAPH_REQUESTS) +
                                    "</strong> books with an average of <strong>" +
                                    NumberFormat.getFormat("###,###,##0").format(statsData.getNumbers().getNumber(Numbers.Category.AVERAGE_PER_MONOGRAPH)) +
                                    " EUR</strong></li><li><strong> " + statsData.getNumbers().getNumber(Numbers.Category.PAID_BOOK_CHAPTER_REQUESTS) +
                                    "</strong> book chapters with an average of <strong>" +
                                    NumberFormat.getFormat("###,###,##0").format(statsData.getNumbers().getNumber(Numbers.Category.AVERAGE_PER_BOOK_CHAPTER)) +
                                    " EUR</strong></li></ul></p>");

                            statisticsForDOIInfoLabel.setHTML("Out of <strong>" + statsData.getNumbers().getNumber(Numbers.Category.REQUESTS) +
                                    "</strong> approved requests, <strong>" + statsData.getNumbers().getNumber(Numbers.Category.APPROVED_REQUESTS_WITH_DOI) +
                                    "</strong> have a DOI.<br>Out of <strong>" + statsData.getNumbers().getNumber(Numbers.Category.PAID_REQUESTS) +
                                    "</strong> paid requests, <strong>" + statsData.getNumbers().getNumber(Numbers.Category.PAID_REQUESTS_WITH_DOI) +
                                    "</strong> have a DOI.");
                        }

                        createOverviewPanel(statsData, graphsPanel);
                        createBreakdownByPanel(statsData, graphsPanel);
                        createMorePanel(statsData, graphsPanel);
                    }
                });
            }
        });
    }

    private void createMorePanel(StatsData statsData, FlowPanel panel) {

        panel.add(new HTML("<h5 class=\"statsHeader\">More ...</h5>"));

        FlowPanel contentPanel = new FlowPanel();
        contentPanel.addStyleName("statisticsContentPanel");
        contentPanel.addStyleName("paddingLeft25");
        contentPanel.addStyleName("paddingRight25");
        panel.add(contentPanel);

        createStatusDistributionPieChart(statsData, contentPanel);
        createPublicationTypeDistributionColumnChart(statsData, contentPanel);
    }

    private void createStatusDistributionPieChart(StatsData statsData, FlowPanel panel) {

        FlowPanel fundingRequestsPieChartPanel = new FlowPanel();
        fundingRequestsPieChartPanel.addStyleName("chart");
        fundingRequestsPieChartPanel.addStyleName("fundingRequestsPiePanel");

        final Map<String, String> namesMap = new HashMap<>();

        final Chart chart = new Chart()
                .setType(Series.Type.PIE)
                .setMargin(50, 130, 50, 130)
                .setChartTitleText("Request status shares")
                .setPlotBackgroundColor("none")
                .setPlotBorderWidth(0)
                .setPlotShadow(false)
                .setPiePlotOptions(new PiePlotOptions()
                        .setAllowPointSelect(true)
                        .setCursor(PlotOptions.Cursor.POINTER)
                        .setPieDataLabels(new PieDataLabels()
                                .setEnabled(true)
                                .setStyle(new Style().setFontSize("8px").setOption("width", "110px"))
                                .setFormatter(new DataLabelsFormatter() {
                                    public String format(DataLabelsData dataLabelsData) {
                                        return "<b>" + dataLabelsData.getPointName() + "</b>: " +
                                                NumberFormat.getFormat("###,###,##0").format(dataLabelsData.getYAsLong());
                                    }
                                })
                        )
                )
                .setToolTip(new ToolTip()
                        .setFormatter(new ToolTipFormatter() {
                            public String format(ToolTipData toolTipData) {
                                return "<b>" + toolTipData.getSeriesName() + "</b><br/>" +
                                        namesMap.get(toolTipData.getPointName()) + ": " +
                                        NumberFormat.getFormat("###,###,##0").format(toolTipData.getYAsLong());
                            }
                        })
                )
                .setCredits(new Credits()
                        .setText("from OpenAIRE via Highcharts (date: " + DateTimeFormat.getFormat("yyyy-MM-dd").format(new Date()) + ")")
                        .setStyle(new Style().setMargin("10px"))
                        .setX(-10)
                        .setY(-2)
                        .setVerticalAlign(Credits.VerticalAlign.BOTTOM));

        List<Quadruple<String, Integer, Float, Float>> data = statsData.getStatusesDistribution();

        Point[] fundedRequests = new Point[data.size()];

        for(int i=0; i<data.size(); i++) {
            fundedRequests[i] = new Point(data.get(i).getFirst(), data.get(i).getSecond());
            namesMap.put(data.get(i).getFirst(), data.get(i).getFirst());
        }

        chart.addSeries(chart.createSeries()
                .setName("Funded requests")
                .setPoints(fundedRequests)
        );

        chart.setWidth("100%");
        chart.setHeight("100%");

        fundingRequestsPieChartPanel.add(chart);

        panel.add(fundingRequestsPieChartPanel);

        chart.setSizeToMatchContainer();
    }

    private void createPublicationTypeDistributionColumnChart(StatsData statsData, FlowPanel panel) {

        FlowPanel fundingRequestsBarChartPanel = new FlowPanel();
        fundingRequestsBarChartPanel.addStyleName("chart");
        fundingRequestsBarChartPanel.addStyleName("fundingRequestsDoubleColumnPanel");

        Chart chart = new Chart()
                .setType(Series.Type.COLUMN)
                .setChartTitleText("Distribution per publication type")
                .setColumnPlotOptions(new ColumnPlotOptions()
                        .setPointPadding(0.2)
                        .setBorderWidth(0)
                )
                .setLegend(new Legend()
                        .setBackgroundColor("#FFFFFF")
                        .setReversed(true)
                )
                .setToolTip(new ToolTip()
                        .setFormatter(new ToolTipFormatter() {
                            public String format(ToolTipData toolTipData) {
                                return "<b>" + toolTipData.getXAsString() + "</b><br/>" +
                                        toolTipData.getSeriesName() + ": " + NumberFormat.getFormat("###,###,##0").format(toolTipData.getYAsLong()) + "<br/>";
                            }
                        })
                )
                .setCredits(new Credits()
                        .setText("from OpenAIRE via Highcharts (date: " + DateTimeFormat.getFormat("yyyy-MM-dd").format(new Date()) + ")")
                        .setStyle(new Style().setMargin("10px"))
                        .setX(-10)
                        .setY(-2)
                        .setVerticalAlign(Credits.VerticalAlign.BOTTOM));

        List<Quadruple<String, Integer, Float, Float>> data = statsData.getPublicationTypeDistribution();

        String[] categories = new String[data.size()];
        Integer[] fundedRequests = new Integer[data.size()];
        Float[] amountSpent = new Float[data.size()];

        for(int i=0; i<data.size() && i<15; i++) {
            categories[i] = data.get(i).getFirst();
            fundedRequests[i] = data.get(i).getSecond();
            amountSpent[i] = data.get(i).getThird();
        }

        chart.getXAxis()
                .setCategories(categories);

        chart.getYAxis(0)
                .setLabels(new YAxisLabels()
                        .setStyle(new Style()
                                .setColor("#cee3ab")
                        )
                        .setFormatter(new AxisLabelsFormatter() {
                            public String format(AxisLabelsData axisLabelsData) {
                                return NumberFormat.getFormat("###,###,##0").format(axisLabelsData.getValueAsLong());
                            }
                        })
                )
                .setAxisTitle(new AxisTitle()
                        .setText("Number of funded requests")
                        .setStyle(new Style()
                                .setColor("#cee3ab")
                        )
                )
                .setGridLineWidth(1);

        chart.getYAxis(1)
                .setLabels(new YAxisLabels()
                        .setStyle(new Style()
                                .setColor("#abc1e6")
                        )
                        .setFormatter(new AxisLabelsFormatter() {
                            public String format(AxisLabelsData axisLabelsData) {
                                return NumberFormat.getFormat("###,###,##0.00").format(axisLabelsData.getValueAsLong()) + " EUR";
                            }
                        })
                )
                .setAxisTitle(new AxisTitle()
                        .setText("Amount of money spent")
                        .setStyle(new Style()
                                .setColor("#abc1e6")
                        )
                )
                .setOpposite(true);

        chart.addSeries(chart.createSeries()
                .setName("Number of funded requests")
                .setType(Series.Type.COLUMN)
                .setPlotOptions(new SplinePlotOptions()
                        .setColor("#cee3ab")
                )
                .setPoints(fundedRequests)
        );

        chart.addSeries(chart.createSeries()
                .setName("Amount of money spent")
                .setType(Series.Type.COLUMN)
                .setPlotOptions(new ColumnPlotOptions()
                        .setColor("#abc1e6")
                )
                .setYAxis(1)
                .setPoints(amountSpent)
        );

        chart.setWidth("100%");
        chart.setHeight("100%");

        fundingRequestsBarChartPanel.add(chart);

        panel.add(fundingRequestsBarChartPanel);

        HTML comment = new HTML("<div class=\"comment fontItalic\">* The amount funded in this chart corresponds to requests " +
                "that have been fully processed and not to all approved requests.</div>");
        comment.addStyleName("amountFundedComment");
        panel.add(comment);

        chart.setSizeToMatchContainer();
    }

    private void createOverviewPanel(final StatsData statsData, FlowPanel panel) {

        panel.add(new HTML("<h5 class=\"statsHeader\">Overview</h5>"));

        FlowPanel contentPanel = new FlowPanel();
        contentPanel.addStyleName("statisticsContentPanel");
        panel.add(contentPanel);

        final TabPanel overviewsPanel = new TabPanel();
        overviewsPanel.setWidth("94%");

        final Tab numberOfRequestsTab = new Tab();
        final Tab amountFundedTab = new Tab();

        final FlowPanel numberOfRequestsPanel = new FlowPanel();
        contentPanel.setWidth("100%");

        final FlowPanel amountFundedPanel = new FlowPanel();
        amountFundedPanel.setWidth("100%");

        overviewsPanel.add(numberOfRequestsTab);
        overviewsPanel.add(amountFundedTab);

        overviewsPanel.setTabPosition("above");
        overviewsPanel.addStyleName("tabPanel");

        numberOfRequestsTab.setHeading("REQUESTS");
        numberOfRequestsTab.setActive(true);
        numberOfRequestsTab.add(numberOfRequestsPanel);

        amountFundedTab.setHeading("FUNDS");
        amountFundedTab.add(amountFundedPanel);

        overviewsPanel.addShownHandler(new TabPanel.ShownEvent.Handler() {
            @Override
            public void onShow(TabPanel.ShownEvent shownEvent) {

                int selectedTab = overviewsPanel.getSelectedTab();
                if(selectedTab==0) {
                    numberOfRequestsPanel.clear();
                    createOverviewTabPanel("number", statsData, numberOfRequestsPanel);
                } else {
                    amountFundedPanel.clear();
                    createOverviewTabPanel("amount", statsData, amountFundedPanel);
                }
            }
        });

        contentPanel.add(overviewsPanel);

        createOverviewTabPanel("number", statsData, numberOfRequestsPanel);
    }

    private void createOverviewTabPanel(final String category, final StatsData statsData, FlowPanel panel) {

        FlowPanel dataPanel = new FlowPanel();
        dataPanel.addStyleName("requestsOverTimeTabPanel");
        panel.add(dataPanel);

        FlowPanel toolbarPanel = new FlowPanel();
        toolbarPanel.addStyleName("dateSeriesToolbarPanel");
        panel.add(toolbarPanel);

        FlowPanel buttonBar = new FlowPanel();
        buttonBar.addStyleName("chartButtonBar");

        final FlowPanel contentPanel = new FlowPanel();
        contentPanel.setWidth("100%");

        ButtonGroup buttonGroup = new ButtonGroup();
        buttonGroup.setToggle(ToggleType.RADIO);
        buttonBar.add(buttonGroup);

        final Button dateSeriesButton = new Button();
        dateSeriesButton.add(new HTML("<i class=\"fa fa-line-chart fa-lg\"></i>"));
        dateSeriesButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent clickEvent) {
                contentPanel.clear();
                createFundingRequestsDateSeries(category, contentPanel);
                updateActiveButton(category, "timeline");
            }
        });
        buttonGroup.add(dateSeriesButton);

        Button mapButton = new Button();
        mapButton.add(new HTML("<i class=\"fa fa-globe fa-lg\"></i>"));
        mapButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent clickEvent) {
                contentPanel.clear();
                createFundingRequestsMap(category, statsData, contentPanel);
                updateActiveButton(category, "map");
            }
        });
        buttonGroup.add(mapButton);

        Button tableButton = new Button();
        tableButton.add(new HTML("<i class=\"fa fa fa-table fa-lg\"></i>"));
        tableButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent clickEvent) {
                contentPanel.clear();
                createFundingRequestsDateSeriesTable(category, contentPanel);
                updateActiveButton(category, "table");
            }
        });
        buttonGroup.add(tableButton);

        DateSeriesToolbarWidget.SelectionChangedListener selectionChangedListener = new DateSeriesToolbarWidget.SelectionChangedListener() {
            @Override
            public void selectionChanged(DateSeries.Over over, Date from, Date to, String[] publishers, String[] organizations, String[] countries, String[] scientificAreas) {

                final HTML loadingWheel = new HTML("<div class=\"loader-big\"></div><div class=\"whiteFilm\"></div>");
                contentPanel.addStyleName("loading");
                contentPanel.add(loadingWheel);

                dataService.getDateSeriesData(over, from, to, publishers, organizations, countries, scientificAreas,
                        new AsyncCallback<DateSeriesData>() {

                            @Override
                            public void onFailure(Throwable throwable) {

                                contentPanel.removeStyleName("loading");
                                contentPanel.clear();

                                Alert errorLabel = new Alert();
                                errorLabel.addStyleName("alertLabel");
                                errorLabel.setType(AlertType.ERROR);
                                errorLabel.setClose(false);
                                errorLabel.setText("System error retrieving data");

                                contentPanel.add(errorLabel);

                                if(category.equals("number"))
                                    numberDateSeriesData = null;
                                else
                                    amountDateSeriesData = null;
                            }

                            @Override
                            public void onSuccess(DateSeriesData dateSeriesData) {

                                contentPanel.removeStyleName("loading");
                                contentPanel.clear();

                                if(category.equals("number")) {

                                    numberDateSeriesData = dateSeriesData;

                                    if (dateSeriesData.getNewRequestsDateSeries() == null
                                            || dateSeriesData.getNewRequestsDateSeries().getData() == null
                                            || dateSeriesData.getNewRequestsDateSeries().getData().isEmpty()) {

                                        Alert warningLabel = new Alert();
                                        warningLabel.addStyleName("alertLabel");
                                        warningLabel.setType(AlertType.WARNING);
                                        warningLabel.setClose(false);
                                        warningLabel.setText("No data available for the current selections");

                                        contentPanel.add(warningLabel);

                                    } else {

                                        if(numberOfRequestsOverviewButton.equals("timeline")) {
                                            createFundingRequestsDateSeries(category, contentPanel);
                                        } else if(numberOfRequestsOverviewButton.equals("map")) {
                                            createFundingRequestsMap(category, statsData, contentPanel);
                                        } else {
                                            createFundingRequestsDateSeriesTable(category, contentPanel);
                                        }
                                    }

                                } else {

                                    amountDateSeriesData = dateSeriesData;

                                    if (dateSeriesData.getCumulativeRequestsDateSeries() == null
                                            || dateSeriesData.getCumulativeRequestsDateSeries().getData() == null
                                            || dateSeriesData.getCumulativeRequestsDateSeries().getData().isEmpty()) {

                                        Alert warningLabel = new Alert();
                                        warningLabel.addStyleName("alertLabel");
                                        warningLabel.setType(AlertType.WARNING);
                                        warningLabel.setClose(false);
                                        warningLabel.setText("No data available for the current selections");

                                        contentPanel.add(warningLabel);

                                    } else {

                                        if(amountFundedOverviewButton.equals("timeline")) {
                                            createFundingRequestsDateSeries(category, contentPanel);
                                        } else if(amountFundedOverviewButton.equals("map")) {
                                            createFundingRequestsMap(category, statsData, contentPanel);
                                        } else {
                                            createFundingRequestsDateSeriesTable(category, contentPanel);
                                        }
                                    }
                                }
                            }
                        });
            }
        };
        if(category.equals("number")) {
            numberOfRequestsToolbar.setSelectionChangedListener(selectionChangedListener);
            toolbarPanel.add(numberOfRequestsToolbar.asWidget());
        } else {
            amountFundedToolbar.setSelectionChangedListener(selectionChangedListener);
            toolbarPanel.add(amountFundedToolbar.asWidget());
        }

        dataPanel.add(buttonBar);
        dataPanel.add(contentPanel);

        if(category.equals("number")) {
            selectButton(numberOfRequestsOverviewButton, category, statsData, contentPanel, dateSeriesButton, mapButton, tableButton);
        } else {
            selectButton(amountFundedOverviewButton, category, statsData, contentPanel, dateSeriesButton, mapButton, tableButton);
        }

    }

    private void createFundingRequestsDateSeries(final String category, FlowPanel panel) {

        FlowPanel fundingRequestsBarChartPanel = new FlowPanel();
        fundingRequestsBarChartPanel.addStyleName("chart");
        fundingRequestsBarChartPanel.addStyleName("fundingRequestsDateSeriesPanel");

        final TimeZone tz = TimeZone.createTimeZone(0);

        String chartTitle = "";
        if(category.equals("number"))
            chartTitle = "Funding Requests over time";
        else
            chartTitle = "Funds granted over time";

        final Chart chart = new Chart()
                .setType(Series.Type.LINE)
                .setChartTitleText(chartTitle)
                .setZoomType(Chart.ZoomType.X_AND_Y)
                .setToolTip(new ToolTip()
                        .setFormatter(new ToolTipFormatter() {
                            public String format(ToolTipData toolTipData) {

                                if(category.equals("number")) {
                                    return DateTimeFormat.getFormat("yyyy-MM-dd").format(
                                            new Date(toolTipData.getXAsLong()), tz)
                                            + ": " + NumberFormat.getFormat("###,###,##0").format(toolTipData.getYAsLong());
                                } else {
                                    return DateTimeFormat.getFormat("yyyy-MM-dd").format(
                                            new Date(toolTipData.getXAsLong()), tz)
                                            + ": " + NumberFormat.getFormat("###,###,##0.00").format(toolTipData.getYAsLong()) + " EUR";
                                }
                            }
                        })
                )
                .setCredits(new Credits()
                        .setText("from OpenAIRE via Highcharts (date: " + DateTimeFormat.getFormat("yyyy-MM-dd").format(new Date()) + ")")
                        .setStyle(new Style().setMargin("10px"))
                        .setX(-10)
                        .setY(-2)
                        .setVerticalAlign(Credits.VerticalAlign.BOTTOM))
                .setLinePlotOptions(new LinePlotOptions()
                        .setMarker(new Marker()
                                .setRadius(3)
                        )
                );

        chart.getXAxis()
                .setType(Axis.Type.DATE_TIME)
                .setStartOnTick(true)
                .setEndOnTick(true)
                .setShowLastLabel(true)
                .setOption("ordinal", false)
                .setDateTimeLabelFormats(new DateTimeLabelFormats()
                        .setMonth("%b '%Y")
                );

        String chartYAxisTitle = "";
        if(category.equals("number"))
            chartYAxisTitle = "Number of funding requests";
        else
            chartYAxisTitle = "Funds granted";

        chart.getYAxis()
                .setAxisTitleText(chartYAxisTitle);

        DateSeriesData dateSeriesData = null;
        if(category.equals("number"))
            dateSeriesData = numberDateSeriesData;
        else
            dateSeriesData = amountDateSeriesData;

        Number[][] totalFundingRequestsParams = new Number[dateSeriesData.getCumulativeRequestsDateSeries().getData().size()][2];
        for(int j = 0; j<dateSeriesData.getCumulativeRequestsDateSeries().getData().size(); j++) {
            totalFundingRequestsParams[j][0] = dateSeriesData.getCumulativeRequestsDateSeries().getData().get(j).getFirst().getTime();
            if(category.equals("number"))
                totalFundingRequestsParams[j][1] = dateSeriesData.getCumulativeRequestsDateSeries().getData().get(j).getSecond();
            else
                totalFundingRequestsParams[j][1] = dateSeriesData.getCumulativeRequestsDateSeries().getData().get(j).getThird();
        }

        String chartCumulativeSeriesName = "";
        if(category.equals("number"))
            chartCumulativeSeriesName = "Total number of funding requests";
        else
            chartCumulativeSeriesName = "Total funds granted";

        chart.addSeries(chart.createSeries()
                .setName(chartCumulativeSeriesName)
                .setPlotOptions(
                        new LinePlotOptions()
                                .setColor("#DB843D")
                )
                .setPoints(totalFundingRequestsParams));

        Number[][] fundingRequestsParams = new Number[dateSeriesData.getNewRequestsDateSeries().getData().size()][2];
        for(int j = 0; j<dateSeriesData.getNewRequestsDateSeries().getData().size(); j++) {
            fundingRequestsParams[j][0] = dateSeriesData.getNewRequestsDateSeries().getData().get(j).getFirst().getTime();
            if(category.equals("number"))
                fundingRequestsParams[j][1] = dateSeriesData.getNewRequestsDateSeries().getData().get(j).getSecond();
            else
                fundingRequestsParams[j][1] = dateSeriesData.getNewRequestsDateSeries().getData().get(j).getThird();
        }

        String chartNewSeriesName = "";
        if(category.equals("number"))
            chartNewSeriesName = "New funding requests";
        else
            chartNewSeriesName = "New funds granted";

        chart.addSeries(chart.createSeries()
                .setName(chartNewSeriesName)
                .setPlotOptions(
                        new LinePlotOptions()
                                .setColor("#2B908F")
                )
                .setPoints(fundingRequestsParams));

        chart.setWidth("100%");
        chart.setHeight("100%");

        fundingRequestsBarChartPanel.add(chart);

        panel.add(fundingRequestsBarChartPanel);

        chart.setSizeToMatchContainer();
    }

    private void createFundingRequestsDateSeriesTable(String category, FlowPanel panel) {

        final FlowPanel fundingRequestsTablePanel = new FlowPanel();
        fundingRequestsTablePanel.addStyleName("fundingRequestsTablePanel");

        Table table = new Table();
        fundingRequestsTablePanel.add(table);

        DataTable dataTable = DataTable.create();
        dataTable.addColumn(ColumnType.DATE, "Date");

        if (category.equals("number")) {
            dataTable.addColumn(ColumnType.NUMBER, "New Funding Requests");
            dataTable.addColumn(ColumnType.NUMBER, "Total Funding Requests");
        } else {
            dataTable.addColumn(ColumnType.NUMBER, "New Funds");
            dataTable.addColumn(ColumnType.NUMBER, "Total Funds");
        }

        DateSeriesData dateSeriesData = null;
        if(category.equals("number"))
            dateSeriesData = numberDateSeriesData;
        else
            dateSeriesData = amountDateSeriesData;

        dataTable.addRows(dateSeriesData.getNewRequestsDateSeries().getData().size());

        for(int i = 0; i<dateSeriesData.getNewRequestsDateSeries().getData().size(); i++) {
            dataTable.setCell(i, 0, dateSeriesData.getNewRequestsDateSeries().getData().get(i).getFirst());
            if(category.equals("number")) {
                dataTable.setCell(i, 1, dateSeriesData.getNewRequestsDateSeries().getData().get(i).getSecond(),
                        NumberFormat.getFormat("###,###,##0").format(dateSeriesData.getNewRequestsDateSeries().getData().get(i).getSecond()));
                dataTable.setCell(i, 2, dateSeriesData.getCumulativeRequestsDateSeries().getData().get(i).getSecond(),
                        NumberFormat.getFormat("###,###,##0").format(dateSeriesData.getCumulativeRequestsDateSeries().getData().get(i).getSecond()));
            } else {
                dataTable.setCell(i, 1, dateSeriesData.getNewRequestsDateSeries().getData().get(i).getThird(),
                        NumberFormat.getFormat("###,###,##0.00").format(dateSeriesData.getNewRequestsDateSeries().getData().get(i).getThird()) + " EUR");
                dataTable.setCell(i, 2, dateSeriesData.getCumulativeRequestsDateSeries().getData().get(i).getThird(),
                        NumberFormat.getFormat("###,###,##0.00").format(dateSeriesData.getCumulativeRequestsDateSeries().getData().get(i).getThird()) + " EUR");
            }
        }

        TableOptions options = TableOptions.create();
        options.setAlternatingRowStyle(true);
        options.setShowRowNumber(true);

        table.draw(dataTable, options);

        if(category.equals("amount")) {
            HTML comment = new HTML("<div class=\"comment fontItalic\">* The total funds in this table correspond to requests " +
                    "that have been fully processed and not to all approved requests.</div>");
            comment.addStyleName("amountFundedTableComment");
            panel.add(comment);
        }

        panel.add(fundingRequestsTablePanel);
    }

    private void createFundingRequestsMap(String category, StatsData statsData, FlowPanel panel) {

        final FlowPanel fundingRequestsMapPanel = new FlowPanel();
        fundingRequestsMapPanel.addStyleName("fundingRequestsTablePanel");

        GeoChart geoChart = new GeoChart();
        fundingRequestsMapPanel.add(geoChart);

        DataTable dataTable = DataTable.create();
        dataTable.addColumn(ColumnType.STRING, "Country");
        if(category.equals("number"))
            dataTable.addColumn(ColumnType.NUMBER, "Funded Requests");
        else
            dataTable.addColumn(ColumnType.NUMBER, "Total funds");

        List<Quintuple<String, Integer, Integer, Float, Float>> data = statsData.getCountriesFullInfo();

        int dataCounter = 0;
        for(int j=0; j<data.size(); j++) {
            if(category.equals("number")) {
                if (data.get(j).getSecond() > 0)
                    dataCounter++;
            } else {
                if (data.get(j).getFourth() > 0)
                    dataCounter++;
            }
        }

        int j=0;
        dataTable.addRows(dataCounter);
        for(int i=0; i<data.size(); i++) {
            if(category.equals("number")) {
                if (data.get(i).getSecond() > 0) {
                    dataTable.setCell(j, 0, data.get(i).getFirst());
                    dataTable.setCell(j, 1, data.get(i).getSecond(), NumberFormat.getFormat("###,###,##0").format(data.get(i).getSecond()));
                    j++;
                }
            } else {
                if (data.get(i).getFourth() > 0) {
                    dataTable.setCell(j, 0, data.get(i).getFirst());
                    dataTable.setCell(j, 1, data.get(i).getFourth(), NumberFormat.getFormat("###,###,##0.00").format(data.get(i).getFourth()) + " EUR");
                    j++;
                }
            }
        }

        GeoChartOptions options = GeoChartOptions.create();
        GeoChartColorAxis geoChartColorAxis = GeoChartColorAxis.create();
        geoChartColorAxis.setColors("#FFEDA0", "#FED976", "#FEB24C", "#FD8D3C", "#FC4E2A", "#E31A1C", "#BD0026", "#800026");
        options.setColorAxis(geoChartColorAxis);
        options.setDatalessRegionColor("#fffade");
        options.setRegion("150");

        geoChart.draw(dataTable, options);

        panel.add(fundingRequestsMapPanel);

        HTML comment = new HTML("<div class=\"comment fontItalic\">* The figures on this map are not affected by your " +
                "choices on the right</div>");
        comment.addStyleName("commentMap");
        panel.add(comment);

        geoChart.setSize("100%", "100%");
    }

    private void createBreakdownByPanel(final StatsData statsData, FlowPanel panel) {

        panel.add(new HTML("<h5 class=\"statsHeader\">Breakdown by</h5>"));

        FlowPanel contentPanel = new FlowPanel();
        contentPanel.addStyleName("statisticsContentPanel");
        panel.add(contentPanel);

        final TabPanel breakdownsPanel = new TabPanel();
        breakdownsPanel.setWidth("94%");

        final Tab countriesTab = new Tab();
        final Tab organizationsTab = new Tab();
        final Tab publishersTab = new Tab();

        final FlowPanel countriesPanel = new FlowPanel();
        countriesPanel.setWidth("100%");

        final FlowPanel organizationsPanel = new FlowPanel();
        organizationsPanel.setWidth("100%");

        final FlowPanel publishersPanel = new FlowPanel();
        publishersPanel.setWidth("100%");

        breakdownsPanel.add(countriesTab);
        breakdownsPanel.add(organizationsTab);
        breakdownsPanel.add(publishersTab);

        breakdownsPanel.setTabPosition("above");
        breakdownsPanel.addStyleName("tabPanel");

        countriesTab.setHeading("COUNTRY");
        countriesTab.setActive(true);
        countriesTab.add(countriesPanel);

        organizationsTab.setHeading("ORGANISATION");
        organizationsTab.add(organizationsPanel);

        publishersTab.setHeading("PUBLISHER");
        publishersTab.add(publishersPanel);

        breakdownsPanel.addShownHandler(new TabPanel.ShownEvent.Handler() {
            @Override
            public void onShow(TabPanel.ShownEvent shownEvent) {

                int selectedTab = breakdownsPanel.getSelectedTab();
                if(selectedTab==0) {
                    countriesPanel.clear();
                    createBreakdownTabPanel(eu.dnetlib.goldoa.domain.stats.Series.Category.COUNTRY, statsData, countriesPanel);
                } else if(selectedTab==1) {
                    organizationsPanel.clear();
                    createBreakdownTabPanel(eu.dnetlib.goldoa.domain.stats.Series.Category.ORGANIZATION, statsData, organizationsPanel);
                } else {
                    publishersPanel.clear();
                    createBreakdownTabPanel(eu.dnetlib.goldoa.domain.stats.Series.Category.PUBLISHER, statsData, publishersPanel);
                }
            }
        });

        contentPanel.add(breakdownsPanel);

        createBreakdownTabPanel(eu.dnetlib.goldoa.domain.stats.Series.Category.COUNTRY, statsData, countriesPanel);
    }

    private void createBreakdownTabPanel(final eu.dnetlib.goldoa.domain.stats.Series.Category category, final StatsData statsData, FlowPanel panel) {

        FlowPanel buttonBar = new FlowPanel();
        buttonBar.addStyleName("chartButtonBar");

        final FlowPanel contentPanel = new FlowPanel();
        contentPanel.setWidth("100%");

        ButtonGroup buttonGroup = new ButtonGroup();
        buttonGroup.setToggle(ToggleType.RADIO);
        buttonBar.add(buttonGroup);

        Button barButton = new Button();
        barButton.add(new HTML("<i class=\"fa fa-bar-chart-o fa-rotate-90 fa-lg\"></i>"));
        barButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent clickEvent) {
                contentPanel.clear();
                createFundingRequestsBarChart(category, statsData, contentPanel);
                createAmountFundedBarChart(category, statsData, contentPanel);
                updateActiveButton(category, "bar");
            }
        });
        buttonGroup.add(barButton);

        Button pieButton = new Button();
        pieButton.add(new HTML("<i class=\"fa fa-pie-chart fa-lg\"></i>"));
        pieButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent clickEvent) {
                contentPanel.clear();
                createFundingRequestsPieChart(category, statsData, contentPanel);
                createAmountFundedPieChart(category, statsData, contentPanel);
                updateActiveButton(category, "pie");
            }
        });
        buttonGroup.add(pieButton);

        Button tableButton = new Button();
        tableButton.add(new HTML("<i class=\"fa fa fa-table fa-lg\"></i>"));
        tableButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent clickEvent) {
                contentPanel.clear();
                createFundingRequestsTable(category, statsData, contentPanel);
                updateActiveButton(category, "table");
            }
        });
        buttonGroup.add(tableButton);

        panel.add(buttonBar);
        panel.add(contentPanel);

        if(category.equals(eu.dnetlib.goldoa.domain.stats.Series.Category.COUNTRY)) {
            selectButton(countriesActiveButton, category, statsData, contentPanel, barButton, pieButton, tableButton);
        } else if(category.equals(eu.dnetlib.goldoa.domain.stats.Series.Category.ORGANIZATION)) {
            selectButton(organisationsActiveButton, category, statsData, contentPanel, barButton, pieButton, tableButton);
        } else {
            selectButton(publishersActiveButton, category, statsData, contentPanel, barButton, pieButton, tableButton);
        }

    }

    private void updateActiveButton(String category, String activeButton) {

        if(category.equals("number")) {
            numberOfRequestsOverviewButton = activeButton;
        } else {
            amountFundedOverviewButton = activeButton;
        }
    }

    private void updateActiveButton(eu.dnetlib.goldoa.domain.stats.Series.Category category, String activeButton) {

        if(category.equals(eu.dnetlib.goldoa.domain.stats.Series.Category.COUNTRY)) {
            countriesActiveButton = activeButton;
        } else if(category.equals(eu.dnetlib.goldoa.domain.stats.Series.Category.ORGANIZATION)) {
            organisationsActiveButton = activeButton;
        } else {
            publishersActiveButton = activeButton;
        }
    }

    private void selectButton(String activeButton, String category,
                              StatsData statsData, FlowPanel contentPanel, Button dateSeries, Button mapButton, Button tableButton) {

        if(activeButton.equals("timeline")) {
            dateSeries.setActive(true);
            createFundingRequestsDateSeries(category, contentPanel);
        } else if(activeButton.equals("map")) {
            mapButton.setActive(true);
            createFundingRequestsMap(category, statsData, contentPanel);
        } else {
            tableButton.setActive(true);
            createFundingRequestsDateSeriesTable(category, contentPanel);
        }
    }

    private void selectButton(String activeButton, eu.dnetlib.goldoa.domain.stats.Series.Category category,
                              StatsData statsData, FlowPanel contentPanel, Button barButton, Button pieButton, Button tableButton) {

        if(activeButton.equals("bar")) {
            barButton.setActive(true);
            createFundingRequestsBarChart(category, statsData, contentPanel);
            createAmountFundedBarChart(category, statsData, contentPanel);
        } else if(activeButton.equals("pie")) {
            pieButton.setActive(true);
            createFundingRequestsPieChart(category, statsData, contentPanel);
            createAmountFundedPieChart(category, statsData, contentPanel);
        } else {
            tableButton.setActive(true);
            createFundingRequestsTable(category, statsData, contentPanel);
        }
    }

    private void createFundingRequestsBarChart(eu.dnetlib.goldoa.domain.stats.Series.Category category,
                                               StatsData statsData, FlowPanel panel) {

        FlowPanel fundingRequestsBarChartPanel = new FlowPanel();
        fundingRequestsBarChartPanel.addStyleName("chart");
        fundingRequestsBarChartPanel.addStyleName("fundingRequestsStackedBarPanel");

        final Map<String, String> namesMap = new HashMap<>();

        Chart chart = new Chart()
                .setType(Series.Type.BAR)
                .setChartTitleText("Funding Requests per " + category.getValue())
                .setChartSubtitleText("top 15")
                .setSeriesPlotOptions(new SeriesPlotOptions()
                        .setStacking(PlotOptions.Stacking.NORMAL)
                        .setDataLabels(new DataLabels()
                                .setStyle(new Style().setFontSize("8px")))
                )
                .setLegend(new Legend()
                        .setBackgroundColor("#FFFFFF")
                        .setReversed(true)
                )
                .setToolTip(new ToolTip()
                        .setFormatter(new ToolTipFormatter() {
                            public String format(ToolTipData toolTipData) {
                                return "<b>" + namesMap.get(toolTipData.getXAsString()) + "</b><br/>" +
                                        toolTipData.getSeriesName() + ": " + NumberFormat.getFormat("###,###,##0").format(toolTipData.getYAsLong()) + "<br/>" +
                                        "Total: " + NumberFormat.getFormat("###,###,##0").format(toolTipData.getTotal());
                            }
                        })
                )
                .setCredits(new Credits()
                        .setText("from OpenAIRE via Highcharts (date: " + DateTimeFormat.getFormat("yyyy-MM-dd").format(new Date()) + ")")
                        .setStyle(new Style().setMargin("10px"))
                        .setX(-10)
                        .setY(-2)
                        .setVerticalAlign(Credits.VerticalAlign.BOTTOM));

        List<Triple<String, Integer, Integer>> data = new ArrayList<>();
        if(category.equals(eu.dnetlib.goldoa.domain.stats.Series.Category.COUNTRY))
            data.addAll(statsData.getRequestsPerCountry());
        else if(category.equals(eu.dnetlib.goldoa.domain.stats.Series.Category.ORGANIZATION))
            data.addAll(statsData.getRequestsPerOrganisation());
        else
            data.addAll(statsData.getRequestsPerPublisher());

        String[] categories = new String[Math.min(data.size(), 15)];
        Integer[] fundedRequests = new Integer[Math.min(data.size(), 15)];
        Integer[] rejectedRequests = new Integer[Math.min(data.size(), 15)];

        for(int i=0; i<data.size() && i<15; i++) {

            if(data.get(i).getFirst() != null && data.get(i).getFirst().length()>15)
                categories[i] = data.get(i).getFirst().substring(0, 15) + "...";
            else
                categories[i] = data.get(i).getFirst();
            namesMap.put(categories[i], data.get(i).getFirst());

            fundedRequests[i] = data.get(i).getSecond();
            rejectedRequests[i] = data.get(i).getThird();

        }

        chart.getXAxis()
                .setCategories(categories);

        chart.getYAxis()
                .setMin(0)
                .setAxisTitleText("Number of requests")
                .setStackLabels(new StackLabels()
                        .setEnabled(true)
                        .setStyle(new Style()
                                .setFontWeight("bold")
                                .setColor("gray")
                                .setFontSize("8px")
                        )
                );

        chart.addSeries(chart.createSeries()
                .setName("Funded requests")
                .setPlotOptions(
                        new LinePlotOptions()
                                .setColor("#80699B")
                )
                .setPoints(fundedRequests)
        );
        chart.addSeries(chart.createSeries()
                .setName("Rejected requests")
                .setPlotOptions(
                        new LinePlotOptions()
                                .setColor("#3D96AE")
                )
                .setPoints(rejectedRequests)
        );

        chart.setWidth("100%");
        chart.setHeight("100%");

        fundingRequestsBarChartPanel.add(chart);

        panel.add(fundingRequestsBarChartPanel);

        chart.setSizeToMatchContainer();
    }

    private void createAmountFundedBarChart(eu.dnetlib.goldoa.domain.stats.Series.Category category,
                                               StatsData statsData, FlowPanel panel) {

        FlowPanel amountFundedBarChartPanel = new FlowPanel();
        amountFundedBarChartPanel.addStyleName("chart");
        amountFundedBarChartPanel.addStyleName("amountFundedBarPanel");

        final Map<String, String> namesMap = new HashMap<>();

        final Chart chart = new Chart()
                .setType(Series.Type.BAR)
                .setChartTitleText("Funds per " + category.getValue())
                .setChartSubtitleText("top 15")
                .setSeriesPlotOptions(new SeriesPlotOptions()
                        .setDataLabels(new DataLabels()
                                .setStyle(new Style().setFontSize("8px"))
                        )
                )
                .setLegend(new Legend()
                        .setBackgroundColor("#FFFFFF")
                        .setReversed(true)
                )
                .setToolTip(new ToolTip()
                        .setFormatter(new ToolTipFormatter() {
                            public String format(ToolTipData toolTipData) {
                                return "<b>" + namesMap.get(toolTipData.getXAsString()) + "</b><br/>" +
                                        toolTipData.getSeriesName() + ": " + NumberFormat.getFormat("###,###,##0.00").format(toolTipData.getYAsLong()) + " EUR";
                            }
                        })
                )
                .setCredits(new Credits()
                        .setText("from OpenAIRE via Highcharts (date: " + DateTimeFormat.getFormat("yyyy-MM-dd").format(new Date()) + ")")
                        .setStyle(new Style().setMargin("10px"))
                        .setX(-10)
                        .setY(-2)
                        .setVerticalAlign(Credits.VerticalAlign.BOTTOM));

        List<Triple<String, Float, Float>> data = new ArrayList<>();
        if(category.equals(eu.dnetlib.goldoa.domain.stats.Series.Category.COUNTRY))
            data.addAll(statsData.getFundsPerCountry());
        else if(category.equals(eu.dnetlib.goldoa.domain.stats.Series.Category.ORGANIZATION))
            data.addAll(statsData.getFundsPerOrganisation());
        else
            data.addAll(statsData.getFundsPerPublisher());

        String[] categories = new String[Math.min(data.size(), 15)];
        Float[] amountFunded = new Float[Math.min(data.size(), 15)];

        for(int i=0; i<data.size() && i<15; i++) {

            if(data.get(i).getFirst() != null && data.get(i).getFirst().length()>15)
                categories[i] = data.get(i).getFirst().substring(0, 15) + "...";
            else
                categories[i] = data.get(i).getFirst();
            namesMap.put(categories[i], data.get(i).getFirst());

            amountFunded[i] = data.get(i).getSecond();
        }

        chart.getXAxis()
                .setCategories(categories);

        chart.getYAxis()
                .setMin(0)
                .setAxisTitleText("Funds")
                .setStackLabels(new StackLabels()
                        .setEnabled(true)
                        .setStyle(new Style()
                                .setFontWeight("bold")
                                .setColor("gray")
                                .setFontSize("8px")
                        )
                        .setFormatter(new StackLabelsFormatter() {
                            @Override
                            public String format(StackLabelsData stackLabelsData) {
                                return NumberFormat.getFormat("###,###,##0.00").format(stackLabelsData.getTotalAsLong()) + " EUR";
                            }
                        })
                );

        chart.addSeries(chart.createSeries()
                .setName("Total funds")
                .setPlotOptions(
                        new LinePlotOptions()
                                .setColor("#80699B")
                )
                .setPoints(amountFunded)
        );

        Point[] avgAmountFunded = new Point[Math.min(data.size(), 15)];

        for(int i=0; i<data.size() && i<15; i++) {

            if(data.get(i).getFirst() != null && data.get(i).getFirst().length()>15) {
                avgAmountFunded[i] = new Point(data.get(i).getFirst().substring(0, 15) + "...", data.get(i).getThird());
            } else {
                avgAmountFunded[i] = new Point(data.get(i).getFirst(), data.get(i).getThird());
            }
        }

        chart.addSeries(chart.createSeries()
                .setName("Average funds")
                .setPlotOptions(
                        new LinePlotOptions()
                                .setColor("#3D96AE")
                )
                .setPoints(avgAmountFunded)
        );

        chart.setWidth("100%");
        chart.setHeight("100%");

        amountFundedBarChartPanel.add(chart);

        panel.add(amountFundedBarChartPanel);

        HTML comment = new HTML("<div class=\"comment fontItalic\">* The total and average funds in " +
                "this chart correspond to requests that have been fully processed and not to all approved requests.</div>");
        comment.addStyleName("amountFundedComment");
        panel.add(comment);

        chart.setSizeToMatchContainer();
    }

    private void createFundingRequestsPieChart(eu.dnetlib.goldoa.domain.stats.Series.Category category,
                                               StatsData statsData, FlowPanel panel) {

        FlowPanel fundingRequestsPieChartPanel = new FlowPanel();
        fundingRequestsPieChartPanel.addStyleName("chart");
        fundingRequestsPieChartPanel.addStyleName("fundingRequestsPiePanel");

        final Map<String, String> namesMap = new HashMap<>();

        final Chart chart = new Chart()
                .setType(Series.Type.PIE)
                .setMargin(50, 130, 50, 130)
                .setChartTitleText("Funded Requests per " + category.getValue())
                .setChartSubtitleText("top 15")
                .setPlotBackgroundColor("none")
                .setPlotBorderWidth(0)
                .setPlotShadow(false)
                .setPiePlotOptions(new PiePlotOptions()
                        .setAllowPointSelect(true)
                        .setCursor(PlotOptions.Cursor.POINTER)
                        .setPieDataLabels(new PieDataLabels()
                                .setEnabled(true)
                                .setStyle(new Style().setFontSize("8px").setOption("width", "110px"))
                                .setFormatter(new DataLabelsFormatter() {
                                    public String format(DataLabelsData dataLabelsData) {
                                        return "<b>" + dataLabelsData.getPointName() + "</b>: " +
                                                NumberFormat.getFormat("###,###,##0").format(dataLabelsData.getYAsLong());
                                    }
                                })
                        )
                )
                .setToolTip(new ToolTip()
                        .setFormatter(new ToolTipFormatter() {
                            public String format(ToolTipData toolTipData) {
                                return "<b>" + toolTipData.getSeriesName() + "</b><br/>" +
                                        namesMap.get(toolTipData.getPointName()) + ": " +
                                        NumberFormat.getFormat("###,###,##0").format(toolTipData.getYAsLong());
                            }
                        })
                )
                .setCredits(new Credits()
                        .setText("from OpenAIRE via Highcharts (date: " + DateTimeFormat.getFormat("yyyy-MM-dd").format(new Date()) + ")")
                        .setStyle(new Style().setMargin("10px"))
                        .setX(-10)
                        .setY(-2)
                        .setVerticalAlign(Credits.VerticalAlign.BOTTOM));

        List<Triple<String, Integer, Integer>> data = new ArrayList<>();
        if(category.equals(eu.dnetlib.goldoa.domain.stats.Series.Category.COUNTRY))
            data.addAll(statsData.getRequestsPerCountry());
        else if(category.equals(eu.dnetlib.goldoa.domain.stats.Series.Category.ORGANIZATION))
            data.addAll(statsData.getRequestsPerOrganisation());
        else
            data.addAll(statsData.getRequestsPerPublisher());

        Point[] fundedRequests = new Point[Math.min(data.size(), 15)];

        for(int i=0; i<data.size() && i<15; i++) {

            if(data.get(i).getFirst() != null && data.get(i).getFirst().length()>15) {
                fundedRequests[i] = new Point(data.get(i).getFirst().substring(0, 15) + "...", data.get(i).getSecond());
                namesMap.put(data.get(i).getFirst().substring(0, 15) + "...", data.get(i).getFirst());
            } else {
                fundedRequests[i] = new Point(data.get(i).getFirst(), data.get(i).getSecond());
                namesMap.put(data.get(i).getFirst(), data.get(i).getFirst());
            }
        }

        chart.addSeries(chart.createSeries()
                .setName("Funded requests")
                .setPoints(fundedRequests)
        );

        chart.setWidth("100%");
        chart.setHeight("100%");

        fundingRequestsPieChartPanel.add(chart);

        panel.add(fundingRequestsPieChartPanel);

        chart.setSizeToMatchContainer();
    }

    private void createAmountFundedPieChart(eu.dnetlib.goldoa.domain.stats.Series.Category category,
                                               StatsData statsData, FlowPanel panel) {

        FlowPanel fundingRequestsPieChartPanel = new FlowPanel();
        fundingRequestsPieChartPanel.addStyleName("chart");
        fundingRequestsPieChartPanel.addStyleName("amountFundedPiePanel");

        final Map<String, String> namesMap = new HashMap<>();

        final Chart chart = new Chart()
                .setType(Series.Type.PIE)
                .setMargin(50, 130, 50, 130)
                .setChartTitleText("Total funds per " + category.getValue())
                .setChartSubtitleText("top 15")
                .setPlotBackgroundColor("none")
                .setPlotBorderWidth(0)
                .setPlotShadow(false)
                .setPiePlotOptions(new PiePlotOptions()
                        .setAllowPointSelect(true)
                        .setCursor(PlotOptions.Cursor.POINTER)
                        .setPieDataLabels(new PieDataLabels()
                                .setEnabled(true)
                                .setStyle(new Style().setFontSize("8px").setOption("width", "110px"))
                                .setFormatter(new DataLabelsFormatter() {
                                    public String format(DataLabelsData dataLabelsData) {
                                        return "<b>" + dataLabelsData.getPointName() + "</b>: " + NumberFormat.getFormat("###,###,##0.00").format(dataLabelsData.getYAsLong()) + " EUR" ;
                                    }
                                })
                        )
                )
                .setToolTip(new ToolTip()
                        .setFormatter(new ToolTipFormatter() {
                            public String format(ToolTipData toolTipData) {
                                return "<b>" + toolTipData.getSeriesName() + "</b><br/>" +
                                        namesMap.get(toolTipData.getPointName()) + ": " + NumberFormat.getFormat("###,###,##0.00").format(toolTipData.getYAsLong()) + " EUR";
                            }
                        })
                )
                .setCredits(new Credits()
                        .setText("from OpenAIRE via Highcharts (date: " + DateTimeFormat.getFormat("yyyy-MM-dd").format(new Date()) + ")")
                        .setStyle(new Style().setMargin("10px"))
                        .setX(-10)
                        .setY(-2)
                        .setVerticalAlign(Credits.VerticalAlign.BOTTOM));

        List<Tuple<String, Float>> data = new ArrayList<>();
        if(category.equals(eu.dnetlib.goldoa.domain.stats.Series.Category.COUNTRY))
            data.addAll(statsData.getFundsPerCountry());
        else if(category.equals(eu.dnetlib.goldoa.domain.stats.Series.Category.ORGANIZATION))
            data.addAll(statsData.getFundsPerOrganisation());
        else
            data.addAll(statsData.getFundsPerPublisher());

        Point[] amountFunded = new Point[Math.min(data.size(), 15)];

        for(int i=0; i<data.size() && i<15; i++) {

            if(data.get(i).getFirst() != null && data.get(i).getFirst().length()>15) {
                amountFunded[i] = new Point(data.get(i).getFirst().substring(0, 15) + "...", data.get(i).getSecond());
                namesMap.put(data.get(i).getFirst().substring(0, 15) + "...", data.get(i).getFirst());
            } else {
                amountFunded[i] = new Point(data.get(i).getFirst(), data.get(i).getSecond());
                namesMap.put(data.get(i).getFirst(), data.get(i).getFirst());
            }
        }

        chart.addSeries(chart.createSeries()
                .setName("Total funds")
                .setPoints(amountFunded)
        );

        chart.setWidth("100%");
        chart.setHeight("100%");

        fundingRequestsPieChartPanel.add(chart);

        panel.add(fundingRequestsPieChartPanel);

        HTML comment = new HTML("<div class=\"comment fontItalic\">* The funds in " +
                "this chart correspond to requests that have been fully processed and not to all approved requests.</div>");
        comment.addStyleName("amountFundedComment");
        panel.add(comment);

        chart.setSizeToMatchContainer();
    }

    private void createFundingRequestsTable(eu.dnetlib.goldoa.domain.stats.Series.Category category,
                                            StatsData statsData, FlowPanel panel) {

        final FlowPanel fundingRequestsTablePanel = new FlowPanel();
        fundingRequestsTablePanel.addStyleName("fundingRequestsTablePanel");

        Table table = new Table();
        fundingRequestsTablePanel.add(table);

        DataTable dataTable = DataTable.create();
        dataTable.addColumn(ColumnType.STRING, category.getValue());
        dataTable.addColumn(ColumnType.NUMBER, "Funded Requests");
        dataTable.addColumn(ColumnType.NUMBER, "Rejected Requests");
        dataTable.addColumn(ColumnType.NUMBER, "Total Funds");
        dataTable.addColumn(ColumnType.NUMBER, "Average Funds");

        List<Quintuple<String, Integer, Integer, Float, Float>> data = new ArrayList<>();
        if(category.equals(eu.dnetlib.goldoa.domain.stats.Series.Category.COUNTRY))
            data.addAll(statsData.getCountriesFullInfo());
        else if(category.equals(eu.dnetlib.goldoa.domain.stats.Series.Category.ORGANIZATION))
            data.addAll(statsData.getOrganisationsFullInfo());
        else
            data.addAll(statsData.getPublishersFullInfo());

        dataTable.addRows(data.size());

        for(int i=0; i<data.size(); i++) {
            dataTable.setCell(i, 0, data.get(i).getFirst());
            dataTable.setCell(i, 1, data.get(i).getSecond(), NumberFormat.getFormat("###,###,##0").format(data.get(i).getSecond()));
            dataTable.setCell(i, 2, data.get(i).getThird(), NumberFormat.getFormat("###,###,##0").format(data.get(i).getThird()));
            dataTable.setCell(i, 3, data.get(i).getFourth(), NumberFormat.getFormat("###,###,##0.00").format(data.get(i).getFourth()) + " EUR");
            dataTable.setCell(i, 4, data.get(i).getFifth(), NumberFormat.getFormat("###,###,##0.00").format(data.get(i).getFifth()) + " EUR");
        }

        TableOptions options = TableOptions.create();
        options.setAlternatingRowStyle(true);
        options.setShowRowNumber(true);

        table.draw(dataTable, options);

        HTML comment = new HTML("<div class=\"comment fontItalic\">* The total and average funds in " +
                "this table correspond to requests that have been fully processed and not to all approved requests.</div>");
        comment.addStyleName("amountFundedTableComment");
        panel.add(comment);

        panel.add(fundingRequestsTablePanel);
    }
}
