/*
 * Copyright 2019 Distributed Systems Group
 * <p>
 * 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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 simblock.node.consensus;

import static simblock.simulator.Main.random;

import java.math.BigInteger;
import simblock.block.Block;
import simblock.block.SamplePoSBlock;
import simblock.node.Node;
import simblock.task.SampleStakingTask;

/**
 * The type Sample proof of stake.
 */
@SuppressWarnings("unused")
public class SampleProofOfStake extends AbstractConsensusAlgo {
  /**
   * Instantiates a new Sample proof of stake.
   *
   * @param selfNode the self node
   */
  public SampleProofOfStake(Node selfNode) {
    super(selfNode);
  }

  @Override
  public SampleStakingTask minting() {
    Node selfNode = this.getSelfNode();
    SamplePoSBlock parent = (SamplePoSBlock) selfNode.getBlock();
    BigInteger difficulty = parent.getNextDifficulty();
    double p = parent.getCoinage(selfNode).getCoinage().doubleValue() / difficulty.doubleValue();
    double u = random.nextDouble();
    return p <= Math.pow(2, -53) ? null : new SampleStakingTask(selfNode,
                                                                (long) (Math.log(u) / Math.log(
                                                                    1.0 - p) * 1000), difficulty
    );
  }

  @SuppressWarnings("CheckStyle")
  @Override
  public boolean isReceivedBlockValid(Block receivedBlock, Block currentBlock) {
    if (!(receivedBlock instanceof SamplePoSBlock)) {
      return false;
    }
    SamplePoSBlock recPoSBlock = (SamplePoSBlock) receivedBlock;
    SamplePoSBlock currPoSBlock = (SamplePoSBlock) currentBlock;
    int receivedBlockHeight = receivedBlock.getHeight();
    SamplePoSBlock receivedBlockParent = receivedBlockHeight == 0 ? null :
        (SamplePoSBlock) receivedBlock.getBlockWithHeight(receivedBlockHeight - 1);

    //TODO - dangerous to split due to short circuit operators being used, refactor?
    return (
        receivedBlockHeight == 0 ||
            recPoSBlock.getDifficulty().compareTo(receivedBlockParent.getNextDifficulty()) >= 0
    ) && (
        currentBlock == null ||
            recPoSBlock.getTotalDifficulty().compareTo(currPoSBlock.getTotalDifficulty()) > 0
    );
  }

  @Override
  public SamplePoSBlock genesisBlock() {
    return SamplePoSBlock.genesisBlock(this.getSelfNode());
  }
}