#include <unistd.h>
#include <math.h>


#include "globals.h"
#include "model.h"


void GenModelPoints(struct Model *model)
{
  int i, j;
  struct ModelPoint **tempPoints;

  for (i=0; i<model->NumSides; i++) {
    for (j=0; j<3; j++) {
      tempPoints=&model->Points;
      while (*tempPoints!=NULL) {
        if (model->Sides[i].Vertices[j]->x==(*tempPoints)->point.x &&
            model->Sides[i].Vertices[j]->y==(*tempPoints)->point.y &&
            model->Sides[i].Vertices[j]->z==(*tempPoints)->point.z) {
          free(model->Sides[i].Vertices[j]);
          free(model->Sides[i].VertexColors[j]);
          model->Sides[i].Vertices[j]=&((*tempPoints)->point);
          model->Sides[i].VertexColors[j]=&((*tempPoints)->color);
          break;
        }
        tempPoints=&((*tempPoints)->next);
      }

      if (*tempPoints==NULL) {
        *tempPoints=(struct ModelPoint *)malloc(sizeof(struct ModelPoint));
        (*tempPoints)->point.x=model->Sides[i].Vertices[j]->x;
        (*tempPoints)->point.y=model->Sides[i].Vertices[j]->y;
        (*tempPoints)->point.z=model->Sides[i].Vertices[j]->z;
        free(model->Sides[i].Vertices[j]);
        (*tempPoints)->color.red=model->Sides[i].VertexColors[j]->red;
        (*tempPoints)->color.green=model->Sides[i].VertexColors[j]->green;
        (*tempPoints)->color.blue=model->Sides[i].VertexColors[j]->blue;
        free(model->Sides[i].VertexColors[j]);
        model->Sides[i].Vertices[j]=&((*tempPoints)->point);
        model->Sides[i].VertexColors[j]=&((*tempPoints)->color);
        (*tempPoints)->next=NULL;
      }
    }
  }
}


void AddModelDetail(struct Model **model)
{
  struct Model *tempModel;
  int i, j;
  float color, lengths[3], length, newlength, randomness;
  struct Point *newPoints[3];
  struct Color *newColors[3];
  struct ModelPoint **tempPoints;

  tempModel=(struct Model *)malloc(sizeof(struct Model));

  tempModel->NumSides=(*model)->NumSides*4;

  tempModel->Sides=(struct Triangle *)malloc(sizeof(struct Triangle)*
                                             tempModel->NumSides);
  tempModel->Points=(*model)->Points;

  for (i=0; i<(*model)->NumSides; i++) {
    for (j=0; j<3; j++) {
      lengths[j]=sqrt((*model)->Sides[i].Vertices[j]->x*
                      (*model)->Sides[i].Vertices[j]->x+
                      (*model)->Sides[i].Vertices[j]->y*
                      (*model)->Sides[i].Vertices[j]->y+
                      (*model)->Sides[i].Vertices[j]->z*
                      (*model)->Sides[i].Vertices[j]->z);
    }

    for (j=0; j<3; j++) {
      newPoints[j]=(struct Point *)malloc(sizeof(struct Point));
      newColors[j]=(struct Color *)malloc(sizeof(struct Color));

      newlength=(lengths[j]+lengths[(j+1)%3])/2;
      newPoints[j]->x=((*model)->Sides[i].Vertices[j]->x+
                       (*model)->Sides[i].Vertices[(j+1)%3]->x)/2;
      newPoints[j]->y=((*model)->Sides[i].Vertices[j]->y+
                       (*model)->Sides[i].Vertices[(j+1)%3]->y)/2;
      newPoints[j]->z=((*model)->Sides[i].Vertices[j]->z+
                       (*model)->Sides[i].Vertices[(j+1)%3]->z)/2;
      length=sqrt(newPoints[j]->x*newPoints[j]->x+newPoints[j]->y*newPoints[j]->y+
                  newPoints[j]->z*newPoints[j]->z);
      newPoints[j]->x*=newlength/length;
      newPoints[j]->y*=newlength/length;
      newPoints[j]->z*=newlength/length;

      newColors[j]->red=((*model)->Sides[i].VertexColors[j]->red+
                         (*model)->Sides[i].VertexColors[(j+1)%3]->red)/2;
      newColors[j]->green=((*model)->Sides[i].VertexColors[j]->green+
                           (*model)->Sides[i].VertexColors[(j+1)%3]->green)/2;
      newColors[j]->blue=((*model)->Sides[i].VertexColors[j]->blue+
                          (*model)->Sides[i].VertexColors[(j+1)%3]->blue)/2;

      tempPoints=&tempModel->Points;
      while (*tempPoints!=NULL) {
        if (newPoints[j]->x==(*tempPoints)->point.x &&
            newPoints[j]->y==(*tempPoints)->point.y &&
            newPoints[j]->z==(*tempPoints)->point.z) {
          free(newPoints[j]);
          free(newColors[j]);
          newPoints[j]=&((*tempPoints)->point);
          newColors[j]=&((*tempPoints)->color);
          break;
        }
        tempPoints=&((*tempPoints)->next);
      }

      if (*tempPoints==NULL) {
        *tempPoints=(struct ModelPoint *)malloc(sizeof(struct ModelPoint));
        (*tempPoints)->point.x=newPoints[j]->x;
        (*tempPoints)->point.y=newPoints[j]->y;
        (*tempPoints)->point.z=newPoints[j]->z;
        free(newPoints[j]);
        (*tempPoints)->color.red=newColors[j]->red;
        (*tempPoints)->color.green=newColors[j]->green;
        (*tempPoints)->color.blue=newColors[j]->blue;
        free(newColors[j]);
        newPoints[j]=&((*tempPoints)->point);
        newColors[j]=&((*tempPoints)->color);
        (*tempPoints)->next=NULL;
      }
    }

    for (j=0; j<3; j++) {
      tempModel->Sides[i*4+j].VertexColors[0]=newColors[j];
      tempModel->Sides[i*4+j].Vertices[0]=newPoints[j];

      tempModel->Sides[i*4+j].VertexColors[1]=(*model)->Sides[i].VertexColors[j];
      tempModel->Sides[i*4+j].Vertices[1]=(*model)->Sides[i].Vertices[j];

      tempModel->Sides[i*4+j].VertexColors[2]=newColors[(2+j)%3];
      tempModel->Sides[i*4+j].Vertices[2]=newPoints[(2+j)%3];
    }

    for (j=0; j<3; j++) {
      tempModel->Sides[i*4+3].VertexColors[j]=newColors[j];
      tempModel->Sides[i*4+3].Vertices[j]=newPoints[j];
    }
  }

  free((*model));
  (*model)=tempModel;
}


