#include "StdAfx.h"
#include "Chart.h"

using namespace Sianaso;


Chart::Chart(array<Label^>^ _xs, array<Label^>^ _ys, PictureBox^  _graf)//pak dotat delku max delku pole, uziv volitelnou
{
	xs = _xs;
	ys = _ys;

	graf = _graf;
	scale_changed = false;
	chart_array2 = gcnew array<float,2>(2,10000);
	chart_array3 = gcnew array<float,2>(2,10000);
//	array_index = 0;
	chart_array2[0,array_index2] = 0;
	chart_array2[1,array_index2] = 0;
	chart_array3[0,array_index3] = 0;
	chart_array3[1,array_index3] = 0;
}

int Chart::array_index::get()
{
	return array_index2;
}

bool Chart::Chart_change_grid(System::Windows::Forms::PaintEventArgs^  e, float x_min, float y_min, float x_max, float y_max, bool labes_ref)
{
	Graphics^ _graph;
	if (e == nullptr)
		_graph = graf->CreateGraphics();
	else
		_graph = e->Graphics;
	
	Pen^ grid = gcnew Pen(Color::White);
	bool found = false;
	float expon = 0.001; 
	int	optimal_x_step, optimal_y_step;
	float x_scale, y_scale;
	float aux;
	array<int>^ steps = gcnew array<int>{1,2,3,5,8};
	array<Single>^GridPattern = {1.0F,4.0F};

	xmin = x_min; //nastaveni globalnich promennych rozmeru grafu, protoze tuhle fci volam pri inicializaci
	xmax = x_max; //pri dalsji nastavenich se to sice prenastavuje, ale porad na stejnou hodnotu, takze to tak muze zustat
	ymin = y_min;
	ymax = y_max;

	if (labes_ref)
	{
		for(int i=1;i<xs->Length;i++)
			xs[i]->Visible = false;
		for(int i=1;i<ys->Length;i++)
			ys[i]->Visible = false;
	}
	//grid->DashStyle = DashStyle::Dot;
	grid->DashCap = DashCap::Round;
	grid->DashPattern = GridPattern;
	x_scale = x_max - x_min;
	y_scale = y_max - y_min;
	while(!found) //najit nejlepsi rozdeleni os pro x
	{

		for (int i=0;i<steps->Length;i++)
		{
			aux = x_scale/(steps[i]*expon);
			if ((aux>=5)&&(aux<=10))
			{
				found = true;
				optimal_x_step = steps[i];
				i = steps->Length;
			}
		}
		expon *= 10;
		if (expon>1) //protoze tam vznikala chyba pri zaokrouhlovani byvalo treba 10.00001
			expon = System::Math::Round(expon);
		if (expon>1e+9) //kdyz to nebylo schopny najit vhodny rozdeleni ani pro tak velky cislo vrati chybu
			return 0;
	}
	expon /= 10;
	if (expon>1) //protoze tam vznikala chyba pri zaokrouhlovani byvalo treba 10.00001
		expon = System::Math::Round(expon);

	xs[0]->Text = x_min.ToString("0.000");
	aux = x_min;
	for(int i=1;i<=(x_scale/optimal_x_step/expon+0.01);i++) //to 0.01 tam je kvuly zaokrouhlovani, jako takova tolerance
	{			
		_graph->DrawLine(grid, (graf->Size.Width/(x_scale/optimal_x_step/expon))*i, 0.0, (graf->Size.Width/(x_scale/optimal_x_step/expon))*i, (float)graf->Size.Height); //x1 y1 x2 y2
		if (labes_ref)
		{
			xs[i]->Visible = true;
			xs[i]->Location = Point(58-7+(int)(graf->Size.Width/(x_scale/optimal_x_step/expon))*i,384); //pak treba dodelat this->x0->Location, nemit tam natvrdo konstantu (ty konst jsou od toho odvozeny)
			aux += optimal_x_step*expon;
			xs[i]->Text = aux.ToString("0.000");
		}
	}

	expon = 0.001;
	found = false;
	while(!found) //najit nejlepsi rozdeleni os pro y
	{

		for (int i=0;i<steps->Length;i++)
		{
			aux = y_scale/(steps[i]*expon);
			if ((aux>=4)&&(aux<=8))
			{
				found = true;
				optimal_y_step = steps[i];
			}
		}
		expon *= 10;
		if (expon>1) //protoze tam vznikala chyba pri zaokrouhlovani byvalo treba 10.00001
			expon = System::Math::Round(expon);
		if (expon>1e+9) //kdyz to nebylo schopny najit vhodny rozdeleni ani pro tak velky cislo vrati chybu
			return 0;
	}
	expon /= 10;
	if (expon>1) //protoze tam vznikala chyba pri zaokrouhlovani byvalo treba 10.00001
		expon = System::Math::Round(expon);

	ys[0]->Text = y_min.ToString("0.000");
	aux = y_min;
	for(int i=1;i<=(y_scale/optimal_y_step/expon+0.01);i++)
	{			
		_graph->DrawLine(grid, 0.0, graf->Size.Height-(graf->Size.Height/(y_scale/optimal_y_step/expon))*i, (float)graf->Size.Width, graf->Size.Height-(graf->Size.Height/(y_scale/optimal_y_step/expon))*i); //x1 y1 x2 y2
		if(labes_ref)
		{
			ys[i]->Visible = true;
			ys[i]->Location = Point(21,372-(int)(graf->Size.Height/(y_scale/optimal_y_step/expon))*i); //pak treba dodelat this->y0->Location, nemit tam natvrdo konstantu
			aux += optimal_y_step*expon;
			ys[i]->Text = aux.ToString("0.000");
		}
	}
	return 1;
}

