/*
 * Copyright (C) 2001-2002 Gary Wong <gtw@gnu.org>
 * Copyright (C) 2002-2021 the AUTHORS
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * $Id: latex.c,v 1.61 2022/02/13 22:30:16 plm Exp $
 */

#include "config.h"

#include <stdio.h>
#include <string.h>

#include "analysis.h"
#include "backgammon.h"
#include "drawboard.h"
#include "export.h"
#include "format.h"
#include <glib/gstdio.h>

static const char *aszLuckTypeLaTeXAbbr[] = { "$--$", "$-$", "", "$+$", "$++$" };

static void
Points1_12(FILE * pf, int y)
{

    int i;

    if (fClockwise)
        for (i = 1; i <= 12; i++)
            fprintf(pf, "\\put(%d,%d){\\makebox(20,10){\\textsl{\\tiny %d}}}\n", 50 + i * 20 + (i > 6) * 20, y, i);
    else
        for (i = 1; i <= 12; i++)
            fprintf(pf, "\\put(%d,%d){\\makebox(20,10){\\textsl{\\tiny %d}}}\n", 330 - i * 20 - (i > 6) * 20, y, i);
}

static void
Points13_24(FILE * pf, int y)
{

    int i;

    if (fClockwise)
        for (i = 13; i <= 24; i++)
            fprintf(pf, "\\put(%d,%d){\\makebox(20,10){\\textsl{\\tiny %d}}}\n", 570 - i * 20 - (i > 18) * 20, y, i);
    else
        for (i = 13; i <= 24; i++)
            fprintf(pf, "\\put(%d,%d){\\makebox(20,10){\\textsl{\\tiny %d}}}\n", i * 20 - 190 + (i > 18) * 20, y, i);
}

static void
LaTeXPrologue(FILE * pf)
{

    fputs("\\documentclass{article}\n"
          "\\usepackage{epic,eepic,textcomp,ucs}\n"
          "\\usepackage[utf8x]{inputenc}\n"
          "\\newcommand{\\board}{\n"
          "\\shade\\path(70,20)(80,120)(90,20)(110,20)(120,120)(130,20)"
          "(150,20)(160,120)\n"
          "(170,20)(70,20)\n"
          "\\path(70,20)(80,120)(90,20)(100,120)(110,20)(120,120)(130,20)"
          "(140,120)(150,20)\n"
          "(160,120)(170,20)(180,120)(190,20)\n"
          "\\shade\\path(90,240)(100,140)(110,240)(130,240)(140,140)(150,240)"
          "(170,240)\n"
          "(180,140)(190,240)(90,240)\n"
          "\\path(70,240)(80,140)(90,240)(100,140)(110,240)(120,140)(130,240)"
          "(140,140)\n"
          "(150,240)(160,140)(170,240)(180,140)(190,240)\n"
          "\\shade\\path(210,20)(220,120)(230,20)(250,20)(260,120)(270,20)"
          "(290,20)(300,120)\n"
          "(310,20)(210,20)\n"
          "\\path(210,20)(220,120)(230,20)(240,120)(250,20)(260,120)(270,20)"
          "(280,120)\n"
          "(290,20)(300,120)(310,20)(320,120)(330,20)\n"
          "\\shade\\path(230,240)(240,140)(250,240)(270,240)(280,140)"
          "(290,240)(310,240)\n"
          "(320,140)(330,240)(230,240)\n"
          "\\path(210,240)(220,140)(230,240)(240,140)(250,240)(260,140)"
          "(270,240)(280,140)\n"
          "(290,240)(300,140)(310,240)(320,140)(330,240)\n"
          "\\path(60,10)(340,10)(340,250)(60,250)(60,10)\n"
          "\\path(70,20)(190,20)(190,240)(70,240)(70,20)\n"
          "\\path(210,20)(330,20)(330,240)(210,240)(210,20)}\n\n", pf);

    fputs("\\newcommand{\\blackboard}{\n" "\\board\n", pf);
    Points1_12(pf, 10);
    Points13_24(pf, 240);
    fputs("}\n\n", pf);

    fputs("\\newcommand{\\whiteboard}{\n" "\\board\n", pf);
    Points1_12(pf, 240);
    Points13_24(pf, 10);
    fputs("}\n\n" "\\addtolength\\textwidth{144pt}\n" "\\addtolength\\textheight{144pt}\n" "\\addtolength\\oddsidemargin{-72pt}\n" "\\addtolength\\evensidemargin{-72pt}\n" "\\addtolength\\topmargin{-72pt}\n\n" "\\setlength{\\unitlength}{0.20mm}\n\n"       /* user should set size */
          "\\begin{document}\n", pf);

    /* FIXME define commands for \cubetext, \labeltext, etc. here, which
     * are scaled to a size specified by the user.  The other functions
     * should then use those definitions. */
}