void RandomizeModel(struct Model *model, float Randomization)
{
  struct ModelPoint *tempPoints;
  float Randomness;

  tempPoints=model->Points;
  while (tempPoints) {
    Randomness=(((float)(rand()%256))*Randomization)/512.0-Randomization/2.0;
    tempPoints->point.x+=Randomness;
    Randomness=(((float)(rand()%256))*Randomization)/512.0-Randomization/2.0;
    tempPoints->point.y+=Randomness;
    Randomness=(((float)(rand()%256))*Randomization)/512.0-Randomization/2.0;
    tempPoints->point.z+=Randomness;
    tempPoints=tempPoints->next;
  }
}


void RandomizeModelColors(struct Model *model)
{
  struct ModelPoint *tempPoints;
  float color;

  tempPoints=model->Points;
  while (tempPoints) {
    color=((float)(rand()%256))/1280.0+0.1;
    tempPoints->color.red=color;
    tempPoints->color.green=color;
    tempPoints->color.blue=color;
    tempPoints=tempPoints->next;
  }
}


void ShrinkModel(struct Model *model, float Size)
{
  struct ModelPoint *tempPoints;
  int i, j;

  if (model->Points) {
    tempPoints=model->Points;
    while (tempPoints) {
      tempPoints->point.x*=Size;
      tempPoints->point.y*=Size;
      tempPoints->point.z*=Size;
      tempPoints=tempPoints->next;
    }
  } else {
    for (i=0; i<model->NumSides; i++) {
      for (j=0; j<3; j++) {
        model->Sides[i].Vertices[j]->x*=Size;
        model->Sides[i].Vertices[j]->y*=Size;
        model->Sides[i].Vertices[j]->z*=Size;
      }
    }
  }
}


void InvertModel(struct Model *model, int Type)
{
  struct ModelPoint *tempPoints;
  int i, j;

  if (model->Points) {
    tempPoints=model->Points;
    while (tempPoints) {
      if (Type==0) {
        tempPoints->point.x*=-1.0;
      } else if (Type==1) {
        tempPoints->point.y*=-1.0;
      } else {
        tempPoints->point.z*=-1.0;
      }
      tempPoints=tempPoints->next;
    }
  } else {
    for (i=0; i<model->NumSides; i++) {
      for (j=0; j<3; j++) {
        if (Type==0) {
          model->Sides[i].Vertices[j]->x*=-1.0;
        } else if (Type==1) {
          model->Sides[i].Vertices[j]->y*=-1.0;
        } else {
          model->Sides[i].Vertices[j]->z*=-1.0;
        }
      }
    }
  }
}


void ReCenterModel(struct Model *model)
{
  float x=0.0, y=0.0, z=0.0;
  int NumPoints;

  struct ModelPoint *tempPoints;

  tempPoints=model->Points;
  while (tempPoints) {
    NumPoints++;
    x+=tempPoints->point.x;
    y+=tempPoints->point.y;
    z+=tempPoints->point.z;
    tempPoints=tempPoints->next;
  }
  x/=(float)NumPoints;
  y/=(float)NumPoints;
  z/=(float)NumPoints;

  tempPoints=model->Points;
  while (tempPoints) {
    tempPoints->point.x-=x;
    tempPoints->point.y-=y;
    tempPoints->point.z-=z;
    tempPoints=tempPoints->next;
  }
}


float FindFurthestPoint(struct Model *model)
{
  struct ModelPoint *tempPoints;
  float dist=0.0, newdist;
  int i, j;

  if (model->Points) {
    tempPoints=model->Points;
    while (tempPoints) {
      newdist=sqrt((tempPoints->point.x*tempPoints->point.x)+
                   (tempPoints->point.y*tempPoints->point.y)+
                   (tempPoints->point.z*tempPoints->point.z));
      if (newdist > dist) {
        dist=newdist;
      }
      tempPoints=tempPoints->next;
    }
  } else {
    for (i=0; i<model->NumSides; i++) {
      for (j=0; j<3; j++) {
        newdist=sqrt((model->Sides[i].Vertices[j]->x*
                      model->Sides[i].Vertices[j]->x)+
                     (model->Sides[i].Vertices[j]->y*
                      model->Sides[i].Vertices[j]->y)+
                     (model->Sides[i].Vertices[j]->z*
                      model->Sides[i].Vertices[j]->z));
        if (newdist > dist) {
          dist=newdist;
        }
      }
    }
  }

  return(dist);
}
