/*
** Astrolog (Version 4.00) File: intrpret.c
**
** IMPORTANT NOTICE: the graphics database and chart display routines
** used in this program are Copyright (C) 1991-1993 by Walter D. Pullen
** (cruiser1@stein.u.washington.edu). Permission is granted to freely
** use and distribute these routines provided one doesn't sell,
** restrict, or profit from them in any way. Modification is allowed
** provided these notices remain with any altered or edited versions of
** the program.
**
** The main planetary calculation routines used in this program have
** been Copyrighted and the core of this program is basically a
** conversion to C of the routines created by James Neely as listed in
** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
** available from Matrix Software. The copyright gives us permission to
** use the routines for personal use but not to sell them or profit from
** them in any way.
**
** The PostScript code within the core graphics routines are programmed
** and Copyright (C) 1992-1993 by Brian D. Willoughby
** (brianw@sounds.wa.com). Conditions are identical to those above.
**
** The extended accurate ephemeris databases and formulas are from the
** calculation routines in the program "Placalc" and are programmed and
** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl
** (alois@azur.ch). The use of that source code is subject to
** regulations made by Astrodienst Zurich, and the code is not in the
** public domain. This copyright notice must not be changed or removed
** by any user of this program.
**
** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991.
** X Window graphics initially programmed 10/23-29/1991.
** PostScript graphics initially programmed 11/29-30/1992.
** Last code change made 12/31/1993.
*/

#include "astrolog.h"

#ifdef INTERPRET

/*
******************************************************************************
** Interpretation Subroutines.
******************************************************************************
*/

/* This function is used by the interpretation routines to print out lines  */
/* of text with newlines inserted just before the end of screen is reached. */

void FieldWord(string)
char *string;
{
  static char line[STRING*4];
  static int cursor = 0;
  int i, j;

  /* Hack: Dump buffer if function called with a null string. */

  if (*string == 0) {
    line[cursor] = 0;
    fprintf(S, "%s\n", line);
    cursor = 0;
    return;
  }
  if (cursor)
    line[cursor++] = ' ';
  for (i = 0; (line[cursor] = string[i]); i++, cursor++)
    ;

  /* When buffer overflows 80 columns, print out one line and start over. */

  while (cursor >= screenwidth-1) {
    for (i = screenwidth-1; line[i] != ' '; i--)
      ;
    line[i] = 0;
    fprintf(S, "%s\n", line);
    line[0] = line[1] = ' ';
    for (j = 2; (line[j] = line[i+j-1]) != 0; j++)
      ;
    cursor -= (i-1);
  }
}


/* Display a general interpretation of what each sign of the zodiac, house, */
/* and planet or object means. This is called to do the -I0 switch table.   */

void InterpretGeneral()
{
  char string[STRING];
  int i;

  fprintf(S,
    "Signs of the zodiac represent psychological characteristics.\n\n");
  for (i = 1; i <= SIGNS; i++) {
    AnsiColor(signansi(i));
    sprintf(string, "%s is", signname[i]); FieldWord(string);
    sprintf(string, "%s, and", description[i]); FieldWord(string);
    sprintf(string, "%s.", desire[i]); FieldWord(string);
    FieldWord("");
  }
  AnsiColor(DEFAULT);
  fprintf(S, "\nHouses represent different areas within one's life.\n\n");
  for (i = 1; i <= SIGNS; i++) {
    AnsiColor(signansi(i));
    sprintf(string, "The %d%s House is the area of life dealing with",
      i, post[i]); FieldWord(string);
    sprintf(string, "%s.", lifearea[i]); FieldWord(string);
    FieldWord("");
  }
  AnsiColor(DEFAULT);
  fprintf(S, "\nPlanets represent various parts of one's mind or self.\n\n");
  for (i = 1; i <= OBJECTI; i++) {
    if (ignore[i] || IsCusp(i))
      continue;
    AnsiColor(objectansi[i]);
    sprintf(string, "%s%s%s%s represents one's",
      i < 3 || (i >= THINGS && i <= OBJECTS) ? "The " : "",
      i == _NOD ? "North " : (i == _FOR ? "Part of " : ""), objectname[i],
      i == 13 ? " Athena" : ""); FieldWord(string);
    sprintf(string, "%s.", mindpart[i]); FieldWord(string);
    FieldWord("");
  }
  AnsiColor(DEFAULT);
}


