/**********************************************************************
This file is part of Crack dot Com's free source code release of Golgotha.
for information about compiling & licensing issues visit this URL
 If that doesn't help, contact Jonathan Clark at 
  golgotha_source@usa.net (Subject should have "GOLG" in it) 
***********************************************************************/

#include 
#include 
#include 

typedef char *cell;
typedef cell matrix[4][4];

cell newcell()
{
  cell ret = (char*)malloc(80);
  ret[0] = 0;
  return ret;
}

char *add_term(char *s, char *t)
{
  cell ret;

  if (!t)
    ret = s;
  else if (!s)
    ret = strdup(t);
  else {
    ret = newcell();

    if (atoi(s) && atoi(t)) {
      sprintf(ret,"%+d",atoi(s)+atoi(t));
    }
    else {
      strcat(ret,s);
      strcat(ret,t);
    }
  }
  return ret;
}

char *mult_term(char *s, char *t)
{
  cell ret;

  if (!s || !t)
    ret = 0;
  else if (atoi(t)==1)
    ret = strdup(s);
  else if (atoi(t)==-1) {
    ret = strdup(s);
    ret[0] = ((ret[0]=='-')?'+':'-');
  }
  else if (atoi(s)==1) {
    ret = strdup(t);
  }
  else if (atoi(s)==-1) {
    ret = strdup(t);
    ret[0] = ((ret[0]=='-')?'+':'-');
  }
  else {
    ret = newcell();
    if (atoi(s) && atoi(t)) {
      sprintf(ret,"%+d",atoi(s)*atoi(t));
    }
    else {
      strcat(ret, s);
      strcat(ret, t+1);
      ret[0] = (((s[0]=='-') ^ (t[0]=='-'))?'-':'+');
    }
  }
  return ret;
}

void matmult(matrix a, matrix b, matrix result)
{
  for (int j=0; j<4; j++) 
    for (int i=0; i<4; i++) {
      cell sum = 0;
      cell prod;
      for (int k=0; k<4; k++)
	sum = add_term(sum, mult_term(a[j][k], b[k][i]));
      result[j][i] = sum;
    }
}

void matdup(matrix a,matrix b)
{
  for (int j=0; j<4; j++)
    for (int i=0; i<4; i++)
      a[j][i] = (b[j][i]?strdup(b[j][i]):0);
}

void mattranspose(matrix a,matrix dest)
{
  for (int j=0; j<4; j++)
    for (int i=0; i<4; i++)
      dest[j][i] = (a[i][j]?strdup(a[i][j]):0);
}

void matnegate(matrix a,matrix dest)
{
  for (int j=0; j<4; j++)
    for (int i=0; i<4; i++)
      dest[j][i] = mult_term(a[i][j],"-1");
}

void mat_gett(matrix a,matrix dest)
{
  for (int j=0; j<4; j++)
    for (int i=0; i<4; i++)
      if (i==j)
        dest[j][i] = strdup("+1");
      else if (i>=3)
        dest[j][i] = (a[j][i]?strdup(a[j][i]):0);
      else
        dest[j][i] = 0;
}

void mat_getrot(matrix a,matrix dest)
{
  for (int j=0; j<3; j++)
  {
    dest[3][j] = 0;
    dest[j][3] = 0;
    for (int i=0; i<3; i++)
      dest[j][i] = (a[j][i]?strdup(a[j][i]):0);
  }
  dest[3][3] = strdup("+1");
}

void print(matrix a)
{
  for (int j=0; j<4; j++)
    for (int i=0; i<4; i++)
      printf("(%d,%d) = %s\n", j+1,i+1, a[j][i]?a[j][i]:"0");
}

void printmat(matrix a)
{
  int len[4], i,j,l;
  char buf[4][20];

  for (i=0; i<4; i++)
    len[i]=1;

  for (j=0; j<4; j++)
    for (i=0; i<4; i++)
      if (a[j][i])
        if ((l=strlen(a[j][i]))>len[i])
          len[i]=l;

  for (i=0; i<4; i++)
    sprintf(buf[i]," %%-%ds ",len[i]);

  for (j=0; j<4; j++)
  {
    printf("[ ");
    for (i=0; i<4; i++)
      printf(buf[i], a[j][i]?a[j][i]:"0");
    printf(" ]\n");
  }
}

void printaff(matrix a)
{
  static char pr[]="xyzt";

  for (int i=0; i<4; i++)
    for (int j=0; j<3; j++)
      printf("aff.%c.%c = %s\n", pr[i],pr[j], a[j][i]?a[j][i]:"0");
}


matrix blank =
{ { "+", "+", "+", "+" },
  { "+", "+", "+", "+" },
  { "+", "+", "+", "+" },
  { "+", "+", "+", "+" } };

matrix v =
{ { "+v(1,1)", "+v(1,2)", "+v(1,3)", "+v(1,4)" },
  { "+v(2,1)", "+v(2,2)", "+v(2,3)", "+v(2,4)" },
  { "+v(3,1)", "+v(3,2)", "+v(3,3)", "+v(3,4)" },
  { "+v(4,1)", "+v(4,2)", "+v(4,3)", "+v(4,4)" } };

matrix aff =
{ { "+aff.x.x", "+aff.y.x", "+aff.z.x", "+aff.t.x" },
  { "+aff.x.y", "+aff.y.y", "+aff.z.y", "+aff.t.y" },
  { "+aff.x.z", "+aff.y.z", "+aff.z.z", "+aff.t.z" },
  { 0         , 0         , 0         , "+1"       } };

matrix m1 =
{ { "+1", 0,    0,    0    },
  { 0,    "+1", 0,    0    },
  { 0,    0,    "+1", 0    },
  { 0,    0,    0,    "+1" } };

