// objekte_classdef.h
#pragma once
//class Mesh;    // Daten eines Gitters (Teil eines Modells) im Grafikkarten-Speicher
//class Model;   // Daten eines Modells im Grafikkarten-Speicher (beinhaltet mehrere Mesh)
class Meshdata;  // Daten eines Gitters im normalen Speicher
class Modeldata; // Daten eines Modells im normalen Speicher
class Klon;      // Daten fuer ein Klon eines Modells, Realisierung eines Objekts in Spielwelt
class Objekte;   // Mehrere Modelle zusammgefasst, inklusive Daten fuer Klone
class Animobjekt; //Animiertes Modell, mehrere Bewegungszustaende als Modeldata gespeichert
class Ding;    // Physikalische Daten eines Klons (Modells) oder eines Animobjekts
class Screen;  // Status-Screen zum Symbole und Texte anzeigen
class Box;     // Viereckiger Kasten um ein Objekt, z.B. fuer Kollisionserkennung

class Box
{
public:
 glm::vec3 minv, maxv;
 Box() {minv = glm::vec3(1000.0f,1000.0f,1000.0f); maxv = -minv;}
 void check(glm::vec3& v);
 void check(glm::vec3& v, glm::mat4& matrix);
 glm::vec3 getMitte() {return (maxv+minv)/2.0f;}
 float getBreite() {return maxv.x-minv.x;}
 float getHoehe()  {return maxv.y-minv.y;}
 float getTiefe()  {return maxv.z-minv.z;}
 void addOffset(glm::vec3 offset) {minv += offset; maxv += offset;}
};

class Kollform
{
public:
 uint8 form=0;
#define FORM_KUGEL 1
#define FORM_ZYLINDER 2
#define FORM_QUADER 3
 float radius; //fuer Kugel und Zylinder
 float beta; //fuer verdrehten Quader
 glm::vec3 fusspunkt, kopfpunkt; //fuer Zylinder und Quader
 float laenge,hoehe,breite; //fuer Quader, und hoehe auch fuer Zylinder
};

class Meshdata {
public:
 Material material;
 uint64 numVertices;
 std::vector<Vertex> vertices;
 uint64 numIndices;
 std::vector<uint32> indices;
 uint32 linienflag;
 Meshdata() {numVertices=numIndices=0; linienflag=0;}
 Meshdata(Material mat) {numVertices=numIndices=0; material=mat;}
 void setMaterial(Material mat) {material=mat;}
 void setoffset(float32 x,float32 y,float32 z);
 void rotateXgrad(float32 wgrad);
 void rotateYgrad(float32 wgrad);
 void rotateZgrad(float32 wgrad);
 void scale(float32 scalefactor);
 void addVertex(Vertex& vertex) {numVertices++; vertices.push_back(vertex);}
 void addIndex(uint32 index) {numIndices++; indices.push_back(index);}
 // Die beiden Vektoren in getMinMaxPos muessen schon vorbelegt sein, z.B. mit 0-Werten
 void getMinMaxPos(glm::vec3 *minvec, glm::vec3 *maxvec);
 void getMinMaxPos(Box* box, glm::mat4& matrix);
 void getMinMaxPos(Box* box);
 void matrixmult(glm::mat4 matrix);
 glm::vec3 summe_aller_vertices();
 void setColor(glm::vec3 color); //TODO: setColor(Material& mat);//??
};