/* Display a general interpretation of what each aspect type means. This */
/* is called when printing the interpretation table in the -I0 switch.   */

void InterpretAspectGeneral()
{
  char string[STRING];
  int i;

  fprintf(S, "\nAspects are different relationships between planets.\n\n");
  for (i = 1; i <= MIN(aspects, ASPECTI); i++) {
    AnsiColor(aspectansi[i]);
    sprintf(string, "When planets are %s, one", aspectname[i]);
    FieldWord(string); sprintf(string, interact[i], ""); FieldWord(string);
    FieldWord("another.");
    if (therefore[i][0]) {
      sprintf(string, "%s.", therefore[i]); FieldWord(string);
    }
    FieldWord("");
  }
  return;
}


/* Print the interpretation of each planet in sign and house, as specified */
/* with the -I switch. This is basically array accessing combining the     */
/* meanings of each planet, sign, and house, and a couple of other things. */

void InterpretLocation()
{
  char string[STRING], c;
  int i, j;

  printl();
  for (i = 1; i <= OBJECTI; i++) {
    if (ignore[i] || IsCusp(i))
      continue;
    AnsiColor(objectansi[i]);
    j = ZTOS(planet[i]); c = Dignify(i, j);
    sprintf(string, "%s%s%s%s in %s", ret[i] < 0.0 ? "Retrograde " : "",
      i == _NOD ? "North " : (i == _FOR ? "Part of " : ""), objectname[i],
      i == 13 ? " Athena" : "", signname[j]);
    FieldWord(string);
    sprintf(string, "and %d%s House:", inhouse[i], post[inhouse[i]]);
    FieldWord(string);
    FieldWord("This person's"); FieldWord(mindpart[i]); FieldWord("is");
    if (((int) planet[i]) % 30 < 10)
      FieldWord("very");
    sprintf(string, "%s, and", description[j]); FieldWord(string);
    sprintf(string, "%s.", desire[j]); FieldWord(string);
    FieldWord("Most often this manifests");
    if (ret[i] < 0.0 && i != _NOD)
      FieldWord("in an independent, backward, introverted manner, and");
    FieldWord("in the area of life dealing with");
    sprintf(string, "%s.", lifearea[inhouse[i]]); FieldWord(string);

    /* Extra information if planet is in its ruling, falling, etc, sign. */

    if (c == 'R')
      FieldWord("This is a major aspect of the person's psyche!");
    else if (c == 'F')
      FieldWord("(This bit plays only a minor part in the person's psyche.)");
    else if (c == 'e')
      FieldWord("It is easy for them to express this part of themself.");
    else if (c == 'd')
      FieldWord("It is difficult for them to express this part of themself.");
    FieldWord("");
  }
}


/* Print an interpretation for a particular aspect in effect in a chart. */
/* This is called from the InterpretGrid and ChartAspect routines.       */

void InterpretAspect(x, y)
int x, y;
{
  char string[STRING*2];
  int n;

  n = grid->n[x][y];
  if (n < 1 || n > ASPECTI ||
    IsCusp(x) || IsCusp(y) || x > OBJECTI || y > OBJECTI)
    return;
  AnsiColor(aspectansi[n]);
  sprintf(string, "%s %s %s: This person's", objectname[x],
    aspectname[n], objectname[y]);
  FieldWord(string); FieldWord(mindpart[x]);
  sprintf(string, interact[n],
    modifier[MIN(abs(grid->v[x][y])/150, 2)][n-1]);
  FieldWord(string);
  sprintf(string, "their %s.", mindpart[y]); FieldWord(string);
  if (therefore[n][0]) {
    sprintf(string, "%s.", therefore[n]); FieldWord(string);
  }
  FieldWord("");
}


