#include "SimulationSPH.h"
#include <stdio.h>
#include <stdlib.h>
#include<conio.h>
#include <math.h>
#include <string.h>
#include<Windows.h>
#include <GL\glut.h>



SimulationSPH::SimulationSPH()
{
	kernel = 0.04f;
	masa = 0.02f;

	max_kulek = 15000;
	liczba_kulek = 0;
	//wielkość kulki (nie rysowania, tylko kulki)
	wielkosc_sw.x = 0.76f;
	wielkosc_sw.y = 0.76f;
	wielkosc_cell = kernel;


	wielkosc_siatki.x = 600;
	wielkosc_siatki.y = 600;
	total_cell = (uint)(wielkosc_siatki.x) * (uint)(wielkosc_siatki.y);

	//params
	grawitacja.x = NULL;//przyciąganie na osi X
	grawitacja.y = -2.5;//przyciąganie na osi Y
	stiff = 700.0f;//stała gazowa
	par_rest_dens = 700.0f;//gęstość w spoczynku
	par_time_stp = 0.002f; //krok czasowy
	par_wall_dumping = 0.1f;//"przykl do ściany"
	par_visco = 8.0f;//lepkość

	particles = (Kulka *)malloc(sizeof(Kulka)*max_kulek);
	cells = (Kom *)malloc(sizeof(Kom)*total_cell);


}

SimulationSPH::~SimulationSPH()
{
	free(particles);
	free(cells);
}

void SimulationSPH::init_ciecz()
{
	Vecf pos;
	Vecf vel(0.0f, 0.0f);

	//mysz

	pos.x = mysz_x;
	pos.y = mysz_y;
	dodaj_kulke(pos, vel);
	pos.x = mysz_x + 0.05;
	pos.y = mysz_y;
	dodaj_kulke(pos, vel);
	pos.x = mysz_x - 0.05;
	pos.y = mysz_y;
	dodaj_kulke(pos, vel);

	pos.x = mysz_x;
	pos.y = mysz_y - 0.05;
	dodaj_kulke(pos, vel);
	pos.x = mysz_x + 0.05;
	pos.y = mysz_y - 0.05;
	dodaj_kulke(pos, vel);
	pos.x = mysz_x - 0.05;
	pos.y = mysz_y - 0.05;
	dodaj_kulke(pos, vel);


	system("cls");
	printf("Ilość kulek: %u\n", liczba_kulek);

}

void SimulationSPH::dodaj_kulke(Vecf pos, Vecf vel)
{
	Kulka *p = &(particles[liczba_kulek]);
	p->pozycja = pos;
	p->predkosc = vel;
	p->przyspieszenie = Vecf(0.0f, 0.0f);
	p->pre = vel;
	p->gestosc = par_rest_dens;
	p->nast = NULL;
	liczba_kulek++;
}

Veci SimulationSPH::calculate_pos(Vecf pos)
{
	Veci p_resolution;
	p_resolution.x = (int)(pos.x / wielkosc_cell);
	p_resolution.y = (int)(pos.y / wielkosc_cell);
	return p_resolution;
}

uint SimulationSPH::licz_siatke(Veci pos)
{
	if (pos.x < 0 || pos.x >= wielkosc_siatki.x || pos.y < 0 || pos.y >= wielkosc_siatki.y)
	{
		return 0xffffffff;
	}//błąd

	uint siatka = (uint)(pos.y) * (uint)(wielkosc_siatki.x) + (uint)(pos.x);
	if (siatka >= total_cell)
	{
		printf("0xffffffff\n");
		_getch();
	}
	return siatka;
}

void SimulationSPH::step()
{
	Kulka *p;
	float max_przys = 0.0f;
	float obecn_przys;
	if (max_przys > 0.0f)
	{
		par_time_stp = kernel / max_przys*0.4f;
	}
	else
	{
		par_time_stp = 0.002f;
	}
	for (uint i = 0; i<liczba_kulek; i++)
	{
		p = &(particles[i]);
		obecn_przys = p->przyspieszenie.dlug();
		if (obecn_przys > max_przys) max_przys = obecn_przys;
	}

}

void SimulationSPH::buduj()
{
	for (uint i = 0; i<total_cell; i++) cells[i].pocz = NULL;
	Kulka *p;
	uint siatka;
	for (uint i = 0; i<liczba_kulek; i++)
	{
		p = &(particles[i]);
		siatka = licz_siatke(calculate_pos(p->pozycja));

		if (cells[siatka].pocz == NULL)
		{
			p->nast = NULL;
			cells[siatka].pocz = p;
		}
		else
		{
			p->nast = cells[siatka].pocz;
			cells[siatka].pocz = p;
		}
	}
}

