#include <TextureMap.h>

TextureMap::TextureMap () {
  int i;
  
  size         = SbVec2s(DEFAULT_S, DEFAULT_T);
  components   = 3;
  filterRadius = 5;
  filter       = QUADRATIC;

  totals    = new float[size[0]*size[1]];
  imgfloats = new float[size[0]*size[1]*components];
  image     = new unsigned char[size[0]*size[1]*components];
  
  for (i = 0; i < size[0]*size[1]; i++)
    totals[i] = 0.0;

  for (i = 0; i < size[0]*size[1]*components; i++) {
    imgfloats[i] = 0.0;
    image[i]     = 0;
  }
}

TextureMap::TextureMap (SbVec2s s, int nc) {
  int i;
  
  size         = s;
  components   = nc;
  filterRadius = 5;
  filter       = QUADRATIC;
  
  totals    = new float[size[0]*size[1]];
  imgfloats = new float[size[0]*size[1]*components];
  image     = new unsigned char[size[0]*size[1]*components];

  for (i = 0; i < size[0]*size[1]; i++)
    totals[i] = 0.0;

  for (i = 0; i < size[0]*size[1]*components; i++) {
    imgfloats[i] = 0.0;
    image[i]     = 0;
  }
}

TextureMap::~TextureMap () {
  delete [] totals;
  delete [] imgfloats;
  delete [] image;
}

unsigned char *TextureMap::getValue (SbVec2s &s, int &nc) {
  s  = size;
  nc = components;
  return image;
}

void TextureMap::setTexture (SbColor color) {
  for (int i = 0; i < size[0]*size[1]*components; i += components)
    for (int j = 0; j < components; j++) {
      imgfloats[i + j] = color[j];
      image[i + j]     = (unsigned char)(255.0 * imgfloats[i + j]);
    }
} 

void TextureMap::drawSTDot (float phi, float theta, SbColor color) {
  float sdelta, tdelta, s1, t1, s2, t2;
  float weight, sinVal, sdist, tdist, dist;
  int width, currFilRad;

  currFilRad = filterRadius;
  
  if (currFilRad <= 0)
    setPixel(phi, theta, color, 1.0);
  else {
    sdelta = 2*M_PI/size[0];
    tdelta =   M_PI/size[1];

    for (int i = 0; i < currFilRad*2; i++) {
      t1 = phi + tdelta*i - tdelta*currFilRad;
    
      sinVal = sin(t1);
      if (sinVal > 0.0)
        width = (int)(currFilRad/sinVal) * 2;
      else
        width = size[0];

      if (width > size[0])
        width = size[0];

      if (t1 >= M_PI)
        t2 = M_PI + (M_PI - t1);
      else if (t1 < 0.0)
        t2 = fabs(t1);
      else
        t2 = t1;

      tdist = pow((t1 - phi), 2.0);
      for (int j = 0; j < width; j++) {
        s1 = theta + sdelta*j - sdelta*(width/2);
 
        if (s1 < 0.0)
	  s2 = s1 + 2*M_PI;
        else if (s1 >= 2*M_PI)
	  s2 = s1 - 2*M_PI;
        else
	  s2 = s1;

        sdist = pow((s1 - theta) * sinVal, 2.0);
        dist = sqrt(sdist + tdist);

        switch (filter) {
        case BOX:
          weight = box(dist, currFilRad*tdelta);
          break;
        case TENT:
          weight = tent(dist, currFilRad*tdelta);
          break;
        case QUADRATIC:
          weight = quadratic(dist, currFilRad*tdelta);
          break;
        case CUBIC:
          weight = cubic(dist, currFilRad*tdelta);
          break;
        case QUARTIC:
          weight = quartic(dist, currFilRad*tdelta);
          break;
        default:
          weight = tent(dist, currFilRad*tdelta);
        }

	if (weight > 0.0)
          this->setPixel(t2, s2, color, weight);
      }
    }
  }
}

void TextureMap::setPixel (float t, float s, SbColor color, float weight) {
  int sIndex, tIndex, totIndex, imgIndex;
  
  sIndex = (int)(s/(2*M_PI)*size[0]);
  tIndex = (int)(t/M_PI*size[1]);

  totIndex = tIndex*size[0] + sIndex;
  imgIndex = tIndex*components*size[0] + components*sIndex;

  totals[totIndex] += weight;
  for (int i = 0; i < components; i++) {
    float val;
    
    imgfloats[imgIndex + i] += color[i] * weight;
    
    if (totals[totIndex] > 1.0)
      val = 255.0 * (imgfloats[imgIndex + i] / totals[totIndex]);
    else
      val = 255.0 * imgfloats[imgIndex + i];

    if (val > 255.0)
      image[imgIndex + i] = 255;
    else
      image[imgIndex + i] = val;
  }
}

void TextureMap::reset () {
  int i;
  
  for (i = 0; i < size[0]*size[1]; i++)
    totals[i] = 0.0;
  for (i = 0; i < size[0]*size[1]*components; i++) {
    imgfloats[i] = 0.0;
    image[i] = 0;
  }
}
  
float TextureMap::box (float dist, float rad) {
  if (dist <= rad)
    return 1.0;
  else
    return 0.0;
}

float TextureMap::tent (float dist, float rad) {
  if (dist <= rad)
    return (1.0 - (1.0/rad) * dist);
  else
    return 0.0;
}

float TextureMap::quadratic (float dist, float rad) {
  if (dist <= rad)
    return (1.0 - (1.0 / pow(rad, 2.0) * pow(dist, 2.0)));
  else
    return 0.0;
}

float TextureMap::cubic (float dist, float rad) {
  if (dist <= rad) {
    float a, b, c;
    a = 1.0;
    b = (-3.0 / pow(rad, 2.0)) * pow(dist, 2.0);
    c = ( 2.0 / pow(rad, 3.0)) * pow(dist, 3.0);
    return (a + b + c);
  }
  else
    return 0.0;
}

float TextureMap::quartic (float dist, float rad) {
  if (dist <= rad) {
    float a, b, c;
    a = 1.0;
    b = (-3.0 / pow(rad, 2.0)) * pow(dist, 2.0);
    c = ( 2.0 / pow(rad, 3.0)) * pow(dist, 3.0);
    return (a + b + c);
  }
  else
    return 0.0;
}