/* Print the interpretation of each aspect in the aspect grid, as specified */
/* with the -g -I switch. Again, this is done by basically array accessing  */
/* of the meanings of the two planets in aspect and of the aspect itself.   */

void InterpretGrid()
{
  int i, j;

  for (i = 1; i < OBJECTI; i++) if (!ignore[i] && !IsCusp(i))
    for (j = i+1; j <= OBJECTI; j++) if (!ignore[j] && !IsCusp(i))
      InterpretAspect(i, j);
}


/* Print an interpretation for a particular midpoint in effect in a chart. */
/* This is called from the ChartMidpoint routine.                          */

void InterpretMidpoint(x, y)
int x, y;
{
  char string[STRING];
  int n, i;

  if (IsCusp(x) || IsCusp(y) || x > OBJECTI || y > OBJECTI)
    return;
  n = grid->n[y][x];
  AnsiColor(signansi(n));
  sprintf(string, "%s midpoint %s in %s: The merging of the person's",
    objectname[x], objectname[y], signname[n]);
  FieldWord(string); FieldWord(mindpart[x]);
  FieldWord("with their"); FieldWord(mindpart[y]);
  FieldWord("is");
  if (grid->v[y][x]/60 < 10)
    FieldWord("very");
  sprintf(string, "%s, and", description[n]); FieldWord(string);
  sprintf(string, "%s.", desire[n]); FieldWord(string);
  FieldWord("Most often this manifests in");
  if (ret[x]+ret[y] < 0.0 && x != _NOD && y != _NOD)
    FieldWord("an independent, backward, introverted manner, and");
  FieldWord("the area of life dealing with");
  i = HousePlaceIn(STOZ(n) + (real)grid->v[y][x]/60.0);
  sprintf(string, "%s.", lifearea[i]); FieldWord(string);
  FieldWord("");
}


/* This is a subprocedure of DisplayInDay(). Print the interpretation for */
/* a particular instance of the various exciting events that can happen.  */ 

void InterpretInDay(source, aspect, dest)
int source, aspect, dest;
{
  char string[STRING];

  if (source > OBJECTI || dest > OBJECTI)
    return;

  /* Interpret object changing direction. */

  if (aspect == _DIR) {
    AnsiColor(objectansi[source]);
    FieldWord("Energy representing"); FieldWord(mindpart[source]);
    FieldWord("will tend to manifest in");
    FieldWord(dest ? "an independent, backward, introverted" :
      "the standard, direct, open");
    FieldWord("manner.");
    FieldWord("");

  /* Interpret object entering new sign. */

  } else if (aspect == _SIG) {
    AnsiColor(objectansi[source]);
    FieldWord("Energy representing"); FieldWord(mindpart[source]);
    sprintf(string, "will be %s,", description[dest]);
    FieldWord(string);
    sprintf(string, "and it %s.", desire[dest]); FieldWord(string);
    FieldWord("");

  /* Interpret aspect between transiting planets. */

  } else if (aspect > 0 && aspect <= ASPECTI) {
    AnsiColor(aspectansi[aspect]);
    FieldWord("Energy representing"); FieldWord(mindpart[source]);
    sprintf(string, interact[aspect], modifier[1][aspect-1]);
    FieldWord(string);
    sprintf(string, "energies of %s.", mindpart[dest]); FieldWord(string);
    if (therefore[aspect][0]) {
      if (aspect > _CON) {
        sprintf(string, "%s.", therefore[aspect]); FieldWord(string);
      } else
        FieldWord("They will affect each other prominently.");
    }
    FieldWord("");
  }
}


/* This is a subprocedure of DisplayTransit(). Print the interpretation for */
/* a particular transit of a planet to a natal object of a chart.           */ 

void InterpretTransit(source, aspect, dest)
int source, aspect, dest;
{
  char string[STRING];

  if (source <= OBJECTS && dest <= OBJECTS && aspect <= ASPECTI) {
    AnsiColor(aspectansi[aspect]);
    FieldWord("Energy representing"); FieldWord(mindpart[source]);
    sprintf(string, interact[aspect], modifier[1][aspect-1]);
    FieldWord(string);
    if (source != dest) {
      sprintf(string, "the person's %s.", mindpart[dest]); FieldWord(string);
    } else
      FieldWord("the same aspect inside the person's makeup.");
    if (therefore[aspect][0]) {
      if (aspect > _CON) {
        sprintf(string, "%s.", therefore[aspect]); FieldWord(string);
      } else
        FieldWord("This part of their psyche will be strongly influenced.");
    }
    FieldWord("");
  }
}


