import { BufferReader } from '../buffer/BufferReader';
import { BufferWriter } from '../buffer/BufferWriter';
import { BlockHeader } from './BlockHeader';
import { MerkleBlock } from './MerkleBlock';
import { Transaction } from '../transaction/Transaction';

export namespace Block {
  /**
   * @typedef {Object} Block.fromObjectParams
   * @property {Transaction.toObjectParams[]} transactions
   * @property {BlockHeader.toObjectParams} header
   */
  export type fromObjectParams = {
    transactions: Transaction[];
    header: BlockHeader.toObjectParams;
  };
  /**
   * @typedef {Object} Block.toObjectParams
   * @property {string} string
   * @property {number} version
   * @property {string} prevHash
   * @property {string} merkleRoot
   * @property {number} time
   * @property {number} bits
   * @property {number} nonce
   */
  export type toObjectParams = {
    hash: string;
    version: number;
    prevHash: string;
    merkleRoot: string;
    time: number;
    bits: number;
    nonce: number;
  };

  export type Values = {
    START_OF_BLOCK: number;
    NULL_HASH: Buffer;
  };

  export { BlockHeader };
  export { MerkleBlock };
}

/**
 * Instantiate a Block from a Buffer, JSON object, or Object with
 * the properties of the Block
 *
 * @param {Buffer|Block.fromObjectParams} arg - A Buffer, JSON string, or Object
 * @returns {Block}
 * @constructor
 */
export class Block {
  constructor(arg: any);

  id: string;
  hash: string;
  header: BlockHeader;
  transactions: Transaction[];
  /**
   * @property {Block.fromObjectParams} obj - A plain JavaScript object
   * @returns {Block} - An instance of block
   */
  static fromObject(obj: Block.fromObjectParams): Block;

  /**
   * @param {BufferReader} br A buffer reader of the block
   * @returns {Block} - An instance of block
   */
  static fromBufferReader(br: BufferReader): Block;

  /**
   * @param {Buffer} buf A buffer of the block
   * @returns {Block} - An instance of block
   */
  static fromBuffer(buf: Buffer): Block;

  /**
   * @param {string} str - A hex encoded string of the block
   * @returns {Block} - A hex encoded string of the block
   */
  static fromString(str: string): Block;

  /**
   * @param {Buffer} data Raw block binary data or buffer
   * @returns {Block} - An instance of block
   */
  static fromRawBlock(data: Buffer): Block;

  /**
   * @function
   * @returns {Block.toObjectParams} - A plain object with the block properties
   */
  toObject(): {
    header: BlockHeader.toObjectParams;
    transactions: any[];
  };

  /**
   * @function
   * @returns {Object} - A plain object with the block properties
   */
  toJSON(): any;

  /**
   * @returns {Buffer} - A buffer of the block
   */
  toBuffer(): Buffer;

  /**
   * @returns {string} - A hex encoded string of the block
   */
  toString(): string;

  /**
   * @param {BufferWriter} bw - An existing instance of BufferWriter
   * @returns {BufferWriter} - An instance of BufferWriter representation of the Block
   */
  toBufferWriter(bw: BufferWriter): BufferWriter;

  /**
   * Will iterate through each transaction and return an array of hashes
   * @returns {Buffer[]} - An array with transaction hashes
   */
  getTransactionHashes(): Buffer[];

  /**
   * Will build a merkle tree of all the transactions, ultimately arriving at
   * a single point, the merkle root.
   * @link https://en.bitcoin.it/wiki/Protocol_specification#Merkle_Trees
   * @returns {Buffer[]} - An array with each level of the tree after the other.
   */
  getMerkleTree(): Buffer[];

  /**
   * Calculates the merkleRoot from the transactions.
   * @returns {Buffer} - A buffer of the merkle root hash
   */
  getMerkleRoot(): Buffer;

  /**
   * Verifies that the transactions in the block match the header merkle root
   * @returns {Boolean} - If the merkle roots match
   */
  validMerkleRoot(): boolean;

  /**
   * @returns {Buffer} - The little endian hash buffer of the header
   */
  _getHash(): Buffer;

  /**
   * @returns {string} - A string formatted for the console
   */
  inspect(): string;
}