static void
LaTeXEpilogue(FILE * pf)
{

    fputs("\\end{document}\n", pf);
}

static void
DrawLaTeXPoint(FILE * pf, int i, int fPlayer, unsigned int c)
{

    unsigned int j;
    int x, y = 0;

    if (i < 6)
        x = 320 - 20 * i;
    else if (i < 12)
        x = 300 - 20 * i;
    else if (i < 18)
        x = 20 * i - 160;
    else if (i < 24)
        x = 20 * i - 140;
    else if (i == 24)           /* bar */
        x = 200;
    else                        /* off */
        x = 365;

    if (fClockwise)
        x = 400 - x;

    for (j = 0; j < c; j++) {
        if (j == 5 || (i == 24 && j == 3)) {
            fprintf(pf, "\\whiten\\path(%d,%d)(%d,%d)(%d,%d)(%d,%d)(%d,%d)\n"
                    "\\path(%d,%d)(%d,%d)(%d,%d)(%d,%d)(%d,%d)\n"
                    "\\put(%d,%d){\\makebox(10,10){\\textsf{\\tiny %u}}}\n",
                    x - 5, y - 5, x + 5, y - 5, x + 5, y + 5, x - 5, y + 5,
                    x - 5, y - 5,
                    x - 5, y - 5, x + 5, y - 5, x + 5, y + 5, x - 5, y + 5, x - 5, y - 5, x - 5, y - 5, c);
            break;
        }

        if (i == 24)
            /* bar */
            y = fPlayer ? 60 + 20 * j : 200 - 20 * j;
        else if (fPlayer)
            y = i < 12 || i == 25 ? 30 + 20 * j : 230 - 20 * j;
        else
            y = i >= 12 && i != 25 ? 30 + 20 * j : 230 - 20 * j;

        fprintf(pf, fPlayer ? "\\put(%d,%d){\\circle{10}}"
                "\\put(%d,%d){\\blacken\\circle{20}}\n" :
                "\\put(%d,%d){\\whiten\\circle{20}}" "\\put(%d,%d){\\circle{20}}\n", x, y, x, y);
    }
}

static void
PrintLaTeXBoard(FILE * pf, matchstate * pms, int fPlayer)
{

    unsigned int anOff[2] = { 15, 15 };
    int i, x, y;

    /* FIXME print position ID and pip count, and the player on roll.
     * Print player names too? */

    fprintf(pf, "\\bigskip\\pagebreak[1]\\begin{center}\\begin{picture}"
            "(356,240)(22,10)\n" "\\%sboard\n", fPlayer ? "black" : "white");

    for (i = 0; i < 25; i++) {
        anOff[0] -= pms->anBoard[0][i];
        anOff[1] -= pms->anBoard[1][i];

        DrawLaTeXPoint(pf, i, 0, pms->anBoard[!fPlayer][i]);
        DrawLaTeXPoint(pf, i, 1, pms->anBoard[fPlayer][i]);
    }

    DrawLaTeXPoint(pf, 25, 0, anOff[!fPlayer]);
    DrawLaTeXPoint(pf, 25, 1, anOff[fPlayer]);

    if (fClockwise)
        x = 365;
    else
        x = 35;

    if (pms->fCubeOwner < 0)
        y = 130;
    else if (pms->fCubeOwner)
        y = 30;
    else
        y = 230;

    fprintf(pf, "\\path(%d,%d)(%d,%d)(%d,%d)(%d,%d)(%d,%d)"
            "\\put(%d,%d){\\makebox(24,24){\\textsf{\\large %d}}}\n",
            x - 12, y - 12, x + 12, y - 12, x + 12, y + 12, x - 12, y + 12,
            x - 12, y - 12, x - 12, y - 12, pms->nCube == 1 ? 64 : pms->nCube);

    fputs("\\end{picture}\\end{center}\\vspace{-4mm}\n\n\\nopagebreak[4]\n", pf);
}

