package com.capgemini.reports.jasper.fillers;

import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRMapCollectionDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.Box.Filler;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * This class is able to build a report containing one or several sub-reports (tables etc).
 */
public class MultiFiller implements JasperFiller {
    private static final Logger LOGGER = LoggerFactory.getLogger(MultiFiller.class);

    private static final String DATA_KEY = "DATA";
    private static final String TEMPLATE_KEY = "TEMPLATE";
    private final JasperReport compiledTemplate;
    private final Collection<Map<String, ?>> data;
    private final Map<String, Object> parameters;

    /**
     * Builds a report {@link Filler} which is able to incorporate sub-reports (for tables etc).
     *
     * @param compiledTemplate the {@link JasperReport} used as template for the main report
     * @param data             a {@link List} of {@link Map Maps} representing a series of objects to be added to the report one after the other
     * @param parameters       some report-wide parameters, stored as a {@link Map} with their name as a String key
     */
    public MultiFiller(final JasperReport compiledTemplate, final Collection<Map<String, ?>> data, final Map<String, Object> parameters) {
        this.compiledTemplate = compiledTemplate;
        this.data = data;
        this.parameters = parameters;
    }

    /**
     * Adds a sub-report to this report.
     *
     * @param key                 the {@link String} key in this 'father' template's parameters which leads to all the sub-report data
     * @param template            the {@link JasperReport} template to use for the sub-report
     * @param subReportData       the data to use in the sub-report
     * @param subReportParameters the sub-report's parameters (parameters are subreport-wide constants)
     */
    public void addSubReport(final String key, final JasperReport template, final Collection<Map<String, ?>> subReportData,
                             final Map<String, Object> subReportParameters) {
        Map<String, Object> reportMap = new HashMap<>();
        reportMap.put(TEMPLATE_KEY, template);
        reportMap.put(DATA_KEY, subReportData);
        reportMap.putAll(subReportParameters);
        parameters.put(key, reportMap);
    }

    /**
     * Adds a sub-report (which is also a composite sub-report) to this report.
     *
     * @param key       the {@link String} key in this 'father' template's parameters which leads to all the sub-report data
     * @param subReport the {@link MultiFiller} to use for the sub-report
     */
    public void addSubReport(final String key, final MultiFiller subReport) {
        Map<String, Object> reportMap = new HashMap<>();
        reportMap.put(TEMPLATE_KEY, subReport.compiledTemplate);
        reportMap.put(DATA_KEY, subReport.data);
        reportMap.putAll(subReport.parameters);
        parameters.put(key, reportMap);
    }

    @Override
    public final JasperPrint generate() throws JRException {
        LOGGER.info("JASPER filling (multi-report)");
        long start = System.currentTimeMillis();
        JasperPrint print = JasperFillManager.fillReport(compiledTemplate, parameters, new JRMapCollectionDataSource(data));
        LOGGER.info("JASPER report Filling time : {} ms, {} pages expected", System.currentTimeMillis() - start, print.getPages().size());
        return print;
    }
}
