/*
    DroidFish - An Android chess program.
    Copyright (C) 2014,2016  Peter Österlund, peterosterlund2@gmail.com

    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 <http://www.gnu.org/licenses/>.
*/

package org.petero.droidfish.tb;

/** Tablebase probe result. */
public final class ProbeResult implements Comparable<ProbeResult> {
    public enum Type {
        DTM,    // score is distance (full moves) to mate, or 0
        DTZ,    // score is distance (full moves) to zeroing move, or 0
        WDL,    // score is +-1 or 0
        NONE,   // No info available, score is 0
    }

    public Type type;
    public int wdl;   // +1 if if side to move wins, 0 for draw, -1 for loss
    public int score; // Distance to win in plies. Always >= 0.
                      // Note! Zero if side to move is checkmated.

    ProbeResult(Type type, int wdl, int score) {
        this.type = type;
        this.wdl = wdl;
        this.score = score;
    }

    /**
     * Return > 0 if other is "better" than this.
     * A win is better than a draw, which is better than a loss.
     * A DTM win is better than a DTZ win, which is better than a WDL win.
     * A WDL loss is better than a DTZ loss, which is better than a DTM loss.
     */
    @Override
    public final int compareTo(ProbeResult other) {
        final Type type1 = this.type;
        final Type type2 = other.type;
        final boolean none1 = type1 == Type.NONE;
        final boolean none2 = type2 == Type.NONE;
        if (none1 != none2)
            return none2 ? -1 : 1;
        if (none1)
            return 0;
        final int wdl1 = this.wdl;
        final int wdl2 = other.wdl;
        final boolean win1 = wdl1 > 0;
        final boolean win2 = wdl2 > 0;
        if (win1 != win2)
            return win2 ? 1 : -1;
        final boolean draw1 = wdl1 == 0;
        final boolean draw2 = wdl2 == 0;
        if (draw1 != draw2)
            return draw2 ? 1 : -1;
        final int score1 = this.score;
        final int score2 = other.score;
        if (win1) {
            final boolean dtm1 = type1 == Type.DTM;
            final boolean dtm2 = type2 == Type.DTM;
            if (dtm1 != dtm2)
                return dtm2 ? 1 : -1;
            if (dtm1)
                return -compareScore(wdl1, score1, wdl2, score2);
            final boolean dtz1 = type1 == Type.DTZ;
            final boolean dtz2 = type2 == Type.DTZ;
            if (dtz1 != dtz2)
                return dtz2 ? 1 : -1;
            return -compareScore(wdl1, score1, wdl2, score2);
        } else if (draw1) {
            return 0;
        } else {
            final boolean wdlType1 = type1 == Type.WDL;
            final boolean wdlType2 = type2 == Type.WDL;
            if (wdlType1 != wdlType2)
                return wdlType2 ? 1 : -1;
            if (wdlType1)
                return -compareScore(wdl1, score1, wdl2, score2);
            final boolean dtzType1 = type1 == Type.DTZ;
            final boolean dtzType2 = type2 == Type.DTZ;
            if (dtzType1 != dtzType2)
                return dtzType2 ? 1 : -1;
            return -compareScore(wdl1, score1, wdl2, score2);
        }
    }

    /** Return f((wdl1,score1)) - f((wdl2,score2)), where f(x) modifies
     * the score so that  larger values are better. */
    static int compareScore(int wdl1, int score1,
                            int wdl2, int score2) {
        final int M = 1000;
        if (wdl1 > 0)
            score1 = M - score1;
        else if (wdl1 < 0)
            score1 = -M + score1;

        if (wdl2 > 0)
            score2 = M - score2;
        else if (wdl2 < 0)
            score2 = -M + score2;
        return score1 - score2;
    }
}