matrix m0 =
{ { 0, 0, 0, 0 },
  { 0, 0, 0, 0 },
  { 0, 0, 0, 0 },
  { 0, 0, 0, 0 } };

matrix rotz =
{ { "+cos(z)", "-sin(z)", 0        , 0         },
  { "+sin(z)", "+cos(z)", 0        , 0         },
  { 0        , 0        , "+1"     , 0         },
  { 0        , 0        , 0        , "+1"      } };

matrix irotz =
{ { "+cos(z)", "+sin(z)", 0        , 0         },
  { "-sin(z)", "+cos(z)", 0        , 0         },
  { 0        , 0        , "+1"     , 0         },
  { 0        , 0        , 0        , "+1"      } };

matrix roty =
{ { "+cos(y)", 0        , "+sin(y)", 0         },
  { 0        , "+1"     , 0        , 0         },
  { "-sin(y)", 0        , "+cos(y)", 0         },
  { 0        , 0        , 0        , "+1"      } };

matrix iroty =
{ { "+cos(y)", 0        , "-sin(y)", 0         },
  { 0        , "+1"     , 0        , 0         },
  { "+sin(y)", 0        , "+cos(y)", 0         },
  { 0        , 0        , 0        , "+1"      } };

matrix rotx =
{ { "+1"     , 0        , 0        , 0         },
  { 0        , "+cos(x)", "-sin(x)", 0         },
  { 0        , "+sin(x)", "+cos(x)", 0         },
  { 0        , 0        , 0        , "+1"      } };

matrix irotx =
{ { "+1"     , 0        , 0        , 0         },
  { 0        , "+cos(x)", "+sin(x)", 0         },
  { 0        , "-sin(x)", "+cos(x)", 0         },
  { 0        , 0        , 0        , "+1"      } };

matrix xlat =
{ { "+1"     , 0        , 0        , "+tx"     },
  { 0        , "+1"     , 0        , "+ty"     },
  { 0        , 0        , "+1"     , "+tz"     },
  { 0        , 0        , 0        , "+1"      } };

matrix ixlat =
{ { "+1"     , 0        , 0        , "-tx"     },
  { 0        , "+1"     , 0        , "-ty"     },
  { 0        , 0        , "+1"     , "-tz"     },
  { 0        , 0        , 0        , "+1"      } };

matrix scale =
{ { "+scalex", 0        , 0        , 0         },
  { 0        , "+scaley", 0        , 0         },
  { 0        , 0        , "+scalez", 0         },
  { 0        , 0        , 0        , "+1"      } };

matrix iscale =
{ { "+(1/scalex)", 0            , 0            , 0         },
  { 0            , "+(1/scaley)", 0            , 0         },
  { 0            , 0            , "+(1/scalez)", 0         },
  { 0            , 0            , 0            , "+1"      } };

matrix cam =
{ { "+cam.x.x", "+cam.y.x", "+cam.z.x", "+cam.t.x" },
  { "+cam.x.y", "+cam.y.y", "+cam.z.y", "+cam.t.y" },
  { "+cam.x.z", "+cam.y.z", "+cam.z.z", "+cam.t.z" },
  { 0         , 0         , 0         , "+1"       } };

matrix icamt =
{ { "+1"     , 0        , 0        , "-cam.t.x"     },
  { 0        , "+1"     , 0        , "-cam.t.y"     },
  { 0        , 0        , "+1"     , "-cam.t.z"     },
  { 0        , 0        , 0        , "+1"      } };

matrix point =
{ { 0        , 0        , 0        , "+px"      },
  { 0        , 0        , 0        , "+py"      },
  { 0        , 0        , 0        , "+pz"      },
  { 0        , 0        , 0        , "+1"       } };

matrix front =
{ { 0        , 0        , 0        , "+1"       },
  { 0        , 0        , 0        , 0          },
  { 0        , 0        , 0        , 0          },
  { 0        , 0        , 0        , "+1"       } };

void main(int argc, char **argv)
{
  matrix tmp,tmp2;
  int test;

  test = (argc>1);

  if (test) {
    printf("\n1*0 = \n");
    matmult(m1,m0,tmp);
    print(tmp);
    
    printf("\n1*1 = \n");
    matmult(m1,m1,tmp);
    print(tmp);
    
    printf("\nv*0 = \n");
    matmult(v,m0,tmp);
    print(tmp);
    
    printf("\nv*1 = \n");
    matmult(v,m1,tmp);
    print(tmp);
    
    printf("\n1*v = \n");
    matmult(m1,v,tmp);
    print(tmp);

    printf("\nrotz*roty*rotx = \n");
    matmult(rotz,roty,tmp);
    matmult(tmp,rotx,tmp2);
    printaff(tmp2);
  }

  matrix test1, test2;

  matmult(aff, xlat, test1);
  printaff(test1);
  printf("\n");
  matmult(aff, rotz, test1);
  printaff(test1);
  printf("\n");
  matmult(aff, roty, test1);
  printaff(test1);
  printf("\n");
  matmult(aff, rotx, test1);
  printaff(test1);
  printf("\n");
  matmult(aff, scale, test1);
  printaff(test1);
  printf("\n");

  matrix camrot,icamrot,ixform;

  mat_getrot(cam, camrot);
  mattranspose(camrot, icamrot);

  printf("\nicamt = \n");
  printmat(icamt);
  printf("\nicamrot = \n");
  printmat(icamrot);

  matmult(icamrot,icamt,ixform);
  printf("\nixform = \n");
  printmat(ixform);

  matmult(ixform,point,tmp);
  printmat(tmp);
}