package org.openjdk.bench.sand;

import jdk.internal.value.ValueClass;
import jdk.internal.vm.annotation.LooselyConsistentValue;
import org.openjdk.jmh.annotations.*;

import java.util.concurrent.TimeUnit;
import java.util.function.IntFunction;

@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@State(Scope.Thread)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(jvmArgs = {"--enable-preview", "-XX:+UseArrayFlattening", "--add-exports=java.base/jdk.internal.vm.annotation=ALL-UNNAMED", "--add-exports=java.base/jdk.internal.value=ALL-UNNAMED"}, value = 3)
public class V1Bench {


    @Benchmark
    public long sharpValueFlat() {
        long l = 0;
        for (int i = 0; i < SIZE; i++) {
            var point = sharpValueFlat.get(i);
            l += point.y() + point.x();
        }
        return l;
    }

    @Benchmark
    public long sharpValueFlatArrayHoist() {
        long l = 0;
        var arr  = sharpValueFlat.array();
        for(var point : arr) {
            l += point.y() + point.x();
        }
        return l;
    }

    @Benchmark
    public long sharpValueNonFlat() {
        long l = 0;
        for (int i = 0; i < SIZE; i++) {
            var point = sharpValueNonFlat.get(i);
            l += point.y() + point.x();
        }
        return l;
    }

    @Benchmark
    public long sharpValueNonFlatArrayHoist() {
        long l = 0;
        var arr  = sharpValueNonFlat.array();
        for(var point : arr) {
            l += point.y() + point.x();
        }
        return l;
    }


    private static final int SIZE = 1_000_000;

    private final SharpBox sharpValueFlat = SharpBox.flatArray(SIZE, V1Bench::toValuePoint);
    private final SharpBox sharpValueNonFlat = SharpBox.nonFlatArray(SIZE, V1Bench::toValuePoint);

    private static ValuePoint toValuePoint(int i) {
        return new ValuePoint(i, i);
    }


    record SharpBox(ValuePoint[] array) {

        public ValuePoint get(int index) {
            return array[index];
        }

        public static SharpBox flatArray(int size, IntFunction<ValuePoint> factory) {
            ValuePoint[] ts = (ValuePoint[]) ValueClass.newNullRestrictedNonAtomicArray(ValuePoint.class, size, factory.apply(0));
            for (int i = 0; i < size; i++) {
                ts[i] = factory.apply(i);
            }
            return new SharpBox(ts);
        }

        public static SharpBox nonFlatArray(int size, IntFunction<ValuePoint> factory) {
            ValuePoint[] ts = new ValuePoint[size];
            for (int i = 0; i < size; i++) {
                ts[i] = factory.apply(i);
            }
            return new SharpBox(ts);
        }

    }

    @LooselyConsistentValue
    public value record ValuePoint(int x, int y) {
    }

}
