// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
/*
*******************************************************************************
*   Copyright (C) 2001-2013, International Business Machines
*   Corporation and others.  All Rights Reserved.
*******************************************************************************
*/

package com.ibm.icu.dev.test.bidi;

import java.util.Arrays;

import org.junit.Test;

import com.ibm.icu.impl.Utility;
import com.ibm.icu.text.Bidi;

/**
 * Regression test for the basic "inverse" Bidi mode.
 *
 * ported from C by Lina Kemmel, Matitiahu Allouche
 */

public class TestInverse extends BidiFmwk {

    private int countRoundtrips = 0;
    private int countNonRoundtrips = 0;

    static final String[] testCases = {
        "\u006c\u0061\u0028\u0074\u0069\u006e\u0020\u05d0\u05d1\u0029\u05d2\u05d3",
        "\u006c\u0061\u0074\u0020\u05d0\u05d1\u05d2\u0020\u0031\u0032\u0033",
        "\u006c\u0061\u0074\u0020\u05d0\u0028\u05d1\u05d2\u0020\u0031\u0029\u0032\u0033",
        "\u0031\u0032\u0033\u0020\u05d0\u05d1\u05d2\u0020\u0034\u0035\u0036",
        "\u0061\u0062\u0020\u0061\u0062\u0020\u0661\u0662"
    };

    @Test
    public void testInverse() {
        Bidi bidi;
        int i;

        logln("\nEntering TestInverse\n");
        bidi = new Bidi();
        log("inverse Bidi: testInverse(L) with " + testCases.length +
            " test cases ---\n");
        for(i = 0; i < testCases.length; ++i) {
            logln("Testing case " + i);
            _testInverseBidi(bidi, testCases[i], Bidi.DIRECTION_LEFT_TO_RIGHT);
        }

        log("inverse Bidi: testInverse(R) with " + testCases.length +
            " test cases ---\n");
        for (i = 0; i < testCases.length; ++i) {
            logln("Testing case " + i);
            _testInverseBidi(bidi, testCases[i], Bidi.DIRECTION_RIGHT_TO_LEFT);
        }

        _testManyInverseBidi(bidi, Bidi.DIRECTION_LEFT_TO_RIGHT);
        _testManyInverseBidi(bidi, Bidi.DIRECTION_RIGHT_TO_LEFT);

        logln("inverse Bidi: rountrips: " + countRoundtrips +
              "   non-roundtrips: " + countNonRoundtrips);

        _testWriteReverse();

        _testManyAddedPoints();

        _testMisc();

        logln("\nExiting TestInverse\n");
    }

    private static final char[][] repeatSegments = {
        { 0x61, 0x62 },     /* L */
        { 0x5d0, 0x5d1 },   /* R */
        { 0x627, 0x628 },   /* AL */
        { 0x31, 0x32 },     /* EN */
        { 0x661, 0x662 },   /* AN */
        { 0x20, 0x20 }      /* WS (N) */
    };
    private static final int COUNT_REPEAT_SEGMENTS = 6;

    private void _testManyInverseBidi(Bidi bidi, int direction) {
        char[] text = { 0, 0, 0x20, 0, 0, 0x20, 0, 0 };
        int i, j, k;

        log("inverse Bidi: testManyInverseBiDi(" +
            (direction == Bidi.DIRECTION_LEFT_TO_RIGHT ? 'L' : 'R') +
            ") - test permutations of text snippets ---\n");
        for (i = 0; i < COUNT_REPEAT_SEGMENTS; ++i) {
            text[0] = repeatSegments[i][0];
            text[1] = repeatSegments[i][1];
            for (j = 0; j < COUNT_REPEAT_SEGMENTS; ++j) {
                text[3] = repeatSegments[j][0];
                text[4] = repeatSegments[j][1];
                for (k = 0; k < COUNT_REPEAT_SEGMENTS; ++k) {
                    text[6] = repeatSegments[k][0];
                    text[7] = repeatSegments[k][1];

                    log("inverse Bidi: testManyInverseBiDi()[" +
                        i + " " + j + " " + k + "]\n");
                    _testInverseBidi(bidi, new String(text), direction);
                }
            }
        }
    }

