// landschaften.h  Berechnung von Landschaft
#pragma once
#ifndef Vec3
#define Vec3 glm::vec3
#endif
//#define MEERWEITE 50.0f  //in main.cpp definiert
const float wurzel3halbe = sqrtf(3.0f)*0.5;

// Koordinatensystem von STL: z-Achse nach oben, x-Achse nach rechts, y-Achse nach hinten 
// Koordinatensystem von OpenGL: y-Achse nach oben, x-Achse nach rechts, z-Achse auf uns zu

struct VecN {
 float a,b;
};

struct VecS { //Vektor fuer Sigmoid-Parameter
 float a,b,k1,k2;
};

struct Wave {
 float alfa, period;
};

struct Teichparameter {
 uint32 meshid,xnmax,znmax;
 float wellenhoehe, wasserstand;
 Vec3 gmin, gmax; //Grenzen des Wassers, TODO:auswerten fuer Test ob Link im Wasser
 std::vector<Wave> waves;
};

class Huegel {
 int numGauss;
 int numSigmoid;
 float ymini,ymaxi;
 float y0=0.0f;
 
 //Parameter fuer Teiche:
 //Teichparameter teichpara[8]; //TODO: vorlaeufig beschraenkt auf 8 Gewaesser
 std::vector<Teichparameter> teichpara;
 int numTeiche=0;

 //Parameter fuer Gausshuegel:
 std::vector<Vec3> position; //x-z-Position der Huegelmitte, y=Huegelhoehe
 std::vector<VecN> param; //Parameter: Huegellaenge, Huegelbreite
 // Gauss-Funktion: y(r) = k1*exp(-k2*r^2)  //r ist Abstand von Mitte
 
 //Parameter fuer Sigmoid-Huegel:
 std::vector<Vec3> sposition; //x-z-Position der Huegelmitte, y=Huegelhoehe
 std::vector<VecS> sparam; //Parameter: Huegellaenge, Huegelbreite,k1,k2
 // Sigmoid-Funktion: s(x) = 1.0 / (1+exp(-k*x))  //x kann auch negativ sein
 // ax=xmitte-laenge/2; bx=xmitte+laenge/2;
 // y(x) = (x<xmitte) ? 1/(1+exp(-k1*(x-ax))) : 1/(1+exp(-k1*(bx-x)))
 // az=xmitte-breite/2; bz=xmitte+breite/2;
 // y(z) = (z<zmitte) ? 1/(1+exp(-k2*(z-az))) : 1/(1+exp(-k2*(bz-z)))
 // y(x,z) = y(x)*y(z)
 
public:
 void set_y0(float y) {y0=y;}
 float xmini= -100.0f,xmaxi=100.0f,zmini= -100.0f,zmaxi=100.0f; //Grenzen der Landschaft
 float meerweite=MEERWEITE;
#define MEERGR (100.0f+MEERWEITE)
 float xmini2= -MEERGR, xmaxi2=MEERGR, zmini2= -MEERGR, zmaxi2=MEERGR; //Maximale Grenzen fuer Meer
 void setGrenzen(float x1,float z1,float x2,float z2) {
  xmini=x1; zmini=z1; xmaxi=x2; zmaxi=z2;
  xmini2=x1-meerweite; zmini2=z1-meerweite; xmaxi2=x2+meerweite; zmaxi2=z2+meerweite;
  //printf("setGrenzen() xmini2=%f ... zmaxi2=%f\n",xmini2,zmaxi2);//test
 }
 void setMeerweite(float meerweit) {meerweite=meerweit; setGrenzen(xmini,zmini,xmaxi,zmaxi);}
 bool ist_in_wasser(Vec3 linkpos,float tiefe) {
  for(int i=0;i<numTeiche-1;i++)
   {
    if(linkpos.x > teichpara[i].gmin.x && linkpos.x < teichpara[i].gmax.x &&
       linkpos.z > teichpara[i].gmin.z && linkpos.z < teichpara[i].gmax.z && linkpos.y<=teichpara[i].gmin.y+tiefe)
     {
      return true;
     }
   }
  return false;
 }
 
