/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * The OpenSearch Contributors require contributions made to
 * this file be licensed under the Apache-2.0 license or a
 * compatible open source license.
 */

/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * Modifications Copyright OpenSearch Contributors. See
 * GitHub history for details.
 */

package org.opensearch.benchmark.time;

import org.opensearch.common.Rounding;
import org.opensearch.common.rounding.DateTimeUnit;
import org.opensearch.common.time.DateUtils;
import org.opensearch.common.unit.TimeValue;
import org.joda.time.DateTimeZone;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;

import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.concurrent.TimeUnit;

import static org.opensearch.common.Rounding.DateTimeUnit.DAY_OF_MONTH;
import static org.opensearch.common.Rounding.DateTimeUnit.MONTH_OF_YEAR;
import static org.opensearch.common.Rounding.DateTimeUnit.QUARTER_OF_YEAR;
import static org.opensearch.common.Rounding.DateTimeUnit.YEAR_OF_CENTURY;

@Fork(3)
@Warmup(iterations = 10)
@Measurement(iterations = 10)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@SuppressWarnings("unused") // invoked by benchmarking framework
public class RoundingBenchmark {

    private final ZoneId zoneId = ZoneId.of("Europe/Amsterdam");
    private final DateTimeZone timeZone = DateUtils.zoneIdToDateTimeZone(zoneId);

    private long timestamp = 1548879021354L;

    private final org.opensearch.common.rounding.Rounding jodaRounding = org.opensearch.common.rounding.Rounding.builder(
        DateTimeUnit.HOUR_OF_DAY
    ).timeZone(timeZone).build();
    private final Rounding javaRounding = Rounding.builder(Rounding.DateTimeUnit.HOUR_OF_DAY).timeZone(zoneId).build();

    @Benchmark
    public long timeRoundingDateTimeUnitJoda() {
        return jodaRounding.round(timestamp);
    }

    @Benchmark
    public long timeRoundingDateTimeUnitJava() {
        return javaRounding.round(timestamp);
    }

    private final org.opensearch.common.rounding.Rounding jodaDayOfMonthRounding = org.opensearch.common.rounding.Rounding.builder(
        DateTimeUnit.DAY_OF_MONTH
    ).timeZone(timeZone).build();
    private final Rounding javaDayOfMonthRounding = Rounding.builder(DAY_OF_MONTH).timeZone(zoneId).build();

    @Benchmark
    public long timeRoundingDateTimeUnitDayOfMonthJoda() {
        return jodaDayOfMonthRounding.round(timestamp);
    }

    @Benchmark
    public long timeRoundingDateTimeUnitDayOfMonthJava() {
        return javaDayOfMonthRounding.round(timestamp);
    }

    private final org.opensearch.common.rounding.Rounding timeIntervalRoundingJoda = org.opensearch.common.rounding.Rounding.builder(
        TimeValue.timeValueMinutes(60)
    ).timeZone(timeZone).build();
    private final Rounding timeIntervalRoundingJava = Rounding.builder(TimeValue.timeValueMinutes(60)).timeZone(zoneId).build();

    @Benchmark
    public long timeIntervalRoundingJava() {
        return timeIntervalRoundingJava.round(timestamp);
    }

    @Benchmark
    public long timeIntervalRoundingJoda() {
        return timeIntervalRoundingJoda.round(timestamp);
    }

    private final org.opensearch.common.rounding.Rounding timeUnitRoundingUtcDayOfMonthJoda = org.opensearch.common.rounding.Rounding
        .builder(DateTimeUnit.DAY_OF_MONTH)
        .timeZone(DateTimeZone.UTC)
        .build();
    private final Rounding timeUnitRoundingUtcDayOfMonthJava = Rounding.builder(DAY_OF_MONTH).timeZone(ZoneOffset.UTC).build();

    @Benchmark
    public long timeUnitRoundingUtcDayOfMonthJava() {
        return timeUnitRoundingUtcDayOfMonthJava.round(timestamp);
    }

    @Benchmark
    public long timeUnitRoundingUtcDayOfMonthJoda() {
        return timeUnitRoundingUtcDayOfMonthJoda.round(timestamp);
    }

    private final org.opensearch.common.rounding.Rounding timeUnitRoundingUtcQuarterOfYearJoda = org.opensearch.common.rounding.Rounding
        .builder(DateTimeUnit.QUARTER)
        .timeZone(DateTimeZone.UTC)
        .build();
    private final Rounding timeUnitRoundingUtcQuarterOfYearJava = Rounding.builder(QUARTER_OF_YEAR).timeZone(ZoneOffset.UTC).build();

    @Benchmark
    public long timeUnitRoundingUtcQuarterOfYearJava() {
        return timeUnitRoundingUtcQuarterOfYearJava.round(timestamp);
    }

    @Benchmark
    public long timeUnitRoundingUtcQuarterOfYearJoda() {
        return timeUnitRoundingUtcQuarterOfYearJoda.round(timestamp);
    }

    private final org.opensearch.common.rounding.Rounding timeUnitRoundingUtcMonthOfYearJoda = org.opensearch.common.rounding.Rounding
        .builder(DateTimeUnit.MONTH_OF_YEAR)
        .timeZone(DateTimeZone.UTC)
        .build();
    private final Rounding timeUnitRoundingUtcMonthOfYearJava = Rounding.builder(MONTH_OF_YEAR).timeZone(ZoneOffset.UTC).build();

    @Benchmark
    public long timeUnitRoundingUtcMonthOfYearJava() {
        return timeUnitRoundingUtcMonthOfYearJava.round(timestamp);
    }

    @Benchmark
    public long timeUnitRoundingUtcMonthOfYearJoda() {
        return timeUnitRoundingUtcMonthOfYearJoda.round(timestamp);
    }

    private final org.opensearch.common.rounding.Rounding timeUnitRoundingUtcYearOfCenturyJoda = org.opensearch.common.rounding.Rounding
        .builder(DateTimeUnit.YEAR_OF_CENTURY)
        .timeZone(DateTimeZone.UTC)
        .build();
    private final Rounding timeUnitRoundingUtcYearOfCenturyJava = Rounding.builder(YEAR_OF_CENTURY).timeZone(ZoneOffset.UTC).build();

    @Benchmark
    public long timeUnitRoundingUtcYearOfCenturyJava() {
        return timeUnitRoundingUtcYearOfCenturyJava.round(timestamp);
    }

    @Benchmark
    public long timeUnitRoundingUtcYearOfCenturyJoda() {
        return timeUnitRoundingUtcYearOfCenturyJoda.round(timestamp);
    }
}