    private void _testInverseBidi(Bidi bidi, String src, int direction) {
        String visualLTR, logicalDest, visualDest;
        try {
            if (direction == Bidi.DIRECTION_LEFT_TO_RIGHT) {
                log("inverse Bidi: testInverse(L)\n");

                /* convert visual to logical */
                bidi.setInverse(true);
                if (!bidi.isInverse()) {
                    err("Error while doing setInverse(true)\n");
                }
                bidi.setPara(src, Bidi.LTR, null);
                if (!Arrays.equals(src.toCharArray(), bidi.getText())) {
                    err("Wrong value returned by getText\n");
                }
                if (!src.equals(bidi.getTextAsString())) {
                    err("Wrong value returned by getTextAsString\n");
                }
                logicalDest = bidi.writeReordered(Bidi.DO_MIRRORING |
                                                  Bidi.INSERT_LRM_FOR_NUMERIC);
                log("  v ");
                printUnicode(src.toCharArray(), bidi.getLevels());
                log("\n");

                /* convert back to visual LTR */
                bidi.setInverse(false);
                if (bidi.isInverse()) {
                    err("Error while doing setInverse(false)\n");
                }
                bidi.setPara(logicalDest, Bidi.LTR, null);
                visualDest = bidi.writeReordered(Bidi.DO_MIRRORING |
                                                 Bidi.REMOVE_BIDI_CONTROLS);
            } else {
                logln("inverse Bidi: testInverse(R)\n");

                /* reverse visual from RTL to LTR */
                visualLTR = Bidi.writeReverse(src, 0);
                log("  vr");
                printUnicode(src.toCharArray(), null);
                log("\n");

                /* convert visual RTL to logical */
                bidi.setInverse(true);
                bidi.setPara(visualLTR, Bidi.LTR, null);
                logicalDest = bidi.writeReordered(Bidi.DO_MIRRORING |
                                                  Bidi.INSERT_LRM_FOR_NUMERIC);
                log("  vl");
                printUnicode(visualLTR.toCharArray(), bidi.getLevels());
                log("\n");

                /* convert back to visual RTL */
                bidi.setInverse(false);
                bidi.setPara(logicalDest, Bidi.LTR, null);
                visualDest = bidi.writeReordered(Bidi.DO_MIRRORING |
                             Bidi.REMOVE_BIDI_CONTROLS | Bidi.OUTPUT_REVERSE);
            }
            log("  l ");
            printUnicode(logicalDest.toCharArray(), bidi.getLevels());
            log("\n");
            log("  v ");
            printUnicode(visualDest.toCharArray(), null);
            log("\n");
        } catch (Exception e) {
            errln("\ninverse Bidi: *** failed");
            errln("   error message: " + e.getMessage());
            e.printStackTrace();
            visualDest = null;
        }

        /* check and print results */
        if (src.equals(visualDest)) {
            ++countRoundtrips;
            log(" + roundtripped\n");
        } else {
            ++countNonRoundtrips;
            log(" * did not roundtrip\n");
        }
    }

    private void _testWriteReverse() {
        /* U+064e and U+0650 are combining marks (Mn) */
        final String
            forward = "\u200f\u0627\u064e\u0650\u0020\u0028\u0031\u0029",
            reverseKeepCombining =
                "\u0029\u0031\u0028\u0020\u0627\u064e\u0650\u200f",
            reverseRemoveControlsKeepCombiningDoMirror =
                "\u0028\u0031\u0029\u0020\u0627\u064e\u0650";

        String reverse;

        /* test Bidi.writeReverse() with "interesting" options */
        try {
            reverse = Bidi.writeReverse(forward, Bidi.KEEP_BASE_COMBINING);
        } catch (Exception e) {
            errln("Failure in Bidi.writeReverse(KEEP_BASE_COMBINING)");
            reverse = null;
        }
        assertEquals("\nFailure in " + getClass().toString() +
                     " in Bidi.writeReverse", reverseKeepCombining,
                     reverse, forward, null, "KEEP_BASE_COMBINING", null);

        try {
            reverse = Bidi.writeReverse(forward, Bidi.REMOVE_BIDI_CONTROLS |
                                        Bidi.DO_MIRRORING | Bidi.KEEP_BASE_COMBINING);
        } catch (Exception e) {
            errln("Failure in Bidi.writeReverse(KEEP_BASE_COMBINING)");
        }
        assertEquals("\nFailure in " + getClass().toString() +
                     " in Bidi.writeReverse",
                     reverseRemoveControlsKeepCombiningDoMirror,
                     reverse, forward, null,
                     "REMOVE_BIDI_CONTROLS|DO_MIRRORING|KEEP_BASE_COMBINING",
                     null);
    }

    private void printUnicode(char[] chars, byte[] levels) {
        int i;

        log("{ ");
        for (i = 0; i < chars.length; ++i) {
            log("0x" + Utility.hex(chars[i]));
            if (levels != null) {
                log("." + levels[i]);
            }
            log("   ");
        }
        log(" }");
    }

    private void _testManyAddedPoints() {
        Bidi bidi = new Bidi();
        char[] text = new char[90];
        for (int i = 0; i < text.length; i+=3) {
            text[i] = 'a';
            text[i+1] = '\u05d0';
            text[i+2] = '3';
        }
        bidi.setReorderingMode(Bidi.REORDER_INVERSE_LIKE_DIRECT);
        bidi.setReorderingOptions(Bidi.OPTION_INSERT_MARKS);
        bidi.setPara(text, Bidi.LTR, null);
        String out = bidi.writeReordered(0);
        char[] expected = new char[120];
        for (int i = 0; i < expected.length; i+=4) {
            expected[i] = 'a';
            expected[i+1] = '\u05d0';
            expected[i+2] = '\u200e';
            expected[i+3] = '3';
        }
        assertEquals("\nInvalid output with many added points",
                     new String(expected), out);
    }

    private void _testMisc() {
        Bidi bidi = new Bidi();
        bidi.setInverse(true);
        bidi.setPara("   ", Bidi.RTL, null);
        String out = bidi.writeReordered(Bidi.OUTPUT_REVERSE | Bidi.INSERT_LRM_FOR_NUMERIC);
        assertEquals("\nInvalid output with RLM at both sides",
                     "\u200f   \u200f", out);
    }
}