 Huegel() {ymini=1e6f; ymaxi= -ymini; numGauss=numSigmoid=0;}
 void add_gausshuegel(Vec3 pos, VecN para) {
  position.push_back(pos); param.push_back(para);
  numGauss++;
 }
 void add_sigmoidhuegel(Vec3 pos, VecS para) {
  sposition.push_back(pos); sparam.push_back(para);
  numSigmoid++;
 }
 float yf(float x,float z); //Funktion zur Berechnung der Kombination aller Huegel
 Vec3 normalenvektor_berechnen(Vec3 v1,float dx);
 void minmaxtest();
 void teich_minmax(float wellhoehe, Vec3 mitte, float dx, Vec3* vmin, Vec3* vmax);
 void set(uint32 id,uint32 xnma,uint32 znma,float wellho,float wastand,int teichnr,Vec3 vmin,Vec3 vmax) {
   //if(teichnr>=8) {printf("Fehler: zu viele Teiche\n"); teichnr=7;}//test
   if(teichnr >= numTeiche)
    {
     Teichparameter para;
     teichpara.push_back(para);
     numTeiche++;
    }
   if(teichnr >= numTeiche) {printf("Fehler: teichnr=%d falsch\n",teichnr); teichnr=numTeiche-1;}//test
   Teichparameter *p = &teichpara[teichnr];
   p->meshid=id; p->xnmax=xnma; p->znmax=znma; p->wellenhoehe=wellho; p->wasserstand=wastand;
   p->gmin=vmin; p->gmax=vmax;
  }
 void get(uint32 *id,uint32 *xnma,uint32 *znma,float *wellho,float *wastand,int teichnr) {
   if(teichnr>=8) {printf("Fehler: zu viele Teiche\n"); teichnr=7;}//test
   Teichparameter *p= &teichpara[teichnr];
   *id=p->meshid; *xnma=p->xnmax; *znma=p->znmax; *wellho=p->wellenhoehe; *wastand=p->wasserstand;
  }
 void addWave(Wave w,int teichnr) {teichpara[teichnr].waves.push_back(w);}
 float getalfa(uint32 j,float delta,int teichnr) { //TODO
  Teichparameter *p= &teichpara[teichnr];
  p->waves[j].alfa += delta/p->waves[j].period;
  return p->waves[j].alfa;
 }
};

float Huegel::yf(float x,float z) //Funktion zur Berechnung der Kombination aller Huegel
{
 float y=y0, rquadrat, x1,z1,k1,k2,k3;
 for(int i=0;i<numGauss;i++)
  {
   k1=position[i].y; k2=0.5/param[i].a; k3=0.5/param[i].b;
   x1 = x-position[i].x; z1 = (z-position[i].z)*k3/k2;
   rquadrat = x1*x1+z1*z1;
   y += k1*exp(-k2*rquadrat);
  }
 float yx,yz,ax,bx,az,bz;
 for(int i=0;i<numSigmoid;i++)
  {
   float breitehalbe = sparam[i].a/2;
   float laengehalbe = sparam[i].b/2;
   ax=sposition[i].x-laengehalbe; bx=sposition[i].x+laengehalbe;
   az=sposition[i].z-breitehalbe; bz=sposition[i].z+breitehalbe;
   k1=sparam[i].k1; k2=sparam[i].k2;
   yx = (x<sposition[i].x) ? 1/(1+exp(-k1*(x-ax))) : 1/(1+exp(-k1*(bx-x)));
   yz = (z<sposition[i].z) ? 1/(1+exp(-k2*(z-az))) : 1/(1+exp(-k2*(bz-z)));
   y += yx*yz*sposition[i].y;
  }
 if(y<ymini) ymini=y;
 if(y>ymaxi) ymaxi=y;
 return y;
}

Vec3 Huegel::normalenvektor_berechnen(Vec3 v1,float dx)
{
 float dd=dx*0.01;
 Vec3 v2,v3,nv1,nv2;
 v2.x=v1.x; v2.z=v1.z+dd;    v2.y=yf(v2.x,v2.z);
 v3.x=v1.x+dd; v3.z=v1.z+dd; v3.y=yf(v3.x,v3.z);
 nv1 = v2 - v1;
 nv2 = v3 - v2;
 return glm::normalize(glm::cross(nv1,nv2));
}

Huegel landschaft1;

void landschaft1_erstellen(int nhueg,float *ktab,float *postabzx,
			   int nsighueg,float *sigmktab,float *sigmpostabzx)
{
 //printf("test: landschaft1_erstellen(nhueg=%d,ktab,postabzx, nsighuegel=%d,..)\n",nhueg,nsighueg);//test
 Vec3 position; //x-z-Position der Huegelmitte, y=Huegelhoehe
 VecN param; //Parameter: Huegellaenge, Huegelbreite
 VecS sparam; //Parameter fuer Sigmoid-Huegel
 for(int i=0;i<nhueg;i++)
  {
   int j=i*2, k=i*3;
   position.y = ktab[k];
   param.a = ktab[k+1]; param.b = ktab[k+2];
   position.x = postabzx[j+1];  position.z = postabzx[j];
   landschaft1.add_gausshuegel(position,param);
  }
 for(int i=0;i<nsighueg;i++)
  {
   int j=i*2, k=i*5;
   position.y = sigmktab[k];
   sparam.a = sigmktab[k+1]; sparam.b = sigmktab[k+2];
   sparam.k1 = sigmktab[k+3]; sparam.k2 = sigmktab[k+4];
   position.x = sigmpostabzx[j+1];  position.z = sigmpostabzx[j];
   landschaft1.add_sigmoidhuegel(position,sparam);
  }
}

