/*
 * gtkcube.c
 *
 * by Joern Thyssen <jthyssen@dk.ibm.com>, 2002
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 3 or later of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id: gtkcube.c,v 1.94 2018/07/21 22:10:42 plm Exp $
 */

#include "config.h"

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

#include "backgammon.h"
#include "eval.h"
#include "export.h"
#include "rollout.h"
#include "gtkgame.h"
#include "gtkcube.h"
#include "gtkwindows.h"
#include "progress.h"
#include "format.h"
#include "gtklocdefs.h"


typedef struct _cubehintdata {
    GtkWidget *pwFrame;         /* the table */
    GtkWidget *pw;              /* the box */
    GtkWidget *pwTools;         /* the tools */
    moverecord *pmr;
    matchstate ms;
    int did_double;
    int did_take;
    int hist;
} cubehintdata;


static GtkWidget *
OutputPercentsTable(const float ar[])
{
    GtkWidget *pwTable;
    GtkWidget *pw;
    int i;
    static gchar *headings[] = { N_("Win"), N_("W(g)"), N_("W(bg)"),
        "-", N_("Lose"), N_("L(g)"), N_("L(bg)")
    };
    pwTable = gtk_table_new(2, 7, FALSE);

    for (i = 0; i < 7; i++) {
        pw = gtk_label_new(gettext(headings[i]));
        gtk_table_attach(GTK_TABLE(pwTable), pw, i, i + 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 0);
    }

    pw = gtk_label_new(OutputPercent(ar[OUTPUT_WIN]));
    gtk_table_attach(GTK_TABLE(pwTable), pw, 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 0);
    pw = gtk_label_new(OutputPercent(ar[OUTPUT_WINGAMMON]));
    gtk_table_attach(GTK_TABLE(pwTable), pw, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 0);
    pw = gtk_label_new(OutputPercent(ar[OUTPUT_WINBACKGAMMON]));
    gtk_table_attach(GTK_TABLE(pwTable), pw, 2, 3, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 0);

    pw = gtk_label_new(" - ");
    gtk_table_attach(GTK_TABLE(pwTable), pw, 3, 4, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 0);

    pw = gtk_label_new(OutputPercent(1.0f - ar[OUTPUT_WIN]));
    gtk_table_attach(GTK_TABLE(pwTable), pw, 4, 5, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 0);
    pw = gtk_label_new(OutputPercent(ar[OUTPUT_LOSEGAMMON]));
    gtk_table_attach(GTK_TABLE(pwTable), pw, 5, 6, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 0);
    pw = gtk_label_new(OutputPercent(ar[OUTPUT_LOSEBACKGAMMON]));
    gtk_table_attach(GTK_TABLE(pwTable), pw, 6, 7, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 2, 0);

    return pwTable;
}