/* Print the interpretation of one person's planet in another's sign and    */
/* house, in a synastry chart as specified with the -r switch combined with */
/* -I. This is very similar to the interpretation of the standard -v chart  */
/* in InterpretLocation(), but we treat the chart as a relationship here.   */

void InterpretSynastry()
{
  char string[STRING], c;
  int i, j;

  printl();
  for (i = 1; i <= OBJECTI; i++) {
    if (ignore[i] || IsCusp(i))
      continue;
    AnsiColor(objectansi[i]);
    j = ZTOS(planet[i]); c = Dignify(i, j);
    sprintf(string, "%s%s%s%s in %s,", ret[i] < 0.0 ? "Retrograde " : "",
      i == _NOD ? "North " : (i == _FOR ? "Part of " : ""), objectname[i],
      i == 13 ? " Athena" : "", signname[j]);
    FieldWord(string);
    sprintf(string, "in their %d%s House:", inhouse[i], post[inhouse[i]]);
    FieldWord(string);
    FieldWord("Person1's"); FieldWord(mindpart[i]); FieldWord("is");
    if (((int) planet[i]) % 30 < 10)
      FieldWord("very");
    sprintf(string, "%s, and", description[j]); FieldWord(string);
    sprintf(string, "%s.", desire[j]); FieldWord(string);
    FieldWord("This");
    if (ret[i] < 0.0 && i != _NOD)
      FieldWord(
        "manifests in an independent, backward, introverted manner, and");
    FieldWord("affects Person2 in the area of life dealing with");
    sprintf(string, "%s.", lifearea[inhouse[i]]); FieldWord(string);

    /* Extra information if planet is in its ruling, falling, etc, sign. */

    if (c == 'R')
      FieldWord("This is a major aspect of Person1's psyche!");
    else if (c == 'F')
      FieldWord("(This bit plays only a minor part in Person1's psyche.)");
    else if (c == 'e')
      FieldWord("Person2 is affected harmoniously in this way.");
    else if (c == 'd')
      FieldWord("Person2 is affected discordantly in this way.");
    FieldWord("");
  }
}


/* Print an interpretation for a particular aspect in effect in a comparison */
/* relationship chart. This is called from the InterpretGridRelation and     */
/* the DisplayAspectRelation routines.                                       */

void InterpretAspectRelation(x, y)
{
  char string[STRING*2];
  int n;

  n = grid->n[y][x];
  if (n < 1 || n > ASPECTI ||
    IsCusp(x) || IsCusp(y) || x > OBJECTI || y > OBJECTI)
    return;
  AnsiColor(aspectansi[n]);
  sprintf(string, "%s %s %s: Person1's", objectname[x],
    aspectname[n], objectname[y]);
  FieldWord(string); FieldWord(mindpart[x]);
  sprintf(string, interact[n],
    modifier[MIN(abs(grid->v[y][x])/150, 2)][n-1]);
  FieldWord(string);
  sprintf(string, "Person2's %s.", mindpart[y]); FieldWord(string);
  if (therefore[n][0]) {
    if (n != 1) {
      sprintf(string, "%s.", therefore[n]); FieldWord(string);
    } else
      FieldWord("These parts affect each other prominently.");
  }
  FieldWord("");
}


/* Print the interpretation of each aspect in the relationship aspect grid, */
/* as specified with the -r0 -g -I switch combination.                      */

void InterpretGridRelation()
{
  int i, j;

  for (i = 1; i <= OBJECTI; i++) if (!ignore[i])
    for (j = 1; j <= OBJECTI; j++) if (!ignore[j])
      InterpretAspectRelation(i, j);
}