void SimulationSPH::licz_cisnienie_gestosc()
{
	Kulka *p;
	Kulka *np;
	Veci cell_position;
	Veci near_position;
	uint siatka;
	for (uint k = 0; k<liczba_kulek; k++)
	{
		p = &(particles[k]);
		p->gestosc = 0.0f;
		p->cisnienie = 0.0f;
		cell_position = calculate_pos(p->pozycja);
		for (int i = -1; i <= 1; i++)
		{
			for (int j = -1; j <= 1; j++)
			{
				near_position.x = cell_position.x + i;
				near_position.y = cell_position.y + j;
				siatka = licz_siatke(near_position);
				if (siatka == 0xffffffff) {
					continue;
				}
				np = cells[siatka].pocz;
				while (np != NULL)
				{
					Vecf dlug_vec = np->pozycja - p->pozycja;
					float dlug = dlug_vec.dlugKwadr();

					if (dlug<INF || dlug >= kernel*kernel)
					{
						np = np->nast;
						continue;
					}

					p->gestosc = p->gestosc + masa * ker_poly6(dlug);
					np = np->nast;
				}
			}
		}
		p->gestosc = p->gestosc + masa*ker_poly6(0.0f);
		p->cisnienie = (pow(p->gestosc / par_rest_dens, 7) - 1) * stiff;
	}
}

void SimulationSPH::licz_sil()
{
	Kulka *p;
	Kulka *np;
	Veci cell_position;
	Veci near_position;
	uint siatka;
	for (uint k = 0; k<liczba_kulek; k++)
	{
		p = &(particles[k]);
		p->przyspieszenie = Vecf(0.0f, 0.0f);
		cell_position = calculate_pos(p->pozycja);
		for (int i = -1; i <= 1; i++)
		{
			for (int j = -1; j <= 1; j++)
			{
				near_position.x = cell_position.x + i;
				near_position.y = cell_position.y + j;
				siatka = licz_siatke(near_position);
				if (siatka == 0xffffffff) continue;
				np = cells[siatka].pocz;
				while (np != NULL)
				{
					Vecf dlug_vec = p->pozycja - np->pozycja;
					float dlug2 = dlug_vec.dlugKwadr();

					if (dlug2 < kernel*kernel && dlug2 > INF)
					{
						float dlug = sqrt(dlug2);
						float v = masa / p->gestosc;

						float temperatura = v * (p->cisnienie + np->cisnienie) * ker_spiky(dlug);
						p->przyspieszenie = p->przyspieszenie - dlug_vec*temperatura / dlug;

						Vecf pred;
						pred = np->pre - p->pre;
						temperatura = v * par_visco * ker_visco(dlug);
						p->przyspieszenie = p->przyspieszenie + pred*temperatura;
					}

					np = np->nast;
				}
			}
		}
		p->przyspieszenie = p->przyspieszenie / p->gestosc + grawitacja;
	}
}

void SimulationSPH::adwekcja()
{
	Kulka *p;
	for (uint i = 0; i<liczba_kulek; i++)
	{
		p = &(particles[i]);
		p->predkosc = p->predkosc + p->przyspieszenie*par_time_stp;
		p->pozycja = p->pozycja + p->predkosc*par_time_stp;

		if (p->pozycja.x < 0.0f)
		{
			p->predkosc.x = p->predkosc.x * par_wall_dumping;
			p->pozycja.x = 0.0f;
		}
		if (p->pozycja.x >= wielkosc_sw.x)
		{
			p->predkosc.x = p->predkosc.x * par_wall_dumping;
			p->pozycja.x = wielkosc_sw.x - 0.0001f;
		}


		if (p->pozycja.y < 0.0f)
		{
			p->predkosc.y = p->predkosc.y * par_wall_dumping;
			p->pozycja.y = 0.0f;
		}
		if (p->pozycja.y >= wielkosc_sw.y)
		{
			p->predkosc.y = p->predkosc.y * par_wall_dumping;
			p->pozycja.y = wielkosc_sw.y - 0.0001f;
		}


		p->pre = (p->pre + p->predkosc) / 2;
	}
}

void SimulationSPH::animacja()
{
	buduj();
	licz_cisnienie_gestosc();
	licz_sil();

	adwekcja();
}
//glunproject