static GtkWidget *
TakeAnalysis(cubehintdata * pchd)
{
    cubeinfo ci;
    GtkWidget *pw;
    GtkWidget *pwTable;
    GtkWidget *pwFrame;
    int iRow;
    int i;
    cubedecision cd;
    int ai[2];
    const char *aszCube[] = {
        NULL, NULL,
        N_("Take"),
        N_("Pass")
    };
    float arDouble[4];
    gchar *sz;
    cubedecisiondata *cdec = pchd->pmr->CubeDecPtr;
    const evalsetup *pes = &cdec->esDouble;


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

    GetMatchStateCubeInfo(&ci, &pchd->ms);

    cd = FindCubeDecision(arDouble, cdec->aarOutput, &ci);

    /* header */

    pwFrame = gtk_frame_new(_("Take analysis"));
    gtk_container_set_border_width(GTK_CONTAINER(pwFrame), 8);

    pwTable = gtk_table_new(6, 4, FALSE);
    gtk_container_add(GTK_CONTAINER(pwFrame), pwTable);

    /* if EVAL_EVAL include cubeless equity and winning percentages */

    iRow = 0;

    InvertEvaluationR(cdec->aarOutput[0], &ci);
    InvertEvaluationR(cdec->aarOutput[1], &ci);

    switch (pes->et) {
    case EVAL_EVAL:
        if (ci.nMatchTo)
            sz = g_strdup_printf(_("Cubeless %d-ply %s: %s (Money: %s)"),
                                 pes->ec.nPlies,
                                 fOutputMWC ? _("MWC") : _("equity"),
                                 OutputEquity(cdec->aarOutput[0][OUTPUT_EQUITY],
                                              &ci, TRUE), OutputMoneyEquity(cdec->aarOutput[0], TRUE));
        else
            sz = g_strdup_printf(_("Cubeless %d-ply equity: %s"),
                                 pes->ec.nPlies, OutputMoneyEquity(cdec->aarOutput[0], TRUE));

        break;

    case EVAL_ROLLOUT:
        if (ci.nMatchTo)
            sz = g_strdup_printf(_("Cubeless rollout %s: %s (Money: %s)"),
                                 fOutputMWC ? _("MWC") : _("equity"),
                                 OutputEquity(cdec->aarOutput[0][OUTPUT_EQUITY],
                                              &ci, TRUE), OutputMoneyEquity(cdec->aarOutput[0], TRUE));
        else
            sz = g_strdup_printf(_("Cubeless rollout equity: %s"), OutputMoneyEquity(cdec->aarOutput[0], TRUE));

        break;

    default:

        sz = g_strdup("");

    }

    pw = gtk_label_new(sz);
#if GTK_CHECK_VERSION(3,0,0)
    gtk_widget_set_halign(pw, GTK_ALIGN_START);
    gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
    gtk_misc_set_alignment(GTK_MISC(pw), 0, 0.5);
#endif
    g_free(sz);

    gtk_table_attach(GTK_TABLE(pwTable), pw, 0, 4, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 0);
    iRow++;

    /* winning percentages */

    switch (pes->et) {

    case EVAL_EVAL:

        pw = OutputPercentsTable(cdec->aarOutput[0]);
        gtk_table_attach(GTK_TABLE(pwTable), pw,
                         0, 4, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 4);
        iRow++;

        break;

    case EVAL_ROLLOUT:

        pw = OutputPercentsTable(cdec->aarOutput[1]);
        gtk_table_attach(GTK_TABLE(pwTable), pw,
                         0, 4, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 4);
        iRow++;

        break;

    default:

        g_assert_not_reached();

    }

    InvertEvaluationR(cdec->aarOutput[0], &ci);
    InvertEvaluationR(cdec->aarOutput[1], &ci);

    /* sub-header */

    pw = gtk_label_new(_("Cubeful equities:"));
    gtk_table_attach(GTK_TABLE(pwTable), pw, 0, 4, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 4);
#if GTK_CHECK_VERSION(3,0,0)
    gtk_widget_set_halign(pw, GTK_ALIGN_START);
    gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
    gtk_misc_set_alignment(GTK_MISC(pw), 0, 0.5);
#endif
    iRow++;

    /* ordered take actions with equities */

    if (arDouble[OUTPUT_TAKE] < arDouble[OUTPUT_DROP]) {
        ai[0] = OUTPUT_TAKE;
        ai[1] = OUTPUT_DROP;
    } else {
        ai[0] = OUTPUT_DROP;
        ai[1] = OUTPUT_TAKE;
    }

    for (i = 0; i < 2; i++) {

        /* numbering */

        sz = g_strdup_printf("%d.", i + 1);
        pw = gtk_label_new(sz);
#if GTK_CHECK_VERSION(3,0,0)
        gtk_widget_set_halign(pw, GTK_ALIGN_START);
        gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
        gtk_misc_set_alignment(GTK_MISC(pw), 0, 0.5);
#endif
        g_free(sz);

        gtk_table_attach(GTK_TABLE(pwTable), pw,
                         0, 1, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 0);

        /* label */

        pw = gtk_label_new(gettext(aszCube[ai[i]]));
#if GTK_CHECK_VERSION(3,0,0)
        gtk_widget_set_halign(pw, GTK_ALIGN_START);
        gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
        gtk_misc_set_alignment(GTK_MISC(pw), 0, 0.5);
#endif

        gtk_table_attach(GTK_TABLE(pwTable), pw,
                         1, 2, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 0);

        /* equity */

        if (!ci.nMatchTo || !fOutputMWC)
            sz = g_strdup_printf("%+7.3f", -arDouble[ai[i]]);
        else
            sz = g_strdup_printf("%7.3f%%", 100.0f * (1.0f - eq2mwc(arDouble[ai[i]], &ci)));

        pw = gtk_label_new(sz);
#if GTK_CHECK_VERSION(3,0,0)
        gtk_widget_set_halign(pw, GTK_ALIGN_END);
        gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
        gtk_misc_set_alignment(GTK_MISC(pw), 1, 0.5);
#endif
        g_free(sz);

        gtk_table_attach(GTK_TABLE(pwTable), pw,
                         2, 3, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 0);

        /* difference */

        if (i) {

            if (!ci.nMatchTo || !fOutputMWC)
                sz = g_strdup_printf("%+7.3f", arDouble[ai[0]] - arDouble[ai[i]]);
            else
                sz = g_strdup_printf("%+7.3f%%",
                                     100.0f * eq2mwc(arDouble[ai[0]], &ci) - 100.0f * eq2mwc(arDouble[ai[i]], &ci));

            pw = gtk_label_new(sz);
#if GTK_CHECK_VERSION(3,0,0)
            gtk_widget_set_halign(pw, GTK_ALIGN_END);
            gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
            gtk_misc_set_alignment(GTK_MISC(pw), 1, 0.5);
#endif
            g_free(sz);

            gtk_table_attach(GTK_TABLE(pwTable), pw,
                             3, 4, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 0);

        }

        iRow++;

    }

    /* proper cube action */

    pw = gtk_label_new(_("Correct response: "));
#if GTK_CHECK_VERSION(3,0,0)
    gtk_widget_set_halign(pw, GTK_ALIGN_START);
    gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
    gtk_misc_set_alignment(GTK_MISC(pw), 0, 0.5);
#endif

    gtk_table_attach(GTK_TABLE(pwTable), pw, 0, 2, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 8);

    switch (cd) {

    case DOUBLE_TAKE:
    case NODOUBLE_TAKE:
    case TOOGOOD_TAKE:
    case REDOUBLE_TAKE:
    case NO_REDOUBLE_TAKE:
    case TOOGOODRE_TAKE:
    case NODOUBLE_DEADCUBE:
    case NO_REDOUBLE_DEADCUBE:
    case OPTIONAL_DOUBLE_TAKE:
    case OPTIONAL_REDOUBLE_TAKE:
        pw = gtk_label_new(_("Take"));
        break;

    case DOUBLE_PASS:
    case TOOGOOD_PASS:
    case REDOUBLE_PASS:
    case TOOGOODRE_PASS:
    case OPTIONAL_DOUBLE_PASS:
    case OPTIONAL_REDOUBLE_PASS:
        pw = gtk_label_new(_("Pass"));
        break;

    case DOUBLE_BEAVER:
    case NODOUBLE_BEAVER:
    case NO_REDOUBLE_BEAVER:
    case OPTIONAL_DOUBLE_BEAVER:
        pw = gtk_label_new(_("Beaver!"));
        break;

    case NOT_AVAILABLE:
        pw = gtk_label_new(_("Eat it!"));
        break;

    }

#if GTK_CHECK_VERSION(3,0,0)
    gtk_widget_set_halign(pw, GTK_ALIGN_START);
    gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
    gtk_misc_set_alignment(GTK_MISC(pw), 0, 0.5);
#endif

    gtk_table_attach(GTK_TABLE(pwTable), pw, 2, 4, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 8);

    return pwFrame;

}


