ribi::QtToolTestApproximatorXyzMainDialog::QtToolTestApproximatorXyzMainDialog(QWidget *parent) noexcept :
  QtHideAndShowDialog(parent),
  ui(new Ui::QtToolTestApproximatorXyzMainDialog),
  m_approximator(),
  m_data(CreateData())
{
  #ifndef NDEBUG
  Test();
  #endif
  ui->setupUi(this);

  //Set up the plots and curves
  GetPlot(0)->setTitle("Approximator, for z = 0.0");
  GetPlot(1)->setTitle("Approximator, for z = 0.5");
  GetPlot(2)->setTitle("Approximator, for z = 1.0");
  for (auto i=0; i!=m_n_curves; ++i)
  {
    const auto plot = GetPlot(i);
    plot->setAxisTitle(QwtPlot::xBottom,"X");
    plot->setAxisTitle(QwtPlot::yLeft,"Y");
    #ifdef _WIN32
    plot->setCanvasBackground(QBrush(QColor(255,255,255)));
    #else
    plot->setCanvasBackground(QColor(255,255,255));
    #endif

    const auto curve_values = GetCurveValues(i);
    assert(curve_values);
    curve_values->setTitle("Points");
    curve_values->attach(plot.get());
    curve_values->setStyle(QwtPlotCurve::Dots);
    curve_values->setPen(QPen(QColor(255,0,0),5));

    const auto curve_approximation = GetCurveApproximation(i);
    assert(curve_approximation);
    curve_approximation->setTitle("Approximation");
    curve_approximation->attach(plot.get());
    curve_approximation->setStyle(QwtPlotCurve::Dots);
    curve_approximation->setPen(QPen(QColor(0,0,255),3));

    //Add grid
    {
      QwtPlotGrid * const grid = new QwtPlotGrid;
      grid->setPen(QPen(QColor(128,128,128)));
      grid->attach(plot.get());
    }
    //Add zoomer
    {
      new QwtPlotZoomer(plot->canvas());
    }
    //Add legend
    {
      QwtLegend * const legend = new QwtLegend;
      legend->setFrameStyle(QFrame::Box|QFrame::Sunken);
      plot->insertLegend(legend, QwtPlot::RightLegend);
    }

    plot->setAxisScale(
      QwtPlot::xBottom,
      static_cast<double>(ui->box_int_x->minimum()),
      static_cast<double>(ui->box_int_x->maximum())
    );
    plot->setAxisScale(
      QwtPlot::yLeft,
      static_cast<double>(ui->box_double_y->minimum()),
      static_cast<double>(ui->box_double_y->maximum())
    );

    //Add to dialog
    assert(ui->verticalLayout->layout());
    ui->verticalLayout->layout()->addWidget(plot.get());
  }




  //Add some nice testing values
  ui->box_int_x->setValue(ui->box_int_x->minimum() / 2);
  ui->box_double_y->setValue(ui->box_double_y->maximum() / 2.0);
  on_button_clicked();

  ui->box_int_x->setValue(ui->box_int_x->minimum() / 4);
  ui->box_double_y->setValue(ui->box_double_y->minimum() / 2.0);
  on_button_clicked();

  ui->box_int_x->setValue(ui->box_int_x->maximum() / 4);
  ui->box_double_y->setValue(ui->box_double_y->maximum() / 2.0);
  on_button_clicked();

  ui->box_int_x->setValue(ui->box_int_x->maximum() / 2);
  ui->box_double_y->setValue(ui->box_double_y->minimum() / 2.0);
  on_button_clicked();

  ui->box_int_x->setValue(0);
  ui->box_double_y->setValue(0.0);
}
TArray<FString> UCurveTable::CreateTableFromCSVString(const FString& InString, ERichCurveInterpMode InterpMode)
{
	// Array used to store problems about table creation
	TArray<FString> OutProblems;

	const FCsvParser Parser(InString);
	const FCsvParser::FRows& Rows = Parser.GetRows();

	// Must have at least 2 rows (x values + y values for at least one row)
	if(Rows.Num() <= 1)
	{
		OutProblems.Add(FString(TEXT("Too few rows.")));
		return OutProblems;
	}

	// Empty existing data
	EmptyTable();

	TArray<float> XValues;
	GetCurveValues(Rows[0], &XValues);

	// Iterate over rows
	for(int32 RowIdx = 1; RowIdx < Rows.Num(); RowIdx++)
	{
		const TArray<const TCHAR*>& Row = Rows[RowIdx];

		// Need at least 1 cells (row name)
		if(Row.Num() < 1)
		{
			OutProblems.Add(FString::Printf(TEXT("Row '%d' has too few cells."), RowIdx));
			continue;
		}

		// Get row name
		FName RowName = MakeValidName(Row[0]);

		// Check its not 'none'
		if(RowName == NAME_None)
		{
			OutProblems.Add(FString::Printf(TEXT("Row '%d' missing a name."), RowIdx));
			continue;
		}

		// Check its not a duplicate
		if(RowMap.Find(RowName) != NULL)
		{
			OutProblems.Add(FString::Printf(TEXT("Duplicate row name '%s'."), *RowName.ToString()));
			continue;
		}

		TArray<float> YValues;
		GetCurveValues(Row, &YValues);

		if(XValues.Num() != YValues.Num())
		{
			OutProblems.Add(FString::Printf(TEXT("Row '%s' does not have the right number of columns."), *RowName.ToString()));
			continue;
		}

		FRichCurve* NewCurve = new FRichCurve();
		// Now iterate over cells (skipping first cell, that was row name)
		for(int32 ColumnIdx = 0; ColumnIdx < XValues.Num(); ColumnIdx++)
		{
			FKeyHandle KeyHandle = NewCurve->AddKey(XValues[ColumnIdx], YValues[ColumnIdx]);
			NewCurve->SetKeyInterpMode(KeyHandle, InterpMode);
		}

		RowMap.Add(RowName, NewCurve);
	}

	Modify(true);
	return OutProblems;
}