#import "AzimuthMat.h"
#import "math.h"
#import <string.h>

@implementation AzimuthMat

+new
{
  self = [super new];
  limits[0] = limits[1] = limits[2] = 0.;
  limits[3] = limits[4] = limits[5] = 1.;
  [self reset];
  phi = s_phi = 0.; c_phi = 1.0;
  dist = 2.0;
  [self setTheta:0.];
}

+newLimits:(float *)newlimits
{
  register short int n = 6;
  register float *lim;

  self = [super new];
  lim = limits;
  while(n--) *lim++ = *newlimits++;
  [self reset];
  phi = s_phi = 0.; c_phi = 1.0;
  dist = 2.0;
  [self setTheta:0.];
}

-reset
{
  [super reset];
  [self translate :-(limits[0] + limits[3]) / 2
		:-(limits[1] + limits[4]) / 2
		:-(limits[2] + limits[5]) / 2];
  [self scale :1. / (limits[3] - limits[0])
            :1. / (limits[4] - limits[1])
            :1. / (limits[5] - limits[2])];
  [self x_rotation_cs :0. :-1.];
  memmove(to_theta_cache, mat, 16*sizeof(float));
  is_cached_to_theta = YES;
}

-setlimits:(float *)newlimits
{
  register short int n = 6;
  register float *lim;

  lim = limits;
  while(n--) *lim++ = *newlimits++;
  [self reset];
}

-setTheta:(float)radians
{
  if (is_cached_to_theta)
    memmove(mat, to_theta_cache, 16*sizeof(float));
  else {
    [self reset];
    memmove(to_theta_cache, mat, 16*sizeof(float));
    is_cached_to_theta = YES;
  }
  is_cached_to_phi = is_cached_to_dist = NO;
  theta = radians;
  [self y_rotation_cs:(c_theta = cos(radians)) :(s_theta = sin(radians))];
  [self x_rotation_cs:c_phi :s_phi];
  [self perspective:dist];
}

-setPhi:(float)radians
{
  if (is_cached_to_phi)
    memmove(mat, to_phi_cache, 16*sizeof(float));
  else {
    if (is_cached_to_theta)
      memmove(mat, to_theta_cache, 16*sizeof(float));
    else
      [self reset];
    [self y_rotation_cs:c_theta:s_theta];
    memmove(to_phi_cache, mat, 16*sizeof(float));
    is_cached_to_phi = YES;
  }
  is_cached_to_dist = NO;
  phi = radians;
  [self x_rotation_cs:(c_phi = cos(radians)) :(s_phi = sin(radians))];
  [self perspective:dist];
}

-setdist:(float)distance
{
  if (is_cached_to_dist)
    memmove(mat, to_dist_cache, 16*sizeof(float));
  else {
    if (is_cached_to_theta)
      memmove(mat, to_theta_cache, 16*sizeof(float));
    else
      [self reset];
    [self y_rotation_cs:c_theta:s_theta];
    [self x_rotation_cs:c_phi:s_phi];
    memmove(to_dist_cache, mat, 16*sizeof(float));
    is_cached_to_dist = YES;
  }
  dist = distance;
  [self perspective:dist];
}

-setinvdist:(float)invdistance
{
  if (is_cached_to_dist)
    memmove(mat, to_dist_cache, 16*sizeof(float));
  else {
    if (is_cached_to_theta)
      memmove(mat, to_theta_cache, 16*sizeof(float));
    else
      [self reset];
    [self y_rotation_cs:c_theta:s_theta];
    [self x_rotation_cs:c_phi:s_phi];
    memmove(to_dist_cache, mat, 16*sizeof(float));
    is_cached_to_dist = YES;
  }
  if (invdistance == 0.) dist = HUGE;
  else dist = 1./invdistance;
  [self perspective_inv:invdistance];
}

-(float)getTheta:sender;
{
  return theta;
}

-(float)getPhi:sender;
{
  return phi;
}

-(float)getdist:sender;
{
  return dist;
}
