/*-*- 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/build/lib/case.h"
#include "tool/plinko/lib/char.h"
#include "tool/plinko/lib/plinko.h"
#include "tool/plinko/lib/print.h"
#include "tool/plinko/lib/tree.h"

int PrintDot(int fd) {
  return PrintChar(fd, L'.');
}

int PrintSpace(int fd) {
  return PrintChar(fd, L' ');
}

void PrintNewline(int fd) {
  PrintChar(fd, L'\n');
}

int PrintIndent(int fd, int n) {
  int i;
  for (i = 0; n > 0; --n) {
    i += PrintSpace(fd);
  }
  return i;
}

int PrintZeroes(int fd, int n) {
  int i;
  for (i = 0; n > 0; --n) {
    i += PrintChar(fd, L'0');
  }
  return i;
}

int PrintDepth(int fd, int n) {
  int i, j;
  for (j = i = 0; n > 0; --n, ++j) {
    if (j < ARRAYLEN(g_depths)) {
      i += PrintChar(fd, g_depths[j][0]);
      i += PrintChar(fd, g_depths[j][1]);
      i += PrintChar(fd, g_depths[j][2]);
    }
  }
  return i;
}

int PrintListDot(int fd, int x) {
  int n;
  n = PrintSpace(fd);
  n += PrintDot(fd);
  n += PrintSpace(fd);
  n += Print(fd, x);
  return n;
}

bool ShouldForceDot(int x) {
  return Cdr(x) < 0 && (Car(Cdr(x)) == kClosure || Car(Cdr(x)) == kLambda ||
                        Car(Cdr(x)) == kMacro);
}

bool ShouldConcealClosure(int x) {
  return !logc &&
         (Car(x) == kClosure /*  && Cdr(x) < 0 && Car(Cdr(x)) < 0 && */
          /* Car(Car(Cdr(x))) == kLambda && CountAtoms(x, 256, 0) >= 256 */);
}

int PrintList(int fd, int x) {
  int n = 0;
  DCHECK_LE(x, 0);
  if (x < cx) {
    n += PrintChar(fd, L'!');
    n += PrintInt(fd, x, 0, 0, 0, 10, true);
  } else {
    if (ShouldConcealClosure(x)) {
      x = Car(Cdr(x));
    }
    n += PrintChar(fd, L'(');
    if (x < 0) {
      n += Print(fd, Car(x));
      if (ShouldForceDot(x)) {
        n += PrintListDot(fd, Cdr(x));
      } else {
        while ((x = Cdr(x))) {
          if (x < 0) {
            n += PrintSpace(fd);
            n += Print(fd, Car(x));
          } else {
            n += PrintListDot(fd, x);
            break;
          }
        }
      }
    }
    n += PrintChar(fd, L')');
  }
  return n;
}

bool HasWeirdCharacters(int x) {
  int c;
  dword e;
  do {
    e = Get(x);
    c = LO(e);
    x = HI(e);
    if (c == L'`' || c == L'"' || c == L'\'' || IsSpace(c) || IsParen(c) ||
        IsSpace(c) || IsLower(c) || IsC0(c)) {
      return true;
    }
  } while (x != TERM);
  return false;
}

int PrintString(int fd, int x) {
  dword e;
  int c, u, n;
  n = PrintChar(fd, L'"');
  do {
    e = Get(x);
    c = LO(e);
    x = HI(e);
    DCHECK_GT(c, 0);
    DCHECK_LT(c, TERM);
    if (c < 0200) {
      u = c;
      c = L'\\';
      switch (c) {
        CASE(L'\a', c |= L'a' << 010);
        CASE(L'\b', c |= L'b' << 010);
        CASE(00033, c |= L'e' << 010);
        CASE(L'\f', c |= L'f' << 010);
        CASE(L'\n', c |= L'n' << 010);
        CASE(L'\r', c |= L'r' << 010);
        CASE(L'\t', c |= L't' << 010);
        CASE(L'\v', c |= L'v' << 010);
        CASE(L'\\', c |= L'\\' << 010);
        CASE(L'"', c |= L'"' << 010);
        default:
          if (32 <= c && c < 127) {
            c = u;
          } else {
            c |= ((L'0' + c / 8 / 8 % 8) << 010 | (L'0' + c / 8 % 8) << 020 |
                  (L'0' + c % 8) << 030);
          }
          break;
      }
      do {
        n += PrintChar(fd, c & 255);
      } while ((c >>= 010));
    } else {
      n += PrintChar(fd, c);
    }
  } while (x != TERM);
  n += PrintChar(fd, L'"');
  return n;
}

int PrintAtom(int fd, int x) {
  dword e;
  int c, n = 0;
  DCHECK_GE(x, 0);
  if (x == 0) {
    if (symbolism && !literally) {
      n += PrintChar(fd, L'â¥');
    } else {
      n += PrintChar(fd, L'N');
      n += PrintChar(fd, L'I');
      n += PrintChar(fd, L'L');
    }
  } else if (x == 1) {
    if (symbolism && !literally) {
      n += PrintChar(fd, L'â¤');
    } else {
      n += PrintChar(fd, L'T');
    }
  } else if (x >= TERM) {
    n += PrintChar(fd, L'!');
    n += PrintInt(fd, x, 0, 0, 0, 10, true);
  } else if (symbolism && (c = Symbolize(x)) != -1) {
    n += PrintChar(fd, c);
  } else {
    if (!literally && HasWeirdCharacters(x)) {
      n += PrintString(fd, x);
    } else {
      do {
        e = Get(x);
        c = LO(e);
        n += PrintChar(fd, c);
      } while ((x = HI(e)) != TERM);
    }
  }
  return n;
}

int EnterPrint(int x) {
#if 0
  int i;
  if (x < 0) {
    DCHECK_GE(pdp, 0);
    DCHECK_LT(pdp, ARRAYLEN(g_print));
    if (x < 0) {
      for (i = 0; i < pdp; ++i) {
        if (x == g_print[i]) {
          x = kCycle;
          break;
        }
      }
    }
    g_print[pdp++] = x;
    if (pdp == ARRAYLEN(g_print)) {
      x = kTrench;
    }
  }
#endif
  return x;
}

void LeavePrint(int x) {
#if 0
  if (x < 0) {
    --pdp;
  }
#endif
}

int Print(int fd, int x) {
  int n;
  GetName(&x);
  x = EnterPrint(x);
  if (1. / x < 0) {
    n = PrintList(fd, x);
  } else {
    n = PrintAtom(fd, x);
  }
  LeavePrint(x);
  return n;
}