/*
 * Make cube analysis widget 
 *
 * Input:
 *   aarOutput, aarStdDev: evaluations
 *   pes: evaluation setup
 *
 * Returns:
 *   nice and pretty widget with cube analysis
 *
 */

static GtkWidget *
CubeAnalysis(cubehintdata * pchd)
{
    cubeinfo ci;
    GtkWidget *pw;
    GtkWidget *pwTable;
    GtkWidget *pwFrame;
    int iRow;
    int i;
    cubedecision cd;
    float r;
    int ai[3];
    const char *aszCube[] = {
        NULL,
        N_("No double"),
        N_("Double, take"),
        N_("Double, pass")
    };
    float arDouble[4];
    gchar *sz;
    cubedecisiondata *cdec = pchd->pmr->CubeDecPtr;
    const evalsetup *pes = &cdec->esDouble;


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

    GetMatchStateCubeInfo(&ci, &pchd->ms);

    cd = FindCubeDecision(arDouble, cdec->aarOutput, &ci);

    if (!GetDPEq(NULL, NULL, &ci) && !(pchd->pmr->mt == MOVE_DOUBLE))
        /* No cube action possible */
        return NULL;

    /* header */

    pwFrame = gtk_frame_new(_("Cube analysis"));
    gtk_container_set_border_width(GTK_CONTAINER(pwFrame), 8);

#if GTK_CHECK_VERSION(3,0,0)
    pw = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
#else
    pw = gtk_vbox_new(FALSE, 0);
#endif
    gtk_container_add(GTK_CONTAINER(pwFrame), pw);

    pwTable = gtk_table_new(8, 4, FALSE);
    gtk_box_pack_start(GTK_BOX(pw), pwTable, FALSE, FALSE, 0);

    iRow = 0;

    /* cubeless equity */

    /* Define first row of text (in sz) */
    switch (pes->et) {
    case EVAL_EVAL:
        if (ci.nMatchTo)
            sz = g_strdup_printf(_("Cubeless %d-ply %s: %s (Money: %s)"),
                                 pes->ec.nPlies,
                                 fOutputMWC ? _("MWC") : _("equity"),
                                 OutputEquity(cdec->aarOutput[0][OUTPUT_EQUITY],
                                              &ci, TRUE), OutputMoneyEquity(cdec->aarOutput[0], TRUE));
        else
            sz = g_strdup_printf(_("Cubeless %d-ply equity: %s"),
                                 pes->ec.nPlies, OutputMoneyEquity(cdec->aarOutput[0], TRUE));

        break;

    case EVAL_ROLLOUT:
        if (ci.nMatchTo)
            sz = g_strdup_printf(_("Cubeless rollout %s: %s (Money: %s)"),
                                 fOutputMWC ? _("MWC") : _("equity"),
                                 OutputEquity(cdec->aarOutput[0][OUTPUT_EQUITY],
                                              &ci, TRUE), OutputMoneyEquity(cdec->aarOutput[0], TRUE));
        else
            sz = g_strdup_printf(_("Cubeless rollout equity: %s"), OutputMoneyEquity(cdec->aarOutput[0], TRUE));

        break;

    default:

        sz = g_strdup("");

    }

    pw = gtk_label_new(sz);
#if GTK_CHECK_VERSION(3,0,0)
    gtk_widget_set_halign(pw, GTK_ALIGN_START);
    gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
    gtk_misc_set_alignment(GTK_MISC(pw), 0, 0.5);
#endif
    g_free(sz);

    gtk_table_attach(GTK_TABLE(pwTable), pw, 0, 4, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 0);

    iRow++;


    /* if EVAL_EVAL include cubeless equity and winning percentages */

    if (pes->et == EVAL_EVAL) {

        pw = OutputPercentsTable(cdec->aarOutput[0]);

        gtk_table_attach(GTK_TABLE(pwTable), pw,
                         0, 4, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 4);

        iRow++;

    }

    pw = gtk_label_new(_("Cubeful equities:"));
    gtk_table_attach(GTK_TABLE(pwTable), pw, 0, 4, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 4);
#if GTK_CHECK_VERSION(3,0,0)
    gtk_widget_set_halign(pw, GTK_ALIGN_START);
    gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
    gtk_misc_set_alignment(GTK_MISC(pw), 0, 0.5);
#endif
    iRow++;


    getCubeDecisionOrdering(ai, arDouble, cdec->aarOutput, &ci);

    for (i = 0; i < 3; i++) {

        /* numbering */

        sz = g_strdup_printf("%d.", i + 1);
        pw = gtk_label_new(sz);
#if GTK_CHECK_VERSION(3,0,0)
        gtk_widget_set_halign(pw, GTK_ALIGN_START);
        gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
        gtk_misc_set_alignment(GTK_MISC(pw), 0, 0.5);
#endif
        g_free(sz);

        gtk_table_attach(GTK_TABLE(pwTable), pw,
                         0, 1, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 0);

        /* label */

        pw = gtk_label_new(gettext(aszCube[ai[i]]));
#if GTK_CHECK_VERSION(3,0,0)
        gtk_widget_set_halign(pw, GTK_ALIGN_START);
        gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
        gtk_misc_set_alignment(GTK_MISC(pw), 0, 0.5);
#endif

        gtk_table_attach(GTK_TABLE(pwTable), pw,
                         1, 2, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 0);

        /* equity */

        sz = OutputEquity(arDouble[ai[i]], &ci, TRUE);

        pw = gtk_label_new(sz);
#if GTK_CHECK_VERSION(3,0,0)
        gtk_widget_set_halign(pw, GTK_ALIGN_END);
        gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
        gtk_misc_set_alignment(GTK_MISC(pw), 1, 0.5);
#endif

        gtk_table_attach(GTK_TABLE(pwTable), pw,
                         2, 3, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 0);

        /* difference */

        if (i) {

            pw = gtk_label_new(OutputEquityDiff(arDouble[ai[i]], arDouble[OUTPUT_OPTIMAL], &ci));
#if GTK_CHECK_VERSION(3,0,0)
            gtk_widget_set_halign(pw, GTK_ALIGN_END);
            gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
            gtk_misc_set_alignment(GTK_MISC(pw), 1, 0.5);
#endif

            gtk_table_attach(GTK_TABLE(pwTable), pw,
                             3, 4, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 0);

        }

        iRow++;

        /* rollout details */

        if (pes->et == EVAL_ROLLOUT && (ai[i] == OUTPUT_TAKE || ai[i] == OUTPUT_NODOUBLE)) {

            /* FIXME: output cubeless equities and percentages for rollout */
            /*        probably along with rollout details */

            pw = gtk_label_new(OutputPercents(cdec->aarOutput[ai[i] - 1], TRUE));
#if GTK_CHECK_VERSION(3,0,0)
            gtk_widget_set_halign(pw, GTK_ALIGN_CENTER);
            gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
            gtk_misc_set_alignment(GTK_MISC(pw), 0.5, 0.5);
#endif

            gtk_table_attach(GTK_TABLE(pwTable), pw,
                             0, 4, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 4);

            iRow++;

        }

    }

    /* proper cube action */

    pw = gtk_label_new(_("Proper cube action: "));
#if GTK_CHECK_VERSION(3,0,0)
    gtk_widget_set_halign(pw, GTK_ALIGN_START);
    gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
    gtk_misc_set_alignment(GTK_MISC(pw), 0, 0.5);
#endif

    gtk_table_attach(GTK_TABLE(pwTable), pw, 0, 2, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 8);

    pw = gtk_label_new(GetCubeRecommendation(cd));
#if GTK_CHECK_VERSION(3,0,0)
    gtk_widget_set_halign(pw, GTK_ALIGN_START);
    gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
    gtk_misc_set_alignment(GTK_MISC(pw), 0, 0.5);
#endif

    gtk_table_attach(GTK_TABLE(pwTable), pw, 2, 3, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 8);

    /* percent */

    if ((r = getPercent(cd, arDouble)) >= 0.0f) {

        sz = g_strdup_printf("(%.1f%%)", 100.0f * r);
        pw = gtk_label_new(sz);
        g_free(sz);
#if GTK_CHECK_VERSION(3,0,0)
        gtk_widget_set_halign(pw, GTK_ALIGN_END);
        gtk_widget_set_valign(pw, GTK_ALIGN_CENTER);
#else
        gtk_misc_set_alignment(GTK_MISC(pw), 1, 0.5);
#endif

        gtk_table_attach(GTK_TABLE(pwTable), pw,
                         3, 4, iRow, iRow + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 8, 8);

    }

    return pwFrame;
}