class Modeldata {
 char name[64];
public:
 uint32 typ; //Typ des Objekts, z.B. Herz, Ring, ...
 uint32 art=0; //Art des Objekts
#define ART_SAMMELOBJEKT 0
#define ART_FIXESOBJEKT 1
#define ART_LOSESOBJEKT 2
#define ART_WASSEROBJEKT 3
#define ART_LINIENOBJEKT 4 //TODO
 void setArt(uint32 a) {art=a;}
 uint64 numMeshes; //TODO: umbenennen: uint32 numMeshdatas waehre logischer
 std::vector<Meshdata*> meshdatas;
 Modeldata() {numMeshes=0; typ=0; name[0]=0;}
 ~Modeldata() {clear();}
 void init(const char* filename);
 void initline(); //TODO: initline(std::vector<Linienzug> &lin); ? bisher global definiert
 void initcalc(const char *filename);
 void recalc(float delta,int teichnr);//test
 void setName(const char *nam) {mystrncpy(name,nam,64); typ = calc_typ(nam);}
 uint32 getTypName(char *nam, int max, uint32 *ar) {mystrncpy(nam,name,max); *ar=art; return typ;}
 uint64 getNumVertices();
 void addVertex(Vertex& vertex);
 void addIndex(uint32 index);
 uint32 addmesh(Material mat); //Rueckgabe von meshid (vorheriges numMeshes)
 void clear();
 void setoffset(float32 x,float32 y,float32 z);
 glm::vec3 getModelsize();
 glm::vec3 mittelwert_aller_vertices();
 void setColor(glm::vec3 color) {meshdatas[0]->setColor(color);}
};

class Screen {
 float breite,hoehe;
 int aktuellefarbe; //r,g,b-Farbe als 0xBBGGRR
 int altefarbe;
 Material material;
 Modeldata modeldata; //fuer Zeichung aus Dreiecken, Linien, Punkten, und Text
 Model *model; //erstellen zum Daten auf Grafikkarte kopieren
public:
 float xmin,xmax,ymin,ymax,xscal,yscal; //Werte fuer Koordinaten-Umrechnungen
 uint8 screenId; //2 oder groesser (1 ist 3D-Welt)
 MyCamera *camera;
 Shader *shader;
 
 Screen() {breite=hoehe=0; camera=NULL; altefarbe=0; aktuellefarbe=0xFFFFFF; model=NULL; screenId=2;}
 ~Screen() {if(camera!=NULL) delete camera; if(model!=NULL) delete model;}
 void init(int br,int ho,int screenid,Shader *shad);
 
 void render(Objekte& objekte); //gesammelte Objekte zeichnen
 void render2(); //Linien, Punkte, Texte zeichnen
 
 void welt2pixel(float32 xf, float32 yf, int *ix, int *iy); //Umrechnung auf Pixelkoordinaten
 void welt2pixel(float32 xf, int *ix); //Distanz umrechnen
 void pixel2welt(int ix, int iy, float32* xf, float32* yf); //Umrechnung von Pixel nach Welt-Koordinaten
 void pixel2welt(int ix, float32* xf); //Umrechnung von Pixel-Distanz nach Distanz in Welt-Koordinaten

 void rgbcolor(int r,int g,int b);
 void ifillbox(int x1,int y1,int x2,int y2);
 void fillbox(float x1,float y1,float x2,float y2);
 void drawLine(int x1,int y1,int x2,int y2);
 void drawPoint(float x,float y);
 void setprintpos(int x,int y);
 void print(const char *str);
 void clearDrawings();
 void flush();
};

class Klon {  //Position und Matrix um ein Modell mehrfach zu verwenden
public:
 glm::mat4 modelMatrix; //Rotationen, Skalierung und auch Position
 glm::vec3 position = glm::vec3(0.0f); //Position (schon in der modelMatrix beruecksichtigt)
 uint32 typ; //Art des Objekts, wie in Modeldata, benuzen zum entscheiden wo auf dem Status-Sreen anzeigen
 uint8 screenId; //status fuer Anzeigeort: 0:inaktiv, 1: in Spielwelt, 2: im Status-Screen sichtbar
 Box kollBox;
 Kollform kollform; //TODO
 Klon() {screenId=1;}
 Klon(glm::mat4 matrix, uint32 ty, uint8 id) {typ=ty; screenId=id; modelMatrix=matrix;}
 void setposition(glm::vec3 pos);  //Position setzen
 void setoffset(glm::vec3 offset); //Position verschieben
 void clearoffset() {setoffset(-position);} //Position wieder auf Koordinatenursprung setzen
 void clearMatrix() {modelMatrix=glm::mat4(1.0f); position=glm::vec3(0.0f);}
 void rotateX(float32 w); //Winkel in Radians
 void rotateY(float32 w); //Winkel in Radians
 void rotateZ(float32 w); //Winkel in Radians
 void rotateXgrad(float32 wgrad) {rotateX(wgrad*GRAD);}
 void rotateYgrad(float32 wgrad) {rotateY(wgrad*GRAD); kollform.beta -= wgrad*GRAD;}
 void rotateZgrad(float32 wgrad) {rotateZ(wgrad*GRAD);}
 void scale(float32 scalefactor);
 void scale(float32 sx,float32 sy,float32 sz);
 void setkollform(Kollform& form);
};

