package info.opencards.ui.ltmstats;

import info.opencards.Utils;
import info.opencards.core.CardFile;
import info.opencards.core.Item;
import info.opencards.learnstrats.ltm.LTMCollection;
import info.opencards.learnstrats.ltm.ScheduleUtils;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.TickUnits;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.StandardCategoryToolTipGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.StackedBarRenderer;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.GradientPaintTransformType;
import org.jfree.ui.StandardGradientPaintTransformer;

import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;


/**
 * DOCUMENT ME!
 *
 * @author Holger Brandl
 */
class SchedulePlanningPanel extends JPanel {


    private final DefaultCategoryDataset dataset;
    private final ValueAxis valueAxis;

    private static final int NUM_HIST_BINS = 8;
    private Collection<CardFile> currentFiles;


    public SchedulePlanningPanel() {
        setLayout(new BorderLayout());

        // setup the stacked bar chart
        dataset = new DefaultCategoryDataset();
        final JFreeChart chart = ChartFactory.createStackedBarChart(
                null,  // chart title
                Utils.getRB().getString("CardTableModel.stats.weekSchedule"),                  // domain axis label
//                "# cards",                     // range axis label
                null,                     // range axis label
                dataset,                     // data
                PlotOrientation.VERTICAL,    // the plot orientation
                false,                        // legend
                true,                        // tooltips
                false                        // urls
        );

        StackedBarRenderer renderer = new StackedBarRenderer();
        renderer.setGradientPaintTransformer(new StandardGradientPaintTransformer(GradientPaintTransformType.HORIZONTAL));

        //green
        renderer.setItemMargin(0.0);
        Paint greenPaint = new GradientPaint(0.0f, 0.0f, new Color(0x22, 0xFF, 0x22), 0.0f, 0.0f, new Color(0x88, 0xFF, 0x88));
        renderer.setSeriesPaint(1, greenPaint);
        renderer.setSeriesToolTipGenerator(1, new StandardCategoryToolTipGenerator());


        //blue
        Paint bluePaint = new GradientPaint(0.0f, 0.0f, new Color(0x22, 0x22, 0xFF), 0.0f, 0.0f, new Color(0x88, 0x88, 0xFF));
        renderer.setSeriesPaint(0, bluePaint);
        renderer.setSeriesToolTipGenerator(0, new StandardCategoryToolTipGenerator());


//        Paint p2 = new GradientPaint(0.0f, 0.0f, new Color(0xFF, 0x22, 0x22), 0.0f, 0.0f, new Color(0xFF, 0x88, 0x88));
//        renderer.setSeriesPaint(1, p2);
//        renderer.setSeriesToolTipGenerator(1, new StandardCategoryToolTipGenerator());
//
//        Paint p3 = new GradientPaint(0.0f, 0.0f, new Color(0x22, 0xFF, 0x22), 0.0f, 0.0f, new Color(0x88, 0xFF, 0x88));
//        renderer.setSeriesPaint(2, p3);
//        renderer.setSeriesToolTipGenerator(2, new StandardCategoryToolTipGenerator());

        CategoryPlot plot = (CategoryPlot) chart.getPlot();
        plot.setRenderer(renderer);
        valueAxis = plot.getRangeAxis();
        TickUnits units = (TickUnits) NumberAxis.createIntegerTickUnits();
        valueAxis.setStandardTickUnits(units);

        ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setPopupMenu(null);
        chartPanel.setDomainZoomable(false);
        chartPanel.setRangeZoomable(false);

        add(chartPanel, BorderLayout.CENTER);

        rebuildPanel(new HashSet<CardFile>());
    }


    void rebuildPanel(Collection<CardFile> currentFiles) {
        if (currentFiles != null)
            this.currentFiles = currentFiles;

        dataset.clear();

        if (this.currentFiles == null)
            return;

        computeScheduleHist(this.currentFiles);
//        set2EDistribution(currentFiles);
    }