static void
UpdateCubeAnalysis(cubehintdata * pchd)
{

    GtkWidget *pw = 0;
    doubletype dt = DoubleType(ms.fDoubled, ms.fMove, ms.fTurn);

    find_skills(pchd->pmr, &ms, pchd->did_double, pchd->did_take);

    switch (pchd->pmr->mt) {
    case MOVE_NORMAL:
    case MOVE_SETDICE:
    case MOVE_SETCUBEPOS:
    case MOVE_SETCUBEVAL:
        /* See CreateCubeAnalysis() for these  MOVE_SET* cases */
    case MOVE_DOUBLE:
        if (dt == DT_NORMAL)
            pw = CubeAnalysis(pchd);
        else if (dt == DT_BEAVER)
            pw = TakeAnalysis(pchd);
        break;

    case MOVE_DROP:
    case MOVE_TAKE:
        pw = TakeAnalysis(pchd);
        break;

    default:
        g_assert_not_reached();
        break;

    }

    /* destroy current analysis */

    gtk_container_remove(GTK_CONTAINER(pchd->pw), pchd->pwFrame);

    /* re-create analysis + tools */

    gtk_box_pack_start(GTK_BOX(pchd->pw), pchd->pwFrame = pw, FALSE, FALSE, 0);

    gtk_widget_show_all(pchd->pw);

}




