# Source: src/main/js/daemon/model/MoneroBlockHeader.js

    const assert = require("assert");
    const BigInteger = require("../../common/biginteger").BigInteger;
    const GenUtils = require("../../common/GenUtils");

    /**
     * Models a Monero block header which contains information about the block.
     *
     * @class
     */
    class MoneroBlockHeader {

      /**
       * Construct the model.
       *
       * @param {MoneroBlockHeader|object} state is existing state to initialize from (optional)
       */
      constructor(state) {

        // initialize internal state
        if (!state) state = {};
        else if (state instanceof MoneroBlockHeader) state = state.toJson();
        else if (typeof state === "object") state = Object.assign({}, state);
        else throw new MoneroError("state must be a MoneroBlockHeader or JavaScript object");
        this.state = state;

        // deserialize BigIntegers
        if (state.difficulty !== undefined && !(state.difficulty instanceof BigInteger)) state.difficulty = BigInteger.parse(state.difficulty);
        if (state.cumulativeDifficulty !== undefined && !(state.cumulativeDifficulty instanceof BigInteger)) state.cumulativeDifficulty = BigInteger.parse(state.cumulativeDifficulty);
        if (state.reward !== undefined && !(state.reward instanceof BigInteger)) state.reward = BigInteger.parse(state.reward);
      }

      copy() {
        return new MoneroBlockHeader(this);
      }

      toJson() {
        let json = Object.assign({}, this.state);
        if (this.getDifficulty()) json.difficulty = this.getDifficulty().toString();
        if (this.getCumulativeDifficulty()) json.cumulativeDifficulty = this.getCumulativeDifficulty().toString();
        if (this.getReward()) json.reward = this.getReward().toString();
        return json;
      }

      getHash() {
        return this.state.hash;
      }

      setHash(hash) {
        this.state.hash = hash;
        return this;
      }

      /**
       * Return the block's height which is the total number of blocks that have occurred before.
       *
       * @return {number} the block's height
       */
      getHeight() {
        return this.state.height;
      }

      /**
       * Set the block's height which is the total number of blocks that have occurred before.
       *
       * @param {number} height is the block's height to set
       * @return {MoneroBlockHeader} a reference to this header for chaining
       */
      setHeight(height) {
        this.state.height = height;
        return this;
      }

      getTimestamp() {
        return this.state.timestamp;
      }

      setTimestamp(timestamp) {
        this.state.timestamp = timestamp;
        return this;
      }

      getSize() {
        return this.state.size;
      }

      setSize(size) {
        this.state.size = size;
        return this;
      }

      getWeight() {
        return this.state.weight;
      }

      setWeight(weight) {
        this.state.weight = weight;
        return this;
      }

      getLongTermWeight() {
        return this.state.longTermWeight;
      }

      setLongTermWeight(longTermWeight) {
        this.state.longTermWeight = longTermWeight;
        return this;
      }

      getDepth() {
        return this.state.depth;
      }

      setDepth(depth) {
        this.state.depth = depth;
        return this;
      }

      getDifficulty() {
        return this.state.difficulty;
      }

      setDifficulty(difficulty) {
        this.state.difficulty = difficulty;
        return this;
      }

      getCumulativeDifficulty() {
        return this.state.cumulativeDifficulty;
      }

      setCumulativeDifficulty(cumulativeDifficulty) {
        this.state.cumulativeDifficulty = cumulativeDifficulty;
        return this;
      }

      getMajorVersion() {
        return this.state.majorVersion;
      }

      setMajorVersion(majorVersion) {
        this.state.majorVersion = majorVersion;
        return this;
      }

      getMinorVersion() {
        return this.state.minorVersion;
      }

      setMinorVersion(minorVersion) {
        this.state.minorVersion = minorVersion;
        return this;
      }

      getNonce() {
        return this.state.nonce;
      }

      setNonce(nonce) {
        this.state.nonce = nonce;
        return this;
      }

      getMinerTxHash() {
        return this.state.minerTxHash;
      }

      setMinerTxHash(minerTxHash) {
        this.state.minerTxHash = minerTxHash;
        return this;
      }

      getNumTxs() {
        return this.state.numTxs;
      }

      setNumTxs(numTxs) {
        this.state.numTxs = numTxs;
        return this;
      }

      getOrphanStatus() {
        return this.state.orphanStatus;
      }

      setOrphanStatus(orphanStatus) {
        this.state.orphanStatus = orphanStatus;
        return this;
      }

      getPrevHash() {
        return this.state.prevHash;
      }

      setPrevHash(prevHash) {
        this.state.prevHash = prevHash;
        return this;
      }

      getReward() {
        return this.state.reward;
      }

      setReward(reward) {
        this.state.reward = reward;
        return this;
      }

      getPowHash() {
        return this.state.powHash;
      }

      setPowHash(powHash) {
        this.state.powHash = powHash;
        return this;
      }

      merge(header) {
        assert(header instanceof MoneroBlockHeader);
        if (this === header) return this;
        this.setHash(GenUtils.reconcile(this.getHash(), header.getHash()));
        this.setHeight(GenUtils.reconcile(this.getHeight(), header.getHeight(), {resolveMax: true}));  // height can increase
        this.setTimestamp(GenUtils.reconcile(this.getTimestamp(), header.getTimestamp(), {resolveMax: true}));  // block timestamp can increase
        this.setSize(GenUtils.reconcile(this.getSize(), header.getSize()));
        this.setWeight(GenUtils.reconcile(this.getWeight(), header.getWeight()));
        this.setDepth(GenUtils.reconcile(this.getDepth(), header.getDepth()));
        this.setDifficulty(GenUtils.reconcile(this.getDifficulty(), header.getDifficulty()));
        this.setCumulativeDifficulty(GenUtils.reconcile(this.getCumulativeDifficulty(), header.getCumulativeDifficulty()));
        this.setMajorVersion(GenUtils.reconcile(this.getMajorVersion(), header.getMajorVersion()));
        this.setMinorVersion(GenUtils.reconcile(this.getMinorVersion(), header.getMinorVersion()));
        this.setNonce(GenUtils.reconcile(this.getNonce(), header.getNonce()));
        this.setMinerTxHash(GenUtils.reconcile(this.getMinerTxHash(), header.getMinerTxHash()));
        this.setNumTxs(GenUtils.reconcile(this.getNumTxs(), header.getNumTxs()));
        this.setOrphanStatus(GenUtils.reconcile(this.getOrphanStatus(), header.getOrphanStatus()));
        this.setPrevHash(GenUtils.reconcile(this.getPrevHash(), header.getPrevHash()));
        this.setReward(GenUtils.reconcile(this.getReward(), header.getReward()));
        this.setPowHash(GenUtils.reconcile(this.getPowHash(), header.getPowHash()));
        return this;
      }

      toString(indent = 0) {
        let str = "";
        str += GenUtils.kvLine("Hash", this.getHash(), indent);
        str += GenUtils.kvLine("Height", this.getHeight(), indent);
        str += GenUtils.kvLine("Timestamp", this.getTimestamp(), indent);
        str += GenUtils.kvLine("Size", this.getSize(), indent);
        str += GenUtils.kvLine("Weight", this.getWeight(), indent);
        str += GenUtils.kvLine("Depth", this.getDepth(), indent);
        str += GenUtils.kvLine("Difficulty", this.getDifficulty(), indent);
        str += GenUtils.kvLine("Cumulative difficulty", this.getCumulativeDifficulty(), indent);
        str += GenUtils.kvLine("Major version", this.getMajorVersion(), indent);
        str += GenUtils.kvLine("Minor version", this.getMinorVersion(), indent);
        str += GenUtils.kvLine("Nonce", this.getNonce(), indent);
        str += GenUtils.kvLine("Miner tx hash", this.getMinerTxHash(), indent);
        str += GenUtils.kvLine("Num txs", this.getNumTxs(), indent);
        str += GenUtils.kvLine("Orphan status", this.getOrphanStatus(), indent);
        str += GenUtils.kvLine("Prev hash", this.getPrevHash(), indent);
        str += GenUtils.kvLine("Reward", this.getReward(), indent);
        str += GenUtils.kvLine("Pow hash", this.getPowHash(), indent);
        return str[str.length - 1] === "\n" ? str.slice(0, str.length - 1) : str  // strip last newline
      }
    }

    module.exports = MoneroBlockHeader;