/*
 * Copyright 2018 ConsenSys AG.
 *
 * Licensed 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.
 */
package tech.pegasys.pantheon.ethereum.vm.operations;

import tech.pegasys.pantheon.ethereum.mainnet.ConstantinopleFixGasCalculator;
import tech.pegasys.pantheon.ethereum.vm.BlockHashLookup;
import tech.pegasys.pantheon.ethereum.vm.MessageFrame;
import tech.pegasys.pantheon.util.bytes.Bytes32;
import tech.pegasys.pantheon.util.uint.UInt256;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;

@State(Scope.Thread)
public class BlockHashOperationBenchmark {

  @Param({
    "1", // Worst-case scenario
    "125", // Must iterate up the chain
    "255" // Hash available directly via current header's parentHash
  })
  public long blockNumber;

  private OperationBenchmarkHelper operationBenchmarkHelper;
  private BlockHashOperation operation;
  private MessageFrame frame;

  @Setup
  public void prepare() throws Exception {
    operationBenchmarkHelper = OperationBenchmarkHelper.create();
    operation = new BlockHashOperation(new ConstantinopleFixGasCalculator());
    frame = operationBenchmarkHelper.createMessageFrame();
  }

  @TearDown
  public void cleanUp() throws Exception {
    operationBenchmarkHelper.cleanUp();
  }

  @Benchmark
  public Bytes32 executeOperation() {
    frame.pushStackItem(UInt256.of(blockNumber).getBytes());
    operation.execute(frame);
    return frame.popStackItem();
  }

  @Benchmark
  public Bytes32 executeOperationWithEmptyHashCache() {
    final MessageFrame cleanFrame =
        operationBenchmarkHelper
            .createMessageFrameBuilder()
            .blockHashLookup(new BlockHashLookup(frame.getBlockHeader(), frame.getBlockchain()))
            .build();
    cleanFrame.pushStackItem(UInt256.of(blockNumber).getBytes());
    operation.execute(cleanFrame);
    return cleanFrame.popStackItem();
  }
}