static void
CubeAnalysisRollout(GtkWidget * pw, cubehintdata * pchd)
{

    cubeinfo ci;
    float aarOutput[2][NUM_ROLLOUT_OUTPUTS];
    float aarStdDev[2][NUM_ROLLOUT_OUTPUTS];
    cubedecisiondata *cdec = pchd->pmr->CubeDecPtr;
    rolloutstat aarsStatistics[2][2];
    evalsetup *pes = &cdec->esDouble;
    char asz[2][40];
    void *p;

    if (pes->et != EVAL_ROLLOUT) {
        pes->rc = rcRollout;
        pes->rc.nGamesDone = 0;
    } else {
        pes->rc.nTrials = rcRollout.nTrials;
        pes->rc.fStopOnSTD = rcRollout.fStopOnSTD;
        pes->rc.nMinimumGames = rcRollout.nMinimumGames;
        pes->rc.rStdLimit = rcRollout.rStdLimit;
        memcpy(aarOutput, cdec->aarOutput, 2 * NUM_ROLLOUT_OUTPUTS * sizeof(float));
        memcpy(aarStdDev, cdec->aarStdDev, 2 * NUM_ROLLOUT_OUTPUTS * sizeof(float));
    }

    GetMatchStateCubeInfo(&ci, &pchd->ms);

    FormatCubePositions(&ci, asz);
    GTKSetCurrentParent(pw);
    RolloutProgressStart(&ci, 2, aarsStatistics, &pes->rc, asz, FALSE, &p);

    if (GeneralCubeDecisionR(aarOutput, aarStdDev, aarsStatistics,
                             (ConstTanBoard) pchd->ms.anBoard, &ci, &pes->rc, pes, RolloutProgress, p) < 0) {
        RolloutProgressEnd(&p, FALSE);
        return;
    }

    RolloutProgressEnd(&p, FALSE);

    memcpy(cdec->aarOutput, aarOutput, 2 * NUM_ROLLOUT_OUTPUTS * sizeof(float));
    memcpy(cdec->aarStdDev, aarStdDev, 2 * NUM_ROLLOUT_OUTPUTS * sizeof(float));

    if (pes->et != EVAL_ROLLOUT)
        memcpy(&pes->rc, &rcRollout, sizeof(rcRollout));

    pes->et = EVAL_ROLLOUT;

    /* If the source widget parent has been destroyed do not attempt
     * to update the hint window */
    if (!GDK_IS_WINDOW(gtk_widget_get_parent_window(pw)))
        return;

    UpdateCubeAnalysis(pchd);
    if (pchd->hist)
        ChangeGame(NULL);
}