    private boolean computeScheduleHist(Collection<CardFile> currentFiles) {

        Map<Integer, List<Item>> schedule = new HashMap<Integer, List<Item>>();

        for (int i = 0; i < NUM_HIST_BINS; i++) {

            for (CardFile currentFile : currentFiles) {
                LTMCollection ltmCollection = currentFile.getFlashCards().getLTMItems();

                if (!schedule.containsKey(i)) {
                    schedule.put(i, new ArrayList<Item>());
                }

                if (i == 0) {
                    schedule.get(i).addAll(ltmCollection.getScheduledItems());
                } else {
                    schedule.get(i).addAll(ltmCollection.predictItemsForDate(ScheduleUtils.getIncDate(ScheduleUtils.getToday(), i), false));
                }
            }
        }

        // now set up the histogram

        int[] scheduleHist = new int[NUM_HIST_BINS];
        int[] newHist = new int[NUM_HIST_BINS];

        int numRemainingNewItems = -Integer.MAX_VALUE; // just to make sure that is never used
        int maxNewPerDay = ScheduleUtils.getMaxNewCardsPerDay();

        for (Integer scheduleDate : schedule.keySet()) {
            int numScheduledCards;
            List<Item> curScheduleSet = schedule.get(scheduleDate);
            List<Item> newItems = ScheduleUtils.getNewItems(curScheduleSet);

            // incorporate the knowledge about new cards when computing the schedule-histogram
            int curDatNewRation = 0;
            if (scheduleDate == 0) {

                int maxNewCardsToday = ScheduleUtils.getMaxCardToBeLearntToday();
                curDatNewRation = Math.min(maxNewCardsToday, newItems.size());

                numScheduledCards = (curScheduleSet.size() - newItems.size());

                numRemainingNewItems = newItems.size() - Math.min(maxNewCardsToday, newItems.size());
            } else {
                numRemainingNewItems += newItems.size();

                numScheduledCards = curScheduleSet.size() - newItems.size();
                if (numRemainingNewItems > 0) {
                    int dailyNewRation = Math.min(maxNewPerDay, numRemainingNewItems);
                    curDatNewRation = dailyNewRation;
//                    numScheduledCards += dailyNewRation;
                    numRemainingNewItems -= dailyNewRation;
                }
            }

            // set the value to the histogram
            if (scheduleDate < NUM_HIST_BINS) {
                scheduleHist[scheduleDate] = numScheduledCards;
                newHist[scheduleDate] = curDatNewRation;
            }
//            scheduleHist[scheduleDate >= NUM_HIST_BINS - 1 ? NUM_HIST_BINS - 1 : scheduleDate] = numScheduledCards;
        }

        List<Integer> eBinSize = new ArrayList<Integer>();

        dataset.addValue((double) newHist[0], "# new flashcards", Utils.getRB().getString("CardTableModel.when.today"));
        dataset.addValue((double) scheduleHist[0], "# flashcards", Utils.getRB().getString("CardTableModel.when.today"));
        eBinSize.add(scheduleHist[0] + newHist[0]);

        dataset.addValue((double) newHist[1], "# new flashcards", Utils.getRB().getString("CardTableModel.when.tomorrow"));
        dataset.addValue((double) scheduleHist[1], "# flashcards", Utils.getRB().getString("CardTableModel.when.tomorrow"));
        eBinSize.add(scheduleHist[1] + newHist[1]);


        for (int i = 2; i < scheduleHist.length; i++) {
            String days = Utils.getRB().getString("CardTableModel.when.days");
//            dataset.addValue((double) scheduleHist[i], "Scheduled", (i == scheduleHist.length - 1 ? (NUM_HIST_BINS-1) + "+" + " " + days : i + " " + days));
            dataset.addValue((double) newHist[i], "# new flashcards", i + " " + days);
            dataset.addValue((double) scheduleHist[i], "# flashcards", i + " " + days);
            eBinSize.add(scheduleHist[i] + newHist[i]);
        }

        valueAxis.setLowerBound(0);
        valueAxis.setUpperBound(1.1 * Math.max(Collections.max(eBinSize), 1));
        valueAxis.setAutoRange(false);

        return false;
    }


    public void selectionChanged(List<CardFile> selectedFiles) {
        rebuildPanel(selectedFiles);
    }
}