package org.unicode.cldr.draft;

import java.util.Random;

import org.unicode.cldr.util.Timer;

import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.text.DecimalFormat;
import com.ibm.icu.text.NumberFormat;

public class GapStringTest extends TestFmwk {

    private static final int ITERATIONS = 100000;
    private static final int RANDOM_STRING_LENGTH = 9;

    public static void main(String[] args) {
        new GapStringTest().run(args);
    }

    public static void TestCorrectness() {
        GapString a = new GapString();
        StringBuilder b = new StringBuilder();

        for (int iteration = 0; iteration < ITERATIONS; ++iteration) {
            int randomPos = random.nextInt(b.length() + 1);
            String randomString;
            switch (random.nextInt(4)) {
            case 0: // delete
                int randomEnd = randomPos + random.nextInt(b.length() - randomPos + 1);
                b.delete(randomPos, randomEnd);
                a.delete(randomPos, randomEnd);
                assertEqual(a, b);
                break;
            case 1: // insert
                char randomChar = (char) ('a' + random.nextInt(26));
                b.insert(randomPos, randomChar);
                a.insert(randomPos, randomChar);
                assertEqual(a, b);
                break;
            case 2: // insert string
                randomString = getRandomString(RANDOM_STRING_LENGTH);
                b.insert(randomPos, randomString);
                a.insert(randomPos, randomString);
                assertEqual(a, b);
                break;
            case 3: // insert char sequence (different code path)
                StringBuffer randomStringBuffer = new StringBuffer(getRandomString(RANDOM_STRING_LENGTH));
                b.insert(randomPos, randomStringBuffer);
                a.insert(randomPos, randomStringBuffer);
                assertEqual(a, b);
                break;
            case 4: // append string
                randomString = getRandomString(RANDOM_STRING_LENGTH);
                b.append(randomString);
                a.append(randomString);
                assertEqual(a, b);
                break;

            }
        }
    }

    static DecimalFormat percent = (DecimalFormat) NumberFormat.getPercentInstance();
    private static Random random = new Random(0);
    static {
        percent.setMaximumFractionDigits(6);
        percent.setPositivePrefix("+");
    }

    public static void TestTimeDeleteInsert() {
        checkTime(TimingStyle.fixed);
    }

    public static void TestTimeRandomDeleteInsert() {
        checkTime(TimingStyle.randomStart);
    }

    public static void TestTimeAppend() {
        checkTime(TimingStyle.append);
    }

    enum TimingStyle {
        fixed, randomStart, append
    }

    private static void checkTime(TimingStyle timingStyle) {
        GapString a = new GapString("abcdefghijklmonpqrstuvwxyz");
        StringBuilder b = new StringBuilder("abcdefghijklmonpqrstuvwxyz");
        double[] randomStarts = new double[256];
        double[] randomInserts = new double[256];
        if (timingStyle == TimingStyle.randomStart) {
            for (int i = 0; i < randomStarts.length; ++i) {
                randomStarts[i] = random.nextDouble();
                randomInserts[i] = random.nextDouble();
            }
        }
        Timer timer = new Timer();

        timer.start();
        for (int i = 0; i < ITERATIONS; ++i) {
            switch (timingStyle) {
            case append:
                a.append("!@#$%X");
                break;
            case randomStart:
            case fixed:
                int deletePos = 5;
                int insertPos = 5;
                if (timingStyle == TimingStyle.randomStart) {
                    final int length = a.length() - 5;
                    deletePos = (int) (length * randomStarts[i & 0xFF]);
                    insertPos = (int) (length * randomInserts[i & 0xFF]);
                }
                a.delete(deletePos, deletePos + 5);
                a.insert(insertPos, "!@#$%X");
            }
        }
        timer.stop();
        long gapDuration = timer.getDuration();

        timer.start();
        for (int i = 0; i < ITERATIONS; ++i) {
            switch (timingStyle) {
            case append:
                b.append("!@#$%X");
                break;
            case randomStart:
            case fixed:
                int deletePos = 5;
                int insertPos = 5;
                if (timingStyle == TimingStyle.randomStart) {
                    final int length = b.length() - 5;
                    deletePos = (int) (length * randomStarts[i & 0xFF]);
                    insertPos = (int) (length * randomInserts[i & 0xFF]);
                }
                b.delete(deletePos, deletePos + 5);
                b.insert(insertPos, "!@#$%X");
            }
        }
        timer.stop();
        long builderDuration = timer.getDuration();
        assertEqual(a, b);
        System.out.println("\tGap - Builder% =\t" + percent.format(gapDuration * 1.0 / builderDuration - 1.0));
    }

    private static String getRandomString(int maxLength) {
        StringBuilder result = new StringBuilder();
        for (int i = random.nextInt(maxLength); i >= 0; --i) {
            result.append((char) ('a' + random.nextInt(26)));
        }
        return result.toString();
    }

    private static void assertEqual(CharSequence a, CharSequence b) {
        if (!a.equals(b)) {
            a.equals(b);
            throw new IllegalArgumentException();
        }
        if (!b.toString().equals(a.toString())) {
            b.equals(a.toString());
            throw new IllegalArgumentException();
        }
    }
}