/*
 * This was removed at first when unicode encoding was used, but we
 * still need to escape what could cause problems (like % $ _
 * etc...). Just to be safe, we escape everything but simple accented
 * characters.
 */
static void
LaTeXEscape(FILE * pf, char *pch)
{

    /* Translation table from GNU recode, by Francois Pinard. */
    static struct translation {
        const unsigned char uch;
        const char *sz;
    } at[] = {
        {
        10, "\n\n"},            /* \n */
        {
        35, "\\#"},             /* # */
        {
        36, "\\$"},             /* $ */
        {
        37, "\\%"},             /* % */
        {
        38, "\\&"},             /* & */
        {
        60, "\\mbox{$<$}"},     /* < */
        {
        62, "\\mbox{$>$}"},     /* > */
        {
        92, "\\mbox{$\\backslash{}$}"}, /* \ */
        {
        94, "\\^{}"},           /* ^ */
        {
        95, "\\_"},             /* _ */
        {
        123, "\\{"},            /* { */
        {
        124, "\\mbox{$|$}"},    /* | */
        {
        125, "\\}"},            /* } */
        {
        126, "\\~{}"},          /* ~ */
        {
        160, "~"},              /* no-break space */
        {
        161, "!`"},             /* inverted exclamation mark */
        {
        163, "\\pound{}"},      /* pound sign */
        {
        167, "\\S{}"},          /* paragraph sign, section sign */
        {
        168, "\\\"{}"},         /* diaeresis */
        {
        169, "\\copyright{}"},  /* copyright sign */
        {
        171, "``"},             /* left angle quotation mark */
        {
        172, "\\neg{}"},        /* not sign */
        {
        173, "\\-"},            /* soft hyphen */
        {
        176, "\\mbox{$^\\circ$}"},      /* degree sign */
        {
        177, "\\mbox{$\\pm$}"}, /* plus-minus sign */
        {
        178, "\\mbox{$^2$}"},   /* superscript two */
        {
        179, "\\mbox{$^3$}"},   /* superscript three */
        {
        180, "\\'{}"},          /* acute accent */
        {
        181, "\\mbox{$\\mu$}"}, /* small greek mu, micro sign */
        {
        183, "\\cdotp"},        /* middle dot */
        {
        184, "\\,{}"},          /* cedilla */
        {
        185, "\\mbox{$^1$}"},   /* superscript one */
        {
        187, "''"},             /* right angle quotation mark */
        {
        188, "\\frac1/4{}"},    /* vulgar fraction one quarter */
        {
        189, "\\frac1/2{}"},    /* vulgar fraction one half */
        {
        190, "\\frac3/4{}"},    /* vulgar fraction three quarters */
        {
        191, "?`"},             /* inverted question mark */
        {
        0, NULL}
    };
    int i;

    if (!pch)
        return;

    while (*pch) {
        for (i = 0; at[i].uch; i++)
            if (at[i].uch > *pch) {
                /* no translation required */
                putc(*pch, pf);
                break;
            } else if (at[i].uch == *pch) {
                /* translation found */
                fputs(at[i].sz, pf);
                break;
            }
        pch++;
    }
}

static void
PrintLaTeXComment(FILE * pf, char *pch)
{

    LaTeXEscape(pf, pch);
    if (pch != NULL)
        fputs(pch, pf);
    fputs("\n\n", pf);
}

static void
PrintLaTeXCubeAnalysis(FILE * pf, const matchstate * pms, int fPlayer,
                       float aarOutput[2][NUM_ROLLOUT_OUTPUTS],
                       float aarStdDev[2][NUM_ROLLOUT_OUTPUTS], const evalsetup * pes, int fTake)
{

    cubeinfo ci;

    if (pes->et == EVAL_NONE)
        return;

    SetCubeInfo(&ci, pms->nCube, pms->fCubeOwner, fPlayer, pms->nMatchTo,
                pms->anScore, pms->fCrawford, pms->fJacoby, nBeavers, pms->bgv);

    /* FIXME use center and tabular environment instead of verbatim */
    fputs("{\\begin{quote}\\footnotesize\\begin{verbatim}\n", pf);
    fputs(OutputCubeAnalysis(aarOutput, aarStdDev, pes, &ci, fTake), pf);
    fputs("\\end{verbatim}\\end{quote}}\n", pf);
}