static void
EvalCube(cubehintdata * pchd, evalcontext * pec)
{
    cubeinfo ci;
    decisionData dd;
    cubedecisiondata *cdec = pchd->pmr->CubeDecPtr;
    evalsetup *pes = &cdec->esDouble;

    GetMatchStateCubeInfo(&ci, &pchd->ms);

    dd.pboard = (ConstTanBoard) pchd->ms.anBoard;
    dd.pci = &ci;
    dd.pec = pec;
    dd.pes = NULL;
    if (RunAsyncProcess((AsyncFun) asyncCubeDecisionE, &dd, _("Considering cube action...")) != 0)
        return;

    /* save evaluation */


    memcpy(cdec->aarOutput, dd.aarOutput, 2 * NUM_ROLLOUT_OUTPUTS * sizeof(float));

    pes->et = EVAL_EVAL;
    memcpy(&pes->ec, dd.pec, sizeof(evalcontext));

    UpdateCubeAnalysis(pchd);
    if (pchd->hist)
        ChangeGame(NULL);
}

static void
CubeAnalysisEvalPly(GtkWidget * pw, cubehintdata * pchd)
{

    char *szPly = (char *) g_object_get_data(G_OBJECT(pw), "ply");
    evalcontext ec = { 0, 0, 0, TRUE, 0.0 };

    ec.fCubeful = esAnalysisCube.ec.fCubeful;
    ec.fUsePrune = esAnalysisCube.ec.fUsePrune;
    ec.nPlies = atoi(szPly);

    EvalCube(pchd, &ec);

}

static void
CubeAnalysisEval(GtkWidget * UNUSED(pw), cubehintdata * pchd)
{

    EvalCube(pchd, &GetEvalCube()->ec);

}

static void
CubeAnalysisEvalSettings(GtkWidget * pw, void *UNUSED(data))
{
    GTKSetCurrentParent(pw);
    SetAnalysis(NULL, 0, NULL);

    /* bring the dialog holding this button to the top */
    gtk_window_present(GTK_WINDOW(gtk_widget_get_toplevel(pw)));

}

static void
CubeAnalysisRolloutSettings(GtkWidget * pw, void *UNUSED(data))
{
    GTKSetCurrentParent(pw);
    SetRollouts(NULL, 0, NULL);

    /* bring the dialog holding this button to the top */
    gtk_window_present(GTK_WINDOW(gtk_widget_get_toplevel(pw)));

}


static void
CubeRolloutPresets(GtkWidget * pw, cubehintdata * pchd)
{
    const gchar *preset;
    gchar *file = NULL;
    gchar *path = NULL;
    gchar *command = NULL;

    preset = (const gchar *) g_object_get_data(G_OBJECT(pw), "user_data");
    file = g_strdup_printf("%s.rol", preset);
    path = g_build_filename(szHomeDirectory, "rol", file, NULL);
    if (g_file_test(path, G_FILE_TEST_IS_REGULAR)) {
        command = g_strdup_printf("load commands \"%s\"", path);
        outputoff();
        UserCommand(command);
        outputon();
        CubeAnalysisRollout(pw, pchd);
    } else {
        outputerrf(_("You need to save a preset as \"%s\""), file);
        CubeAnalysisRolloutSettings(pw, NULL);
    }
    g_free(file);
    g_free(path);
    g_free(command);
}

static void
CubeAnalysisMWC(GtkWidget * pw, cubehintdata * pchd)
{

    char sz[80];
    int f = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pw));

    if (f != fOutputMWC) {
        sprintf(sz, "set output mwc %s", fOutputMWC ? "off" : "on");

        UserCommand(sz);
        UserCommand("save settings");
    }

    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw), fOutputMWC);

    UpdateCubeAnalysis(pchd);

}

static char *
GetContent(cubehintdata * pchd)
{
    static char *pc;
    cubeinfo ci;
    cubedecisiondata *cdec = pchd->pmr->CubeDecPtr;

    GetMatchStateCubeInfo(&ci, &pchd->ms);
    pc = OutputCubeAnalysis(cdec->aarOutput, cdec->aarStdDev, &cdec->esDouble, &ci);

    return pc;
}

static void
CubeAnalysisCopy(GtkWidget * UNUSED(pw), cubehintdata * pchd)
{

    char *pc = GetContent(pchd);

    if (pc)
        GTKTextWindow(pc, _("Cube details"), DT_INFO, NULL);

}

static void
CubeAnalysisTempMap(GtkWidget * UNUSED(pw), cubehintdata * UNUSED(pchd))
{

    char *sz = g_strdup("show temperaturemap =cube");
    UserCommand(sz);
    g_free(sz);

}

static void
CubeAnalysisCmark(GtkWidget * pw, cubehintdata * pchd)
{
    pchd->pmr->CubeDecPtr->cmark = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pw));
}