/* Print the interpretation of a midpoint in the relationship grid, as */
/* specified with the -r0 -m -I switch combination.                    */

void InterpretMidpointRelation(x, y)
{
  char string[STRING];
  int n;

  if (IsCusp(x) || IsCusp(y) || x > OBJECTI || y > OBJECTI)
    return;
  n = grid->n[y][x];
  AnsiColor(signansi(n));
  sprintf(string, "%s midpoint %s in %s: The merging of person1's",
    objectname[x], objectname[y], signname[n]);
  FieldWord(string); FieldWord(mindpart[x]);
  FieldWord("with person2's"); FieldWord(mindpart[y]);
  FieldWord("is");
  if (grid->v[y][x]/60 < 10)
    FieldWord("very");
  sprintf(string, "%s, and", description[n]); FieldWord(string);
  sprintf(string, "%s.", desire[n]); FieldWord(string);
  if (ret1[x]+ret2[y] < 0.0 && x != _NOD && y != _NOD) {
    FieldWord("Most often this manifests in");
    FieldWord("an independent, backward, introverted manner.");
  }
  FieldWord("");
}
#endif /* INTERPRET */


/*
******************************************************************************
** Chart Influence Subroutines.
******************************************************************************
*/

/* This is a subprocedure of ChartInfluence(). Based on the values in the */
/* array parameter 'value', store numbers in array 'rank' reflecting the  */
/* relative order, e.g. value[x] 2nd greatest array value -> rank[x] = 2. */

void SortRank(value, rank, size)
real *value;
int *rank;
{
  int h, i, j, k;

  value[0] = -1.0;
  for (i = 1; i <= size; i++)
    rank[i] = -1;
  for (h = 1, i = 0; h <= size; h++) {
    if (size != SIGNS && (ignore[h] || !IsThing(h)))
      continue;
    i++;
    k = 0;
    for (j = 1; j <= size; j++) {
      if (size != SIGNS && (ignore[j] || !IsThing(j)))
        continue;
      if (value[j] > value[k] && rank[j] < 0)
        k = j;
    }

        /* 'k' is the current position of the 'i'th place planet. */

    rank[k] = i;
  }
}


/* Print out a list of power values and relative rankings, based on the */
/* placements of the planets, and their aspects in the aspect grid, as  */
/* specified with the -j "find influences" switch.                      */

void ChartInfluence()
{
  real power[BASE+1], power1[BASE+1], power2[BASE+1],
    total, total1, total2, x;
  int rank[BASE+1], rank1[BASE+1], rank2[BASE+1], i, j, k, l;
  char c;

  for (i = 1; i <= BASE; i++)
    power1[i] = power2[i] = 0.0;
  total = total1 = total2 = 0.0;

  /* First, for each object, find its power based on its placement alone. */

  for (i = 1; i <= BASE; i++) if (!ignore[i] && IsThing(i)) {
    j = ZTOS(planet[i]);
    power1[i] += objectinf[i];            /* Influence of planet itself. */
    power1[i] += houseinf[inhouse[i]];    /* Influence of house it's in. */
    c = Dignify(i, j);
    switch (c) {
    case 'R': x = objectinf[BASE+1]; break;  /* Planets in signs they rule / */
    case 'e': x = objectinf[BASE+2]; break;  /* exalted in have influence.   */
    default:  x = 0.0;
    }
    c = Dignify(i, inhouse[i]);
    switch (c) {
    case 'R': x += houseinf[SIGNS+1]; break; /* Planet in house aligned with */
    case 'e': x += houseinf[SIGNS+2]; break; /* sign ruled has influence.    */
    default: ;
    }
    power1[i] += x;
    if (i != rules[j])                         /* The planet ruling the sign */
      power1[rules[j]] += objectinf[i]/2.0;    /* and the house that the     */
    if (i != (j = rules[inhouse[i]]))          /* current planet is in, gets */
      power1[j] += objectinf[i]/2.0;           /* extra influence.           */
  }
  for (i = 1; i <= SIGNS; i++) {        /* Various planets get influence */
    j = ZTOS(house[i]);                 /* if house cusps fall in signs  */
    power1[rules[j]] += houseinf[i];    /* they rule.                    */
  }

  /* Second, for each object, find its power based on aspects it makes. */

  CreateGrid(FALSE);
  for (j = 1; j <= BASE; j++) if (!ignore[j] && IsThing(j))
    for (i = 1; i <= BASE; i++) if (!ignore[i] && IsThing(i) && i != j) {
      k = grid->n[MIN(i, j)][MAX(i, j)];
      if (k) {
        l = grid->v[MIN(i, j)][MAX(i, j)];
        power2[j] += aspectinf[k]*objectinf[i]*
          (1.0-dabs((real)l)/60.0/aspectorb[k]);
      }
    }

  /* Calculate total power of each planet. */

  for (i = 1; i <= BASE; i++) if (!ignore[i] && IsThing(i)) {
    power[i] = power1[i]+power2[i]; total1 += power1[i]; total2 += power2[i];
  }
  total = total1+total2;

  /* Finally, determine ranks of the arrays, then print everything out. */

  SortRank(power1, rank1, BASE); SortRank(power2, rank2, BASE);
  SortRank(power, rank, BASE);
  fprintf(S, "  Planet:    Position      Aspects    Total Rank  Percent\n");
  for (i = 1; i <= BASE; i++) if (!ignore[i] && IsThing(i)) {
    AnsiColor(objectansi[i]);
    fprintf(S, "%8.8s: ", objectname[i]);
    fprintf(S, "%6.1f (%2d) +%6.1f (%2d) =%7.1f (%2d) /%6.1f%%\n",
      power1[i], rank1[i], power2[i], rank2[i],
      power[i], rank[i], power[i]/total*100.0);
  }
  AnsiColor(DEFAULT);
  fprintf(S, "   Total: %6.1f      +%6.1f      =%7.1f      / 100.0%%\n",
    total1, total2, total);

  /* Now, print out a list of power values and relative rankings, based on  */
  /* the power of each sign of the zodiac, as indicated by the placement of */
  /* the planets above, in the chart, as specified with the -j0 switch.     */

  if (!(exdisplay & DASHj0))
    return;
  for (i = 1; i <= SIGNS; i++)
    power1[i] = 0.0;

  /* For each sign, determine its power based on the power of the object. */

  for (i = 1; i <= BASE; i++) if (!ignore[i] && !IsCusp(i)) {
    power1[ZTOS(planet[i])] += objectinf[i] / 2.0;
    power1[inhouse[i]]  += objectinf[i] / 4.0;
    power1[ruler1[i]]   += objectinf[i] / 3.0;
    if (ruler2[i])
      power1[ruler2[i]] += objectinf[i] / 4.0;
  }
  if (!ignore[_NOD]) {
    power1[Mod12((int)planet[_NOD]/30+7)] += power[_NOD] / 2.0; /* South */
    power1[Mod12(inhouse[_NOD]+6)]        += power[_NOD] / 4.0; /* Node. */
  }
  total1 = 0.0;
  for (i = 1; i <= SIGNS; i++)
    total1 += power1[i];

  /* Again, determine ranks in the array, and print everything out. */

  SortRank(power1, rank1, SIGNS);
  fprintf(S,
    "\n       Sign:  Power Rank  Percent  - Element  Power  Percent\n");
  for (i = 1; i <= SIGNS; i++) {
    AnsiColor(signansi(i));
    fprintf(S, "%11.11s: ", signname[i]);
    fprintf(S, "%6.1f (%2d) /%6.1f%%",
      power1[i], rank1[i], power1[i]/total1*100.0);
    if (i <= 4) {
      fprintf(S, "  -%7.7s:", element[i-1]);
      total2 = 0.0;
      for (j = 1; j < SIGNS; j += 4)
        total2 += power1[i+j-1];
      fprintf(S, "%7.1f /%6.1f%%", total2, total2/total1*100.0);
    }
    printl();
  }
  AnsiColor(DEFAULT);
  fprintf(S, "      Total:%7.1f      / 100.0%%\n", total1);
}

/* intrpret.c */