void Huegel::minmaxtest() //test
{
 float x,y,z;
 float ymi=1e6f, yma= -ymi, xmi=0, xma=0, zmi=0, zma=0;
 int ix,iz;
 float dx=0.125f, dz=dx*wurzel3halbe;
 int xnmax= (int)((xmaxi-xmini)/dx+1.5);
 int znmax= (int)((zmaxi-zmini)/dz+1.5);
 printf("xmini=%f xmaxi=%f\n",xmini,xmaxi);//test
 printf("zmini=%f zmaxi=%f\n",zmini,zmaxi);//test
 for(ix=0,x=xmini; ix<=xnmax; ix++,x+=dx)
  for(iz=0,z=zmini; iz<=znmax;iz++,z+=dz)
   {
    y=yf(x,z);
    if(y<ymi) {ymi=y; xmi=x; zmi=z;}
    if(y>yma) {yma=y; xma=x; zma=z;}
   }
 if(ymini!=ymi || ymaxi!=yma) printf("ymini = %f  ymaxi = %f\n",ymini,ymaxi);
 printf("Tiefster  Punkt: x: %f y: %f z: %f\n",xmi,ymi,zmi);
 printf("Hoechster Punkt: x: %f y: %f z: %f\n",xma,yma,zma);
}

void Huegel::teich_minmax(float wellenhoehe, Vec3 mitte, float dx, Vec3* vmin, Vec3* vmax)
{
 float dz=dx*wurzel3halbe;
 float wasserstand=mitte.y+wellenhoehe;
 float x1=0,z1=0,x2=0,z2=0;
 float y1,y2; y1=y2=wasserstand;
 uint32 ix,iz;
 Vec3 v;
 //printf("Huegel::teich_minmax() wasserstand=%f dx=%f dz=%f\n",wasserstand,dx,dz);//test
 for(y1=y2=wasserstand; y1<=wasserstand || y2<=wasserstand || x1<=xmini2 || x2>=xmaxi2;)
  {
   v=mitte; v.x += x1;
   y1=yf(v.x,v.z); if(y1<=wasserstand) x1-=dx;
   v=mitte; v.x += x2;
   y2=yf(v.x,v.z); if(y2<=wasserstand) x2+=dx;
  }
 for(y1=y2=wasserstand; y1<=wasserstand || y2<=wasserstand || z1<=zmini2 || z2>=zmaxi2;)
  {
   v=mitte; v.z += z1;
   y1=yf(v.x,v.z); if(y1<=wasserstand) z1-=dz;
   v=mitte; v.z += z2;
   y2=yf(v.x,v.z); if(y2<=wasserstand) z2+=dz;
  }
 
 //testen ob durch x1,z1,x2,z2 definiertes Viereck vollstaendig an Land liegt:
 //printf("Grenzen pruefen: %f %f %f %f\n",x1,z1,x2,z2);//test
 for(int flag=4,timeout=0; flag!=0 && ++timeout<1000000;)
  {
   float x,y,z;
   float ymin=1e6f, xmin=0.0f, zmin=0.0f;
   flag=0;
   for(x=x1,z=z1; x<=x2; x+=dx)
    {
     if((y=yf(mitte.x+x,mitte.z+z)) <= wasserstand)
      {
       flag=1; if(y<ymin) {ymin=y; xmin=x;}
      }
    }
   if(flag==1)
    {
     while(z1>zmini2 && yf(mitte.x+xmin,mitte.z+z1) <= wasserstand) z1 -= dz;
    }
   ymin=1e6f; 
   for(x=x1,z=z2; x<=x2; x+=dx)
    {
     if((y=yf(mitte.x+x,mitte.z+z)) <= wasserstand)
      {
       flag=2; if(y<ymin) {ymin=y; xmin=x;}
      }
    }
   if(flag==2)
    {
     while(z2<zmaxi2 && yf(mitte.x+xmin,mitte.z+z2) <= wasserstand) z2 += dz;
    }
   ymin=1e6f; 
   for(z=z1,x=x1; z<=z2; z+=dz)
    {
     if((y=yf(mitte.x+x,mitte.z+z)) <= wasserstand)
      {
       flag=3;
       if(y<ymin) {ymin=y; zmin=z;}
      }
    }
   if(flag==3)
    {
     while(yf(mitte.x+x1,mitte.z+zmin) <= wasserstand) x1 -= dx;
    }
   ymin=1e6f; 
   for(z=z1,x=x2; z<=z2; z+=dz)
    {
     if((y=yf(mitte.x+x,mitte.z+z)) <= wasserstand)
      {
       flag=4;
       if(y<ymin) {ymin=y; zmin=z;}
      }
    }
   if(flag==4)
    {
     while(x2<xmaxi2 && yf(mitte.x+x2,mitte.z+zmin) <= wasserstand) x2 += dx;
    }
  }
 //printf("neue Grenzen: %f %f %f %f\n",x1,z1,x2,z2);//test
 
 vmin->x = x1; vmin->z = z1;
 vmax->x = x2; vmax->z = z2;
}
