/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-â
âvi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8                                :viâ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¡
â Copyright 2022 Justine Alexandra Roberts Tunney                              â
â                                                                              â
â Permission to use, copy, modify, and/or distribute this software for         â
â any purpose with or without fee is hereby granted, provided that the         â
â above copyright notice and this permission notice appear in all copies.      â
â                                                                              â
â THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                â
â WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                â
â WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             â
â AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         â
â DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        â
â PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               â
â TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             â
â PERFORMANCE OF THIS SOFTWARE.                                                â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ*/
#include "tool/plinko/lib/plinko.h"
#include "tool/plinko/lib/printf.h"
#include "tool/plinko/lib/trace.h"

void EnableTracing(void) {
  eval = EvalTrace;
  bind_ = BindTrace;
  evlis = EvlisTrace;
  pairlis = PairlisTrace;
  recurse = RecurseTrace;
  expand = mtrace ? ExpandTrace : Expand;
  kTail[0] = DispatchTailTrace;
  kTail[4] = DispatchTailTrace;
  kTail[5] = DispatchTailGcTrace;
  kTail[6] = DispatchTailTrace;
  kTail[7] = DispatchTailTmcGcTrace;
}

static inline int ShortenExpression(int e) {
  return e;
}

static bool AlwaysTrace(int e) {
  return true;
}
static bool LimitedTrace(int e) {
  return e < 0;
}

static relegated struct T TailTracer(dword ea, dword tm, dword r, dword p1,
                                     dword p2, TailFn *f) {
  if (depth < ARRAYLEN(g_depths)) {
    Fprintf(2, "%Jâ âTail[%p @ %d] p1=%S p2=%S Î´ %'Rns%n",
            ShortenExpression(LO(ea)), LO(ea), LO(p1), LO(p2));
  }
  return f(ea, tm, r, p1, p2);
}

relegated struct T DispatchTailTrace(dword ea, dword tm, dword r, dword p1,
                                     dword p2) {
  return TailTracer(ea, tm, r, p1, p2, DispatchTail);
}

relegated struct T DispatchTailGcTrace(dword ea, dword tm, dword r, dword p1,
                                       dword p2) {
  return TailTracer(ea, tm, r, p1, p2, DispatchTailGc);
}

relegated struct T DispatchTailTmcGcTrace(dword ea, dword tm, dword r, dword p1,
                                          dword p2) {
  int x;
  if (depth < ARRAYLEN(g_depths)) {
    x = LO(tm);
    x = x < 0 ? x : ~x;
    Fprintf(2, "%Jââ¿Cons[%p @ %d] Î´ %'Rns%n", x, x);
  }
  return TailTracer(ea, tm, r, p1, p2, DispatchTailTmcGc);
}

static relegated int Trace(int e, int a, EvalFn *f, bool p(int), const char *s,
                           const unsigned short c[5]) {
  int d, r, rp, S = sp;
  DCHECK_GE(depth, -1);
  d = depth;
  depth = MAX(d, 0);
  if (depth < ARRAYLEN(g_depths)) {
    if (p(e)) {
      if (loga) {
        Fprintf(2, "%I%c%c%s[e=%S @ %d; a=%S @ %d] Î´ %'Rns%n", c[0], c[1], s,
                ShortenExpression(e), e, a, a);
      } else {
        Fprintf(2, "%I%c%c%s[%S @ %d] Î´ %'Rns%n", c[0], c[1], s,
                ShortenExpression(e), e);
      }
    }
    g_depths[depth][0] = c[2];
    g_depths[depth][1] = L' ';
    g_depths[depth][2] = L' ';
  }
  ++depth;
  r = f(e, a);
  --depth;
  if (depth < ARRAYLEN(g_depths) && p(e)) {
    rp = r != e ? r : kUnchanged;
    if (sp == S) {
      Fprintf(2, "%I%c%c%p @ %d %'Rns%n", c[3], c[4], rp, r);
    } else {
      Fprintf(2, "%I%c%c%p @ %d %'Rns [STACK SKEW S=%d vs. sp=%d]%n", c[3],
              c[4], rp, r, S, sp);
    }
  }
  depth = d;
  return r;
}