static GtkWidget *
CreateCubeAnalysisTools(cubehintdata * pchd)
{
    GtkWidget *pwTools;
    GtkWidget *pwEval = gtk_button_new_with_label(_("Eval"));
    GtkWidget *pwply;
    GtkWidget *pwEvalSettings = gtk_button_new_with_label(_("..."));
    GtkWidget *pwRollout = gtk_button_new_with_label(_("Rollout"));
    GtkWidget *pwRolloutPresets;
    GtkWidget *pwRolloutSettings = gtk_button_new_with_label(_("..."));
    GtkWidget *pwMWC = gtk_toggle_button_new_with_label(_("MWC"));
    GtkWidget *pwCopy = gtk_button_new_with_label(_("Copy"));
    GtkWidget *pwTempMap = gtk_button_new_with_label(_("Temp. Map"));
    GtkWidget *pwCmark = gtk_toggle_button_new_with_label(_("Cmark"));
    GtkWidget *pw;
    int i;
    char *sz;

#if GTK_CHECK_VERSION(3,0,0)
    gtk_style_context_add_class(gtk_widget_get_style_context(pwEval), "gnubg-analysis-button");
    gtk_style_context_add_class(gtk_widget_get_style_context(pwEvalSettings), "gnubg-analysis-button");
    gtk_style_context_add_class(gtk_widget_get_style_context(pwRollout), "gnubg-analysis-button");
    gtk_style_context_add_class(gtk_widget_get_style_context(pwRolloutSettings), "gnubg-analysis-button");
    gtk_style_context_add_class(gtk_widget_get_style_context(pwMWC), "gnubg-analysis-button");
    gtk_style_context_add_class(gtk_widget_get_style_context(pwCopy), "gnubg-analysis-button");
    gtk_style_context_add_class(gtk_widget_get_style_context(pwTempMap), "gnubg-analysis-button");
    gtk_style_context_add_class(gtk_widget_get_style_context(pwCmark), "gnubg-analysis-button");
#endif

    /* toolbox on the left with buttons for eval, rollout and more */

    pchd->pwTools = pwTools = gtk_table_new(2, 5, FALSE);

    gtk_table_attach(GTK_TABLE(pwTools), pwEval, 0, 1, 0, 1,
                     (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);

    gtk_table_attach(GTK_TABLE(pwTools), pwEvalSettings, 1, 2, 0, 1,
                     (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);

#if GTK_CHECK_VERSION(3,0,0)
    pw = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
#else
    pw = gtk_hbox_new(FALSE, 0);
#endif
    gtk_table_attach(GTK_TABLE(pwTools), pw, 2, 3, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);

    for (i = 0; i < 5; ++i) {

        sz = g_strdup_printf("%d", i);  /* string is freed by set_data_full */
        pwply = gtk_button_new_with_label(sz);
#if GTK_CHECK_VERSION(3,0,0)
        gtk_style_context_add_class(gtk_widget_get_style_context(pwply), "gnubg-analysis-button");
#endif
        gtk_box_pack_start(GTK_BOX(pw), pwply, TRUE, TRUE, 0);

        g_signal_connect(G_OBJECT(pwply), "clicked", G_CALLBACK(CubeAnalysisEvalPly), pchd);

        g_object_set_data_full(G_OBJECT(pwply), "ply", sz, g_free);

        sz = g_strdup_printf(_("Evaluate play on cubeful %d-ply"), i);
        gtk_widget_set_tooltip_text(pwply, sz);
        g_free(sz);

    }

#if GTK_CHECK_VERSION(3,0,0)
    pwRolloutPresets = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
#else
    pwRolloutPresets = gtk_hbox_new(FALSE, 0);
#endif

    gtk_table_attach(GTK_TABLE(pwTools), pwMWC, 3, 4, 0, 1,
                     (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);

    gtk_table_attach(GTK_TABLE(pwTools), pwTempMap, 4, 5, 0, 1,
                     (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);

    gtk_table_attach(GTK_TABLE(pwTools), pwCmark, 4, 5, 1, 2,
                     (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);


    gtk_table_attach(GTK_TABLE(pwTools), pwRollout, 0, 1, 1, 2,
                     (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);

    gtk_table_attach(GTK_TABLE(pwTools), pwRolloutSettings, 1, 2, 1, 2,
                     (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);

    gtk_table_attach(GTK_TABLE(pwTools), pwRolloutPresets, 2, 3, 1, 2,
                     (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);

    for (i = 0; i < 5; ++i) {
        GtkWidget *ro_preset;
        sz = g_strdup_printf("%c", i + 'a');    /* string is freed by set_data_full */
        ro_preset = gtk_button_new_with_label(sz);
#if GTK_CHECK_VERSION(3,0,0)
        gtk_style_context_add_class(gtk_widget_get_style_context(ro_preset), "gnubg-analysis-button");
#endif
        gtk_box_pack_start(GTK_BOX(pwRolloutPresets), ro_preset, TRUE, TRUE, 0);

        g_signal_connect(G_OBJECT(ro_preset), "clicked", G_CALLBACK(CubeRolloutPresets), pchd);

        g_object_set_data_full(G_OBJECT(ro_preset), "user_data", sz, g_free);

        sz = g_strdup_printf(_("Rollout preset %c"), i + 'a');
        gtk_widget_set_tooltip_text(ro_preset, sz);
        g_free(sz);

    }

    gtk_table_attach(GTK_TABLE(pwTools), pwCopy, 3, 4, 1, 2,
                     (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);

    gtk_widget_set_sensitive(pwMWC, pchd->ms.nMatchTo);

    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pwCmark), pchd->pmr->CubeDecPtr->cmark);

    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pwMWC), fOutputMWC);

    /* signals */

    g_signal_connect(G_OBJECT(pwRollout), "clicked", G_CALLBACK(CubeAnalysisRollout), pchd);
    g_signal_connect(G_OBJECT(pwEval), "clicked", G_CALLBACK(CubeAnalysisEval), pchd);
    g_signal_connect(G_OBJECT(pwEvalSettings), "clicked", G_CALLBACK(CubeAnalysisEvalSettings), NULL);
    g_signal_connect(G_OBJECT(pwRolloutSettings), "clicked", G_CALLBACK(CubeAnalysisRolloutSettings), NULL);
    g_signal_connect(G_OBJECT(pwMWC), "toggled", G_CALLBACK(CubeAnalysisMWC), pchd);
    g_signal_connect(G_OBJECT(pwCopy), "clicked", G_CALLBACK(CubeAnalysisCopy), pchd);
    g_signal_connect(G_OBJECT(pwTempMap), "clicked", G_CALLBACK(CubeAnalysisTempMap), pchd);
    g_signal_connect(G_OBJECT(pwCmark), "toggled", G_CALLBACK(CubeAnalysisCmark), pchd);

    /* tool tips */

    gtk_widget_set_tooltip_text(pwRollout, _("Rollout cube decision with current settings"));

    gtk_widget_set_tooltip_text(pwEval, _("Evaluate cube decision with current settings"));

    gtk_widget_set_tooltip_text(pwRolloutSettings, _("Modify rollout settings"));

    gtk_widget_set_tooltip_text(pwEvalSettings, _("Modify evaluation settings"));

    gtk_widget_set_tooltip_text(pwMWC, _("Toggle output as MWC or equity"));

    gtk_widget_set_tooltip_text(pwCopy, _("Copy cube decision to clipboard"));

    gtk_widget_set_tooltip_text(pwCmark, _("Mark cube decision for later rollout"));

    gtk_widget_set_tooltip_text(pwTempMap, _("Show Sho Sengoku Temperature Map of position"));

    return pwTools;

}


extern GtkWidget *
CreateCubeAnalysis(moverecord * pmr, const matchstate * pms, int did_double, int did_take, int hist)
{
    doubletype dt = DoubleType(pms->fDoubled, pms->fMove, pms->fTurn);
    cubehintdata *pchd = g_new0(cubehintdata, 1);

    GtkWidget *pw, *pwhb, *pwx;

    pchd->pmr = pmr;
    pchd->ms = *pms;
    pchd->did_double = did_double;
    pchd->did_take = did_take;
    pchd->hist = hist;

#if GTK_CHECK_VERSION(3,0,0)
    pchd->pw = pw = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
#else
    pchd->pw = pw = gtk_hbox_new(FALSE, 2);
#endif

    switch (pmr->mt) {

    case MOVE_NORMAL:
    case MOVE_SETDICE:
        /*
         * We're stepping back in the game record after rolling and
         * asking for a hint on the missed cube decision, for instance
         */
    case MOVE_SETCUBEPOS:
    case MOVE_SETCUBEVAL:
        /*
         * These two happen in cube decisions loaded from a .sgf file
         */
    case MOVE_DOUBLE:
        if (dt == DT_NORMAL)
            pchd->pwFrame = CubeAnalysis(pchd);
        else if (dt == DT_BEAVER)
            pchd->pwFrame = TakeAnalysis(pchd);
        break;
    case MOVE_TAKE:
    case MOVE_DROP:
        pchd->pwFrame = TakeAnalysis(pchd);
        break;
    default:
        g_assert_not_reached();
        break;

    }

    if (!pchd->pwFrame) {
        g_free(pchd);
        return NULL;
    }

    gtk_box_pack_start(GTK_BOX(pw), pchd->pwFrame, FALSE, FALSE, 0);

#if GTK_CHECK_VERSION(3,0,0)
    pwx = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
#else
    pwx = gtk_vbox_new(FALSE, 0);
#endif

    gtk_box_pack_start(GTK_BOX(pwx), pw, FALSE, FALSE, 0);

#if GTK_CHECK_VERSION(3,0,0)
    pwhb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
#else
    pwhb = gtk_hbox_new(FALSE, 0);
#endif
    gtk_box_pack_start(GTK_BOX(pwhb), CreateCubeAnalysisTools(pchd), FALSE, FALSE, 8);

    gtk_box_pack_start(GTK_BOX(pwx), pwhb, FALSE, FALSE, 0);

    /* hook to free cubehintdata on widget destruction */
    g_object_set_data_full(G_OBJECT(pw), "cubehintdata", pchd, g_free);

    return pwx;

}
