package net.heartshield.ui;

import android.graphics.Color;

import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;

import java.util.ArrayList;
import java.util.List;

/**
 * Manages LineChart from MPAndroidChart.
 *
 * Logic goes here, bling goes into {@link LineChartUI}.
 *
 * @author David Madl (git@abanbytes.eu)
 * @date 2017-03-20
 */
public class LineChartWrapper extends LineChartUI {
    private List<Entry> mEntries = new ArrayList<>();

    /** window size displayed by default */
    public static final float WINDOW_SIZE_SECS = 3.0f;

    /**
     * pass in an existing LineChart from the UI:
     * <p>
     * <pre>
     *     new LineChartWrapper((LineChart) findViewById(R.id.chart))
     * </pre>
     */
    public LineChartWrapper(LineChart chart, float lineWidth, int lineColor) {
        super(chart, lineWidth, lineColor);

        // axis ranges: x is in secs, y is [AXIS_MIN : AXIS_MAX] == [0.0 : 1.0]
        mChart.setViewPortOffsets(0.0f, AXIS_MAX, WINDOW_SIZE_SECS, AXIS_MIN);
    }

    private static final float LINE_WIDTH = 5.0f;
    private static final int LINE_COLOR = Color.RED;

    public LineChartWrapper(LineChart chart) {
        this(chart, LINE_WIDTH, LINE_COLOR);
    }

    /**
     * Replace chart data and scroll to it.
     * Must be called from a drawing UI thread.
     */
    public void replaceData(double[] times, double[] series) {
        throwIfDataSetNull();
        if(times.length != series.length)
            throw new IllegalArgumentException("times.length != series.length");
        if(times.length == 0)
            throw new IllegalArgumentException("times.length == 0");

        LineData data = mChart.getData();

        data.getDataSetByIndex(0).clear();

        for(int i = 0; i < times.length; i++) {
            Entry e = new Entry((float) (times[i] - times[0]), (float) series[i]);
            data.addEntry(e, 0);
        }

        data.notifyDataChanged();
        mChart.notifyDataSetChanged();
        mChart.invalidate();
    }

    /**
     * Add a new data point to the chart, and scroll to it.
     * Must be called from a drawing UI thread.
     */
    public void addEntry(float time, float frame) {
        throwIfDataSetNull();
        LineData data = mChart.getData();

        Entry e = new Entry(time, frame);
        mEntries.add(e);
        data.addEntry(e, 0);

        // only keep the last few relevant entries around
        //if(mEntries.size() > 100) {
        if((time - mEntries.get(0).getX()) > WINDOW_SIZE_SECS) {
            data.removeEntry(mEntries.get(0), 0);
            mEntries.remove(0);
        }

        data.notifyDataChanged();


        // axis ranges: x is in secs, y is [AXIS_MIN : AXIS_MAX] == [0.0 : 1.0]
        float x1 = -time;
        float x2 = x1 + WINDOW_SIZE_SECS;

        // viewport is where we draw things (relative to screen coordinates), i.e.
        // if viewport left is negative, we see later points in time
        mChart.setViewPortOffsets(x1, AXIS_MAX, x2, AXIS_MIN);

        mChart.notifyDataSetChanged();
        mChart.invalidate();
        // << android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

        // this automatically refreshes the chart (calls invalidate())
        // mChart.moveViewTo(data.getXValCount()-7, 55f,
        // AxisDependency.LEFT);
    }

    private void throwIfDataSetNull() {
        LineData data = mChart.getData();
        if(data == null)
            throw new IllegalStateException("chart data has not been constructed yet");

        ILineDataSet set = data.getDataSetByIndex(0);
        if(set == null)
            throw new IllegalStateException("chart dataset has not been constructed yet");
    }
}