relegated int RecurseTrace(dword ea, dword p1, dword p2) {
  int r, d, S = sp;
  const char *s = "Recurse";
  const unsigned short c[5] = u"âââââ";
  if (depth < ARRAYLEN(g_depths)) {
    if (loga) {
      Fprintf(2,
              "%I%c%c%s[LO(ea)=%S @ %d; HI(ea)=%S @ %d] p1=%S p2=%S Î´ %'Rns%n",
              c[0], c[1], s, ShortenExpression(LO(ea)), LO(ea), HI(ea), HI(ea),
              LO(p1), LO(p2));
    } else {
      Fprintf(2, "%I%c%c%s[%S @ %d] p1=%S p2=%S Î´ %'Rns%n", c[0], c[1], s,
              ShortenExpression(LO(ea)), LO(ea), LO(p1), LO(p2));
    }
    g_depths[depth][0] = c[2];
    g_depths[depth][1] = L' ';
    g_depths[depth][2] = L' ';
  }
  ++depth;
  r = Recurse(ea, p1, p2);
  --depth;
  if (depth < ARRAYLEN(g_depths)) {
    if (r != LO(ea)) {
      Fprintf(2, "%I%c%c%p @ %d Î´ %'Rns%n", c[3], c[4], r, r);
    } else {
      Fprintf(2, "%I%c%câ¿/â Î´ %'Rns%n", c[3], c[4]);
    }
  }
  return r;
}

relegated int EvlisTrace(int e, int a, dword p1, dword p2) {
  int r, d, S = sp;
  const char *s = "Evlis";
  const unsigned short c[5] = u"âââââ";
  DCHECK_GE(depth, -1);
  d = depth;
  depth = MAX(d, 0);
  if (depth < ARRAYLEN(g_depths)) {
    if (loga) {
      Fprintf(2, "%I%c%c%s[e=%S @ %d; a=%S @ %d] Î´ %'Rns%n", c[0], c[1], s,
              ShortenExpression(e), e, a, a);
    } else {
      Fprintf(2, "%I%c%c%s[%S @ %d] Î´ %'Rns%n", c[0], c[1], s,
              ShortenExpression(e), e);
    }
    g_depths[depth][0] = c[2];
    g_depths[depth][1] = L' ';
    g_depths[depth][2] = L' ';
  }
  ++depth;
  r = Evlis(e, a, p1, p2);
  --depth;
  if (depth < ARRAYLEN(g_depths)) {
    if (r != e) {
      Fprintf(2, "%I%c%c%p @ %d Î´ %'Rns%n", c[3], c[4], r, r);
    } else {
      Fprintf(2, "%I%c%câ¿/â Î´ %'Rns%n", c[3], c[4]);
    }
  }
  depth = d;
  return r;
}

relegated int Trace3(int x, int y, int a, PairFn *f, const char *s,
                     const unsigned short c[5]) {
  int r, d, S = sp;
  if (depth < ARRAYLEN(g_depths)) {
    if (loga) {
      Fprintf(2, "%I%c%c%s[x=%S; y=%S; a=%S] Î´ %'Rns%n", c[0], c[1], s,
              ShortenExpression(x), ShortenExpression(y), a);
    } else {
      Fprintf(2, "%I%c%c%s[x=%S; y=%S] Î´ %'Rns%n", c[0], c[1], s,
              ShortenExpression(x), ShortenExpression(y));
    }
    g_depths[depth][0] = c[2];
    g_depths[depth][1] = L' ';
    g_depths[depth][2] = L' ';
  }
  ++depth;
  r = f(x, y, a);
  --depth;
  return r;
}

relegated struct Binding BindTrace(int x, int y, int a, int u, dword p1,
                                   dword p2) {
  int d, S = sp;
  struct Binding r;
  if (depth < ARRAYLEN(g_depths)) {
    if (loga) {
      Fprintf(2, "%I%c%c%s[x=%S; y=%S; a=%S; u=%S] Î´ %'Rns%n", L'â', L'â',
              "Bind", ShortenExpression(x), ShortenExpression(y), a, u);
    } else {
      Fprintf(2, "%I%c%c%s[x=%S; y=%S] Î´ %'Rns%n", L'â', L'â', "Bind",
              ShortenExpression(x), ShortenExpression(y));
    }
    g_depths[depth][0] = L'â';
    g_depths[depth][1] = L' ';
    g_depths[depth][2] = L' ';
  }
  ++depth;
  r = Bind(x, y, a, u, p1, p2);
  --depth;
  return r;
}

int EvalTrace(int e, int a) {
  return Trace(e, a, Eval, AlwaysTrace, "Eval", u"âââââ");
}

int ExpandTrace(int e, int a) {
  return Trace(e, a, Expand, LimitedTrace, "Expand", u"âââââ");
}

int PairlisTrace(int x, int y, int a) {
  return Trace3(x, y, a, Pairlis, "Pairlis", u"âââââ");
}
