void FlowViewWindow::updatedFrames(int numFrames)
{
    CANFrame thisFrame;
    if (numFrames == -1) //all frames deleted. Kill the display
    {
        ui->listFrameID->clear();
        foundID.clear();
        currentPosition = 0;
        refreshIDList();
        updateFrameLabel();
        removeAllGraphs();
        memset(refBytes, 0, 8);
        memset(currBytes, 0, 8);
        updateDataView();
    }
    else if (numFrames == -2) //all new set of frames. Reset
    {
        ui->listFrameID->clear();
        foundID.clear();
        currentPosition = 0;
        refreshIDList();
        if (ui->listFrameID->count() > 0)
        {
            changeID(ui->listFrameID->item(0)->text());
            ui->listFrameID->setCurrentRow(0);
        }
        updateFrameLabel();
    }
    else //just got some new frames. See if they are relevant.
    {
        int refID = frameCache[0].ID;
        bool needRefresh = false;
        for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
        {
            thisFrame = modelFrames->at(i);
            if (thisFrame.ID == refID)
            {
                frameCache.append(thisFrame);
                if (ui->cbLiveMode->checkState() == Qt::Checked)
                {
                    currentPosition = frameCache.count() - 1;
                    needRefresh = true;
                }
            }
        }
        if (needRefresh)
        {
            updateDataView();
            if (ui->cbSync->checkState() == Qt::Checked) emit sendCenterTimeID(frameCache[currentPosition].ID, frameCache[currentPosition].timestamp / 1000000.0);
        }
    }
}
void FlowViewWindow::btnFwdOneClick()
{
    playbackTimer->stop();
    playbackActive = false;
    updatePosition(true);
    updateDataView();
}
void FlowViewWindow::btnBackOneClick()
{
    playbackTimer->stop(); //pushing this button halts automatic playback
    playbackActive = false;

    updatePosition(false);
    updateDataView();
}
void FlowViewWindow::btnStopClick()
{
    playbackTimer->stop(); //pushing this button halts automatic playback
    playbackActive = false;
    currentPosition = 0;

    memcpy(currBytes, frameCache.at(currentPosition).data, 8);
    memcpy(refBytes, currBytes, 8);

    updateFrameLabel();
    updateDataView();
}
void PlotWindow::signalSmoothingChanged()
{
	// Filter the data
	filterCurveData();

	// Update the plots
	setCurveData();
	_plot->replot();

	// Update other windows viewing this data
	emit updateDataView();
}
void FlowViewWindow::gotCenterTimeID(int32_t ID, double timestamp)
{
    uint64_t t_stamp;

    t_stamp = timestamp * 1000000l;

    qDebug() << "timestamp: " << t_stamp;

    changeID(QString::number(ID)); //to be sure we're focused on the proper ID

    for (int j = 0; j < ui->listFrameID->count(); j++)
    {
        int thisNum = Utility::ParseStringToNum(ui->listFrameID->item(j)->text());
        if (thisNum == ID)
        {
            ui->listFrameID->setCurrentRow(j);
            break;
        }
    }

    int bestIdx = -1;
    for (int i = 0; i < frameCache.count(); i++)
    {
        if (frameCache[i].timestamp > t_stamp)
        {
            bestIdx = i - 1;
            break;
        }
    }
    qDebug() << "Best index " << bestIdx;
    if (bestIdx > -1)
    {
        currentPosition = bestIdx;
        if (ui->cbAutoRef->isChecked())
        {
            memcpy(refBytes, currBytes, 8);
        }

        memcpy(currBytes, frameCache.at(currentPosition).data, 8);

        updateDataView();
    }
}
void FlowViewWindow::timerTriggered()
{
    if (!playbackActive || (ui->cbLiveMode->checkState() == Qt::Checked))
    {
        playbackTimer->stop();
        return;
    }
    if (playbackForward)
    {
        updatePosition(true);
    }
    else
    {
        updatePosition(false);
    }

    updateDataView();

    if (!ui->cbLoopPlayback->isChecked())
    {
        if (currentPosition == 0) playbackActive = false;
        if (currentPosition == (frameCache.count() - 1)) playbackActive = false;
    }
}
void FlowViewWindow::changeID(QString newID)
{
    //parse the ID and then load up the frame cache with just messages with that ID.
    int id = Utility::ParseStringToNum(newID);
    frameCache.clear();

    if (modelFrames->count() == 0) return;

    playbackTimer->stop();
    playbackActive = false;
    for (int x = 0; x < modelFrames->count(); x++)
    {
        CANFrame thisFrame = modelFrames->at(x);
        if (thisFrame.ID == id)
        {
            for (int j = thisFrame.len; j < 8; j++) thisFrame.data[j] = 0;
            frameCache.append(thisFrame);
        }
    }
    currentPosition = 0;

    if (frameCache.count() == 0) return;

    removeAllGraphs();
    for (int c = 0; c < frameCache.at(0).len; c++)
    {
        createGraph(c);
    }

    updateGraphLocation();

    memcpy(currBytes, frameCache.at(currentPosition).data, 8);
    memcpy(refBytes, currBytes, 8);

    updateDataView();
}
void FlowViewWindow::btnPauseClick()
{
    playbackActive = false;
    playbackTimer->stop();
    updateDataView();
}
PlotWindow::PlotWindow(
	boost::shared_ptr<GoogleMapWindow> google_map, 
	boost::shared_ptr<DataStatisticsWindow> stats_view)
{
	// Create the plot
	_plot = new QwtPlot();

	// Create HR zone markers
	const QColor hr_zone_colours[5] = 
		{HR_ZONE1_COLOUR, HR_ZONE2_COLOUR, HR_ZONE3_COLOUR, HR_ZONE4_COLOUR, HR_ZONE5_COLOUR};
	_hr_zone_markers.resize(5);
	for (unsigned int i=0; i < _hr_zone_markers.size(); ++i)
	{
		_hr_zone_markers[i] = new HRZoneItem;

		_hr_zone_markers[i]->attach(_plot);
		_hr_zone_markers[i]->hide();
		
		// Set the fill colour
		QColor c(hr_zone_colours[i]);
		c.setAlpha(40);
		_hr_zone_markers[i]->setColour(c);
	}

	// Create lap markers
	_lap_markers.resize(0);
	
	// Connect this window to the google map
	connect(this, SIGNAL(setMarkerPosition(int)), google_map.get(), SLOT(setMarkerPosition(int)));
	connect(this, SIGNAL(beginSelection(int)), google_map.get(), SLOT(beginSelection(int)));
	connect(this, SIGNAL(endSelection(int)), google_map.get(), SLOT(endSelection(int)));
	connect(this, SIGNAL(zoomSelection(int,int)), google_map.get(), SLOT(zoomSelection(int,int)));
	connect(this, SIGNAL(deleteSelection()), google_map.get(), SLOT(deleteSelection()));
	connect(this, SIGNAL(panSelection(int)), google_map.get(), SLOT(moveSelection(int)));
	connect(this, SIGNAL(panAndHoldSelection(int)), google_map.get(), SLOT(moveAndHoldSelection(int)));
	connect(this, SIGNAL(updateDataView()), google_map.get(), SLOT(definePathColour()));

	// Connect this window to the statistical viewer
	connect(this, SIGNAL(zoomSelection(int,int)), stats_view.get(), SLOT(displaySelectedRideStats(int,int)));
	connect(this, SIGNAL(panAndHoldSelection(int)), stats_view.get(), SLOT(moveSelection(int)));
	connect(this, SIGNAL(deleteSelection()), stats_view.get(), SLOT(deleteSelection()));
	connect(this, SIGNAL(updateDataView()), stats_view.get(), SLOT(displayCompleteRideStats()));
	
	// Setup the axis
	_plot->enableAxis(QwtPlot::yRight,true);
	_plot->setAxisAutoScale(QwtPlot::xBottom,true);

	QwtText axis_text;
	QFont font = _plot->axisFont(QwtPlot::xBottom);
	font.setPointSize(8);
	axis_text.setFont(font);

	axis_text.setText("HR (bpm) Speed (km/h) Cadence (rpm)\nPower (W) Temp (C)");
	_plot->setAxisTitle(QwtPlot::yLeft,axis_text);

	axis_text.setText("Elevation (m)");
	_plot->setAxisTitle(QwtPlot::yRight,axis_text);

	axis_text.setText("Distance (km)");
	_plot->setAxisTitle(QwtPlot::xBottom,axis_text);

	// Define the curves to plot
	QColor c;

	_curve_hr = new QwtPlotCurve("Heart Rate");
	c = HR_COLOUR;
	_curve_hr->setPen(c);
	_curve_hr->setYAxis(QwtPlot::yLeft);

	_curve_cadence = new QwtPlotCurve("Cadence");
	c = CADENCE_COLOUR;
	_curve_cadence->setPen(c);
	_curve_cadence->setYAxis(QwtPlot::yLeft);

	_curve_speed = new QwtPlotCurve("Speed");
	c = SPEED_COLOUR;
	_curve_speed->setPen(c);
	_curve_speed->setYAxis(QwtPlot::yLeft);

	_curve_power = new QwtPlotCurve("Power");
	c = POWER_COLOUR;
	_curve_power->setPen(c);
	_curve_power->setYAxis(QwtPlot::yLeft);

	_curve_temp = new QwtPlotCurve("Temp");
	c = TEMP_COLOUR;
	_curve_temp->setPen(c);
	_curve_temp->setYAxis(QwtPlot::yLeft);

	_curve_alt = new QwtPlotCurve("Elevation");
	_curve_alt->setRenderHint(QwtPlotItem::RenderAntialiased);
	c = ALT_COLOUR;
	_curve_alt->setPen(c);
    _curve_alt->setBrush(c);
	_curve_alt->setYAxis(QwtPlot::yRight);
	_curve_alt->setBaseline(-300.0); // ensure display is correct even when -ve altitude

	_curve_alt->attach(_plot);
	_curve_cadence->attach(_plot);
	_curve_speed->attach(_plot);
	_curve_hr->attach(_plot);
	_curve_power->attach(_plot);
	_curve_temp->attach(_plot);

	// Checkboxes for graph plots
	_hr_cb.reset(new QCheckBox("Heart Rate"));
	_speed_cb.reset(new QCheckBox("Speed"));
	_alt_cb.reset(new QCheckBox("Elevation"));
	_cadence_cb.reset(new QCheckBox("Cadence"));
	_power_cb.reset(new QCheckBox("Power"));
	_temp_cb.reset(new QCheckBox("Temp"));
	_laps_cb = new QCheckBox("Laps");
	_hr_zones_cb = new QCheckBox("HR Zones");
	_hr_cb->setChecked(true);
	_speed_cb->setChecked(true);
	_alt_cb->setChecked(true);
	_cadence_cb->setChecked(true);
	_power_cb->setChecked(false);
	_temp_cb->setChecked(false);
	_laps_cb->setChecked(true);
	_hr_zones_cb->setChecked(false);

	QPalette plt;
	plt.setColor(QPalette::WindowText, HR_COLOUR);
	_hr_cb->setPalette(plt);
	plt.setColor(QPalette::WindowText, SPEED_COLOUR);
	_speed_cb->setPalette(plt);
	plt.setColor(QPalette::WindowText, ALT_COLOUR);
	_alt_cb->setPalette(plt);
	plt.setColor(QPalette::WindowText, CADENCE_COLOUR);
	_cadence_cb->setPalette(plt);
	plt.setColor(QPalette::WindowText, POWER_COLOUR);
	_power_cb->setPalette(plt);
	plt.setColor(QPalette::WindowText, TEMP_COLOUR);
	_temp_cb->setPalette(plt);

	connect(_hr_cb.get(), SIGNAL(stateChanged(int)),this,SLOT(curveSelectionChanged()));
	connect(_speed_cb.get(), SIGNAL(stateChanged(int)),this,SLOT(curveSelectionChanged()));
	connect(_alt_cb.get(), SIGNAL(stateChanged(int)),this,SLOT(curveSelectionChanged()));
	connect(_cadence_cb.get(), SIGNAL(stateChanged(int)),this,SLOT(curveSelectionChanged()));
	connect(_power_cb.get(), SIGNAL(stateChanged(int)),this,SLOT(curveSelectionChanged()));
	connect(_temp_cb.get(), SIGNAL(stateChanged(int)),this,SLOT(curveSelectionChanged()));
	connect(_laps_cb, SIGNAL(stateChanged(int)),this,SLOT(lapSelectionChanged()));
	connect(_hr_zones_cb, SIGNAL(stateChanged(int)),this,SLOT(hrZoneSelectionChanged()));

	// Plot picker for numerical display
	_plot_picker1 = 
		new QwtCustomPlotPicker(
		QwtPlot::xBottom, QwtPlot::yLeft, 
		_data_log, 
		_plot->canvas(), 
		_hr_cb, _speed_cb, _alt_cb, _cadence_cb, _power_cb, _temp_cb);
		
	_plot_picker1->setRubberBandPen(QColor(Qt::white));
    _plot_picker1->setTrackerPen(QColor(Qt::black));
	_plot_picker1->setStateMachine(new QwtPickerTrackerMachine());
	connect(_plot_picker1, SIGNAL(moved(const QPointF&)), this, SLOT(setMarkerPosition(const QPointF&)));
	
	// Plot picker for drawing user selection
	_plot_picker2 = 
		new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft, QwtPlotPicker::NoRubberBand, QwtPicker::AlwaysOff, _plot->canvas());
	_plot_picker2->setStateMachine(new QwtPickerDragPointMachine());
	connect(_plot_picker2, SIGNAL(appended(const QPointF&)), this, SLOT(beginSelection(const QPointF&)));
	connect(_plot_picker2, SIGNAL(moved(const QPointF&)), this, SLOT(endSelection(const QPointF&)));

	// Plot zoomer
	_plot_zoomer = new QwtCustomPlotZoomer(QwtPlot::xBottom, QwtPlot::yLeft, _plot->canvas());
	_plot_zoomer->setRubberBand(QwtPicker::UserRubberBand);
    _plot_zoomer->setRubberBandPen(QColor(Qt::white));
    _plot_zoomer->setTrackerMode(QwtPicker::AlwaysOff);
    _plot_zoomer->setMousePattern(QwtEventPattern::MouseSelect2,Qt::RightButton, Qt::ControlModifier);
    _plot_zoomer->setMousePattern(QwtEventPattern::MouseSelect3,Qt::RightButton);
	connect(_plot_zoomer, SIGNAL(zoomed(const QRectF&)), this, SLOT(zoomSelection(const QRectF&)));

	// Plot panner
	_plot_panner = new QwtPlotPanner(_plot->canvas());
	_plot_panner->setMouseButton(Qt::MidButton);
	connect(_plot_panner, SIGNAL(moved(int, int)), this, SLOT(panSelection(int, int)));
	connect(_plot_panner, SIGNAL(panned(int, int)), this, SLOT(panAndHoldSelection(int, int)));

	// Selection for x-axis measurement
	_x_axis_measurement = new QComboBox;
	_x_axis_measurement->insertItem(0, "x-axis = time");
	_x_axis_measurement->insertItem(1, "x-axis = distance");
	_x_axis_measurement->setCurrentIndex(1);
	_plot->setAxisScaleDraw(QwtPlot::xBottom, new XAxisScaleDraw(tr("dist")));
	connect(_x_axis_measurement,SIGNAL(currentIndexChanged(int)), this, SLOT(xAxisUnitsChanged(int)));
	connect(_x_axis_measurement,SIGNAL(currentIndexChanged(int)), _plot_picker1, SLOT(xAxisUnitsChanged(int)));

	// Selection for signal smoothing
	_smoothing_selection = new QSpinBox;
	_smoothing_selection->setRange(1,50);
	_smoothing_selection->setPrefix("Smoothing: ");
	_smoothing_selection->setValue(5); // default value
	connect(_smoothing_selection, SIGNAL(valueChanged(int)),this,SLOT(signalSmoothingChanged()));

	// Layout the GUI
	QWidget* plot_options_widget = new QWidget;
	QVBoxLayout* vlayout1 = new QVBoxLayout(plot_options_widget);
	vlayout1->addWidget(_hr_cb.get());
	vlayout1->addWidget(_speed_cb.get());
	vlayout1->addWidget(_alt_cb.get());
	vlayout1->addWidget(_cadence_cb.get());
	vlayout1->addWidget(_power_cb.get());
	vlayout1->addWidget(_temp_cb.get());
	vlayout1->addWidget(_x_axis_measurement);
	vlayout1->addWidget(_smoothing_selection);
	vlayout1->addWidget(_laps_cb);
	vlayout1->addWidget(_hr_zones_cb);
	vlayout1->addStretch();

	QHBoxLayout* hlayout2 = new QHBoxLayout(this);
	hlayout2->addWidget(_plot);
	hlayout2->addWidget(plot_options_widget);
	
	resize(700,270);

	// Disable all controls until ride data is loaded
	setEnabled(false);
}