bool Chart::Chart_change_grid(float x_min, float y_min, float x_max, float y_max)
{
	System::Windows::Forms::PaintEventArgs^  _e = nullptr;
	return Chart_change_grid(_e, x_min, y_min, x_max, y_max, true);
}

void Chart::Chart_Refresh(System::Windows::Forms::PaintEventArgs^  e)
{
	Chart_Refresh(e, chart_array2, array_index2, 0);
	Chart_Refresh(e, chart_array3, array_index3, 1);
}

void Chart::Chart_Refresh(System::Windows::Forms::PaintEventArgs^  e, array<float,2>^ chart_array, int %array_index, int numOfChart)
{			
	Graphics^ _graph;// = e->Graphics;
	Pen^ line;
	float Tvz;
	float step_x, step_y;
	float t0, t1, y0, y1;
	int i = 2;

	if (e == nullptr)
		_graph = graf->CreateGraphics();
	else
		_graph = e->Graphics;

	switch (numOfChart)
	{
		case 0: line = gcnew Pen(Color::Yellow); break;
		case 1: line = gcnew Pen(Color::Red); break;
		default: break;
	}

	//_graph = graf->CreateGraphics();
	if (array_index > 0)
	{
		Tvz = chart_array[0,array_index] - chart_array[0,array_index-1];
		step_x = graf->Size.Width/(xmax-xmin); //x_scale = xmax-xmin
		step_y = graf->Size.Height/(ymax-ymin);

		while((chart_array[0,i]) && (t0 <= xmax+Tvz))// dokude je nenulovej cas a je na plose grafu
		{
			t0 = chart_array[0,i-1];
			t1 = chart_array[0,i];
			y0 = chart_array[1,i-1]; 
			y1 = chart_array[1,i];

			if ((t0 >= xmin)) // && (y0 >= ymin))
			{
				t0 -= xmin;
				t1 -= xmin;
				y0 -= ymin;
				y1 -= ymin;
				if (numOfChart == 1) //vykresly jako diskretni vzorky, projdite tvarovacem
				{
					if ((t0<Tvz) && (t0>0.001))
					{	
						float _y;
						_y = chart_array[1,i-2] - ymin;
						_graph->DrawLine(line,(float)(0*step_x), graf->Size.Height-_y*step_y-1, t0*step_x, graf->Size.Height-_y*step_y-1);
						_graph->DrawLine(line,t0*step_x, graf->Size.Height-_y*step_y-1, t0*step_x, graf->Size.Height-y0*step_y-1); //x1 y1 x2 y
					}
					_graph->DrawLine(line,t0*step_x, graf->Size.Height-y0*step_y-1, t1*step_x, graf->Size.Height-y0*step_y-1); //x1 y1 x2 y2 //graf->Size.Height tam je proto, ze to musim jakoby invertovat, protoze u osy y je 0 nahore
					_graph->DrawLine(line,t1*step_x, graf->Size.Height-y0*step_y-1, t1*step_x, graf->Size.Height-y1*step_y-1); //x1 y1 x2 y
				}
				else // vykresly "pojite"
				{
					float tx1, tx2, t_1, yx, y_1;
					if (i>1) //urceni prvniho bodu v grafu, protoze predchozi y muze byt nalevo od osy casu => z podobnosti trojuhelnika se urci velikost y na nulove ose x(casu)
					{
						y_1 = chart_array[1,i-2];
						t_1 = chart_array[0,i-2];
						tx1 = xmin - t_1;
						tx2 = (t0 + xmin) - t_1; //(t0 + xmin) protoze ho predtim odcitam
						yx = (tx1/tx2)*(y0 + ymin -y_1) + y_1 - ymin; //y0 + ymin to samy co vyse
					}

					if ((t0<Tvz) && (t0>0.001))
					{
						_graph->DrawLine(line,(float)(0*step_x), graf->Size.Height-yx*step_y-1, t0*step_x, graf->Size.Height-y0*step_y-1);
					}
					_graph->DrawLine(line,t0*step_x, graf->Size.Height-y0*step_y-1, t1*step_x, graf->Size.Height-y1*step_y-1); //x1 y1 x2 y2 //graf->Size.Height tam je proto, ze to musim jakoby invertovat, protoze u osy y je 0 nahore
				}
			}
			i++;
		}
	}
}
void Chart::Chart_Refresh(void)
{
	System::Windows::Forms::PaintEventArgs^  _e = nullptr;
	Chart_Refresh(_e, chart_array2, array_index2, 0);
	Chart_Refresh(_e, chart_array3, array_index3, 1);
}

void Chart::Chart_Refresh_grid(System::Windows::Forms::PaintEventArgs^  e)
{
	if (scale_changed)
	{
		Chart_change_grid(e, xmin, ymin, xmax, ymax, true);
		scale_changed = false;
	}
	else
		Chart_change_grid(e, xmin, ymin, xmax, ymax, false);
}

void Chart::Chart_Refresh_grid(void)
{
	System::Windows::Forms::PaintEventArgs^  _e = nullptr;
	
	if (scale_changed)
	{
		Chart_change_grid(_e, xmin, ymin, xmax, ymax, true);
		scale_changed = false;
	}
	else
		Chart_change_grid(_e, xmin, ymin, xmax, ymax, false);
}

bool Chart::Chart_Change_Scale(Rectangle Rec)
{
	float actual_y_scale, actual_x_scale;
	float aux;
	float _xmin, _xmax, _ymin, _ymax;

	actual_x_scale = xmax - xmin; //urci aktualni meritko os z jejich maxim a minim
	actual_y_scale = ymax - ymin;

	if ((Rec.Width > 0) || (Rec.Height > 0))
	{
		aux = (float)graf->Width;
		_xmax = (actual_x_scale*(Rec.X + Rec.Width))/aux + xmin;  //zmeni se maxima a minima
		_xmin = (actual_x_scale*Rec.X)/aux + xmin;
		aux = (float)graf->Height;
		_ymin = (-actual_y_scale*(Rec.Y + Rec.Height))/aux + ymax;
		_ymax = (-actual_y_scale*Rec.Y)/aux + ymax;
	
		actual_x_scale = _xmax - _xmin; //urci se nove aktualni meritko os z jejich maxim a minim
		actual_y_scale = _ymax - _ymin;
		
		if ((actual_x_scale<0.005) || (actual_y_scale<0.004)) //jestli bude vse OK, tak se zapise do promennych, jinak Upozorneni
		{
			System::Windows::Forms::MessageBox::Show( L"Nelze více přiblížit", L"Upozornění", MessageBoxButtons::OK, MessageBoxIcon::Information);
			return 0;
		}
		else
		{
			xmin = _xmin;
			xmax = _xmax;
			ymin = _ymin;
			ymax = _ymax;
			scale_changed = true;
			return 1;
		}
	}
	else
		return 0;
}
void Chart::Chart_Draw(float t, float y, int numOfChart)
{
	switch (numOfChart)
	{
		case 0: Draw(t, y, chart_array2, array_index2, 0); break;
		case 1: Draw(t, y, chart_array3, array_index3, 1); break;
		default: break;
	}
}