static const char *
PlayerSymbol(int fPlayer)
{

    return fPlayer ? "\\textbullet{}" : "\\textopenbullet{}";
}

static void
ExportGameLaTeX(FILE * pf, listOLD * plGame)
{

    listOLD *pl;
    moverecord *pmr;
    matchstate msExport;
    int fTook = FALSE;
    unsigned int i;
    char sz[1024];
    doubletype dt = DT_NORMAL;
    listOLD *pl_hint = NULL;

    updateStatisticsGame(plGame);

    if (game_is_last(plGame))
        pl_hint = game_add_pmr_hint(plGame);
    for (pl = plGame->plNext; pl != plGame; pl = pl->plNext) {
        pmr = pl->p;
        FixMatchState(&msExport, pmr);
        switch (pmr->mt) {
        case MOVE_GAMEINFO:
            dt = DT_NORMAL;
            fputs("\\clearpage\n", pf);
            fputs("\\noindent{\\Large ", pf);
            if (pmr->g.nMatch) {
                fprintf(pf, ngettext("%d point match", "%d points match", pmr->g.nMatch), pmr->g.nMatch);
                fprintf(pf, _(" (game %d)"), pmr->g.i + 1);
             } else
                fprintf(pf, _("Money session (game %d)"), pmr->g.i + 1);

            fputs("}\n\n\\vspace{\\baselineskip}\n\n", pf);

            fprintf(pf, "\\noindent\n\\makebox[0.5\\textwidth][s]" "{\\large %s ", PlayerSymbol(0));
            LaTeXEscape(pf, ap[0].szName);
            fprintf(pf, " (");
            fprintf(pf, ngettext("%d point", "%d points", pmr->g.anScore[0]), pmr->g.anScore[0]);
            fprintf(pf, ")\\hfill}");
            fprintf(pf, "\\makebox[0.5\\textwidth][s]" "{\\large %s ", PlayerSymbol(1));
            LaTeXEscape(pf, ap[1].szName);
            fprintf(pf, " (");
            fprintf(pf, ngettext("%d point", "%d points", pmr->g.anScore[1]), pmr->g.anScore[1]);
            fprintf(pf, ")\\hfill}\n\n");

            break;

        case MOVE_NORMAL:
            dt = DT_NORMAL;
            msExport.fTurn = msExport.fMove = pmr->fPlayer;
            if (fTook)
                /* no need to print board following a double/take */
                fTook = FALSE;
            else
                PrintLaTeXBoard(pf, &msExport, pmr->fPlayer);

            PrintLaTeXCubeAnalysis(pf, &msExport, pmr->fPlayer,
                                   pmr->CubeDecPtr->aarOutput, pmr->CubeDecPtr->aarStdDev, &pmr->CubeDecPtr->esDouble, -1);
            /* FIXME: output cube skill */

            sprintf(sz, "%s %u%u%s: ", PlayerSymbol(pmr->fPlayer),
                    pmr->anDice[0], pmr->anDice[1], aszLuckTypeLaTeXAbbr[pmr->lt]);
            FormatMove(strchr(sz, 0), (ConstTanBoard) msExport.anBoard, pmr->n.anMove);
            fprintf(pf, "\\begin{center}%s%s\\end{center}\n\n", sz, aszSkillTypeAbbr[pmr->n.stMove]);

            /* FIXME use center and tabular environment instead of verbatim */
            fputs("{\\footnotesize\\begin{verbatim}\n", pf);
            for (i = 0; i < pmr->ml.cMoves; i++) {
                if (i >= exsExport.nMoves && i != pmr->n.iMove)
                    continue;

                putc(i == pmr->n.iMove ? '*' : ' ', pf);
                FormatMoveHint(sz, &msExport, &pmr->ml, i,
                               i != pmr->n.iMove || i != pmr->ml.cMoves - 1 || i < exsExport.nMoves, TRUE, TRUE);
                fputs(sz, pf);
            }
            fputs("\\end{verbatim}}", pf);

            PrintLaTeXComment(pf, pmr->sz);

            break;

        case MOVE_DOUBLE:
            dt = DoubleType(msExport.fDoubled, msExport.fMove, msExport.fTurn);
            if (dt != DT_NORMAL) {
                fprintf(pf, "\\begin{center}\\emph{%s}\\end{center}", _("Cannot analyse beaver nor raccoons!"));
                break;
            }
            PrintLaTeXBoard(pf, &msExport, pmr->fPlayer);

            PrintLaTeXCubeAnalysis(pf, &msExport, pmr->fPlayer,
                                   pmr->CubeDecPtr->aarOutput, pmr->CubeDecPtr->aarStdDev, &pmr->CubeDecPtr->esDouble, -1);

            /* FIXME what about beavers? */
            fprintf(pf, "\\begin{center}%s %s%s\\end{center}\n\n",
                    PlayerSymbol(pmr->fPlayer), _("Double"), aszSkillTypeAbbr[pmr->stCube]);

            PrintLaTeXComment(pf, pmr->sz);

            break;

        case MOVE_TAKE:
            if (dt != DT_NORMAL) {
                dt = DT_NORMAL;
                fprintf(pf, "\\begin{center}\\emph{%s}\\end{center}", _("Cannot analyse beaver nor raccoons!"));
                break;
            }
            fTook = TRUE;
            fprintf(pf, "\\begin{center}%s %s%s\\end{center}\n\n",
                    PlayerSymbol(pmr->fPlayer), _("Take"), aszSkillTypeAbbr[pmr->stCube]);

            PrintLaTeXComment(pf, pmr->sz);

            break;

        case MOVE_DROP:
            if (dt != DT_NORMAL) {
                dt = DT_NORMAL;
                fprintf(pf, "\\begin{center}\\emph{%s}\\end{center}", _("Cannot analyse beaver nor raccoons!"));
                break;
            }
            fprintf(pf, "\\begin{center}%s %s%s\\end{center}\n\n",
                    PlayerSymbol(pmr->fPlayer), _("Drop"), aszSkillTypeAbbr[pmr->stCube]);

            PrintLaTeXComment(pf, pmr->sz);

            break;

        case MOVE_RESIGN:
            fprintf(pf, "\\begin{center}%s %s %s\\end{center}\n\n",
                    PlayerSymbol(pmr->fPlayer), _("Resigns"), gettext(aszGameResult[pmr->r.nResigned - 1]));
            /* FIXME print resignation analysis, if available */
            PrintLaTeXComment(pf, pmr->sz);
            break;

        case MOVE_SETDICE:
            /* ignore */
            break;

        case MOVE_SETBOARD:
        case MOVE_SETCUBEVAL:
        case MOVE_SETCUBEPOS:
            /* FIXME print something? */
            break;
        }

        ApplyMoveRecord(&msExport, plGame, pmr);
    }

    if (pl_hint)
        game_remove_pmr_hint(pl_hint);

    /* FIXME print game result */
}