struct Klonlist {
 uint32 numKlones;
 std::vector<Klon> klones;
 Klonlist() {numKlones=0;}
 //~Klonlist() {for(uint32 i=0;i<numKlones;i++) klones.pop_back;} //TODO: ist das noetig?
 void add(Klon klon) {klones.push_back(klon); numKlones++;}
 void remove() {if(numKlones>0) {klones.pop_back(); numKlones--;}}//TODO: ueberpruefen ob korrekt
};

class Objekte {
public:
 uint32 numModeldatas;
 uint32 numModels; //gleich wie numModeldatas
 std::vector<Modeldata*> modeldatas; //Die Daten der Modelle um die models zu machen
 std::vector<Model*> models; //Grafikkarte (GPU) gespeicherte Daten
 std::vector<Klonlist*> klonlists; //mehrere Listen von Klonen (Anzahl ist numModels)
public:
 uint32 getTypName(uint32 modelid, char *name, int max, uint32 *art) {
  return modeldatas[modelid]->getTypName(name,max,art);
 }
 glm::vec3 getPosition(uint32 modelid,uint32 klonid);
 void setPosition(uint32 modelid,uint32 klonid,glm::vec3 pos);
 void setColor(uint32 modelid,glm::vec3 color,Shader *shader);//TODO //Farbe von erstem Mesh setzen (fuer Sonne)
 Objekte() {numModels=numModeldatas=0;}
 ~Objekte() {
  for(uint32 i=0;i<numModels;i++)
   {delete models[i]; delete klonlists[i];}
  for(uint32 i=0;i<numModeldatas;i++)
   {delete modeldatas[i];}
 }
 void init(const char *filename, Shader *shader);
 uint32 addKlon(uint32 modelid, glm::vec3 position, glm::mat4 matrix);
 uint32 removeKlon(uint32 modelid);//TODO
 //Objekt auf Status-Screen verschieben:
 uint32 moveToScreen(uint32 modelid,uint32 klonid,Screen* screen);
 void render(uint8 screenid,MyCamera* camera, Shader* shader);
 Box getModelbox(uint32 modelid,uint32 klonid);
 void calculate_kollBoxes(); //TODO
};

class Animobjekt {
 uint16 numDatas; //gesamte Anzahl Bewegungszustaende
 uint16 index;    //aktueller Bewegungszustand
 uint32 numModels; //provisorisch fuer init()
 uint8 status; //TODO
 std::vector<Modeldata*> modeldatas; //Je ein Modell pro Bewegungszustand
 std::vector<Model*> models; //alle Modelle (keine Klone) auf Grafikkarte (GPU) gespeichert
public:
 uint16 get_maxIndex() {return numDatas;}
 glm::vec3 position; //aktuelle Position
 float32 yrotation; //aktuelle Rotation um y-Achse (in Radians)
 glm::mat4 modelMatrix; //Drehungen und Position (yrotation und position mit beruecksichtigt)
 Box kollBox;
 Kollform kollform; //TODO
 Animobjekt() {
  status=1; numDatas=index=0; numModels=0;
  position=glm::vec3(0.0f); yrotation=0.0f; modelMatrix=glm::mat4(1.0f);
  kollform.form=FORM_ZYLINDER; //provi. fuer LINK
  kollform.radius = 0.5f; //provi. fuer LINK
 }
 //void render(); //Zeichnen des aktuellen Bewegungszustands
 void render(MyCamera* camera, Shader* shader); //Zeichnen mit Beruecksichtigung von modelMatrix
 void animate(uint32 frameid, glm::vec3 animOffset, float animRotate);
 void init(const char *filename, Shader *shader);
 void calculate_kollBox();
};