void Chart::Draw(float t, float y, array<float,2>^ chart_array, int %array_index, int numOfChart) //void Chart::Chart_Draw(System::Windows::Forms::PaintEventArgs^  e, double t, double y)
{
	Graphics^ _graph;
	Pen^ line;
	float Tvz;
	float step_x, step_y;
	float t0, t1, y0, y1; 

	switch (numOfChart)
	{
		case 0: line = gcnew Pen(Color::Yellow); break;
		case 1: line = gcnew Pen(Color::Red); break;
		default: break;
	}

	_graph = graf->CreateGraphics();
	
	array_index++;
	chart_array[0,array_index] = t;
	chart_array[1,array_index] = y;

	Tvz = chart_array[0,array_index] - chart_array[0,array_index-1];
	step_x = graf->Size.Width/(xmax-xmin); //x_scale = xmax-xmin
	step_y = graf->Size.Height/(ymax-ymin);

	t0 = chart_array[0,array_index-1];
	y0 = chart_array[1,array_index-1]; 
	t1 = chart_array[0,array_index];
	y1 = chart_array[1,array_index];

	if ((t0 >= xmin) && (t0 <= xmax)) // && (y0 >= ymin))
	{
		t0 -= xmin;
		t1 -= xmin;
		y0 -= ymin;
		y1 -= ymin;
		if (numOfChart == 1) //vykresly jako diskretni vzorky, projdite tvarovacem
		{
			if ((t0<Tvz) && (t0>0.001))
			{
				float _y;
				_y = chart_array[1,array_index-2] - ymin;
				_graph->DrawLine(line,(float)(0*step_x), graf->Size.Height-_y*step_y-1, t0*step_x, graf->Size.Height-_y*step_y-1);
				_graph->DrawLine(line,t0*step_x, graf->Size.Height-_y*step_y-1, t0*step_x, graf->Size.Height-y0*step_y-1); //x1 y1 x2 y
			}
			_graph->DrawLine(line,t0*step_x, graf->Size.Height-y0*step_y-1, t1*step_x, graf->Size.Height-y0*step_y-1); //x1 y1 x2 y2 //graf->Size.Height tam je proto, ze to musim jakoby invertovat, protoze u osy y je 0 nahore
			_graph->DrawLine(line,t1*step_x, graf->Size.Height-y0*step_y-1, t1*step_x, graf->Size.Height-y1*step_y-1); //x1 y1 x2 y
		}
		else // vykresly "pojite"
		{
			float tx1, tx2, t_1, yx, y_1;
			if (array_index>1) //urceni prvniho bodu v grafu, protoze predchozi y muze byt nalevo od osy casu => z podobnosti trojuhelnika se urci velikost y na nulove ose x(casu)
			{
				y_1 = chart_array[1,array_index-2];
				t_1 = chart_array[0,array_index-2];
				tx1 = xmin - t_1;
				tx2 = (t0 + xmin) - t_1; //(t0 + xmin) protoze ho predtim odcitam
				yx = (tx1/tx2)*(y0 + ymin -y_1) + y_1 - ymin; //y0 + ymin to samy co vyse
			}

			if ((t0<Tvz) && (t0>0.001))
				_graph->DrawLine(line,(float)(0*step_x), graf->Size.Height-yx*step_y-1, t0*step_x, graf->Size.Height-y0*step_y-1);
			_graph->DrawLine(line,t0*step_x, graf->Size.Height-y0*step_y-1, t1*step_x, graf->Size.Height-y1*step_y-1); //x1 y1 x2 y2 //graf->Size.Height tam je proto, ze to musim jakoby invertovat, protoze u osy y je 0 nahore, -1 tam je proto, aby, kdyz je to v 0 aby se mohl vykreslit na poslednim pixelu graf->Size.Height
		}
	}
}

void Chart::Chart_Clear(void)
{
	Array::Clear(chart_array2,0,chart_array2->Length);
	array_index2 = 0;
	chart_array2[0,array_index2] = 0;
	chart_array2[1,array_index2] = 0;

	Array::Clear(chart_array3,0,chart_array3->Length);
	array_index3 = 0;
	chart_array3[0,array_index3] = 0;
	chart_array3[1,array_index3] = 0;
}

void Chart::Chart_AutoScale(void)
{
	int i;
	int arr_length;
	float aux;

	/*arr_length = (chart_array->Length)/2-1;
	i = arr_length;
	while (chart_array[0,i] && ((arr_length-i) > graf->Size.Width))
	{
		i--;
	}*/
	if (array_index2 <= (chart_array2->Length/2))
	{
		xmin = chart_array2[0,0];
		xmax = chart_array2[0,array_index2];
		
		// pro hledani min hodnoty 
		aux = 0;
		for(i=0;i<=array_index2;i++)
		{
			if(chart_array2[1,i] < aux)
				aux = chart_array2[1,i];
		}
		for(i=0;i<=array_index3;i++)
		{
			if(chart_array3[1,i] < aux)
				aux = chart_array3[1,i];
		}
		ymin = aux;

		// pro hledani max hodnoty 
		aux = 0;
		for(i=0;i<=array_index2;i++)
		{
			if(chart_array2[1,i] > aux)
				aux = chart_array2[1,i];
		}
		for(i=0;i<=array_index3;i++)
		{
			if(chart_array3[1,i] > aux)
				aux = chart_array3[1,i];
		}
		ymax = aux + (aux-ymin)*0.05;  //aby to nebylo narvany uplne nahore
		if (ymax == 0 && ymin == 0)
			ymax = 1;
		else if (ymax == ymin)
			ymax = ymin*1.5;
	}
	else
	{
		//dodelat, kdyz je vetsi nez rozmer plochy grafu asi tak, aby to vykreslilo posledni 600 hodnot
	}
	scale_changed = true;
}