extern void
CommandExportGameLaTeX(char *sz)
{

    FILE *pf;

    sz = NextToken(&sz);

    if (!plGame) {
        outputl(_("No game in progress (type `new game' to start one)."));
        return;
    }

    if (!confirmOverwrite(sz, fConfirmSave))
        return;

    if (!sz || !*sz) {
        outputl(_("You must specify a file to export to (see `help export " "game latex')."));
        return;
    }

    if (!strcmp(sz, "-"))
        pf = stdout;
    else if ((pf = g_fopen(sz, "w")) == 0) {
        outputerr(sz);
        return;
    }

    LaTeXPrologue(pf);

    ExportGameLaTeX(pf, plGame);

    LaTeXEpilogue(pf);

    if (pf != stdout)
        fclose(pf);

    setDefaultFileName(sz);

}

extern void
CommandExportMatchLaTeX(char *sz)
{

    FILE *pf;
    listOLD *pl;
    int fDontClose = FALSE;

    sz = NextToken(&sz);

    if (!sz || !*sz) {
        outputl(_("You must specify a file to export to (see `help export " "match latex')."));
        return;
    }

    if (!confirmOverwrite(sz, fConfirmSave))
        return;

    if (!strcmp(sz, "-")) {
        pf = stdout;
        fDontClose = TRUE;
    } else if ((pf = g_fopen(sz, "w")) == 0) {
        outputerr(sz);
        return;
    }

    LaTeXPrologue(pf);

    /* FIXME write match introduction? */

    for (pl = lMatch.plNext; pl != &lMatch; pl = pl->plNext)
        ExportGameLaTeX(pf, pl->p);

    LaTeXEpilogue(pf);

    if (!fDontClose)
        fclose(pf);

    setDefaultFileName(sz);

}
