text: /* eslint-disable */
  // TODO: Remove previous line and work through linting issues at next edit
  
  'use strict';
  
  var _ = require('lodash');
  var $ = require('../../util/preconditions');
  var errors = require('../../errors');
  var BufferWriter = require('../../encoding/bufferwriter');
  var BufferUtil = require('../../util/buffer');
  var JSUtil = require('../../util/js');
  var Script = require('../../script');
  var Sighash = require('../sighash');
  var Output = require('../output');
  
  var MAXINT = 0xffffffff; // Math.pow(2, 32) - 1;
  var DEFAULT_SEQNUMBER = MAXINT;
  var DEFAULT_LOCKTIME_SEQNUMBER = MAXINT - 1;
  
  /**
   * Instantiate an Input from an Object
   *
   * @param params
   * @returns {Input|*}
   * @constructor
   */
  function Input(params) {
    if (!(this instanceof Input)) {
      return new Input(params);
    }
    if (params) {
      return this._fromObject(params);
    }
  }
  
  Input.MAXINT = MAXINT;
  Input.DEFAULT_SEQNUMBER = DEFAULT_SEQNUMBER;
  Input.DEFAULT_LOCKTIME_SEQNUMBER = DEFAULT_LOCKTIME_SEQNUMBER;
  
  Object.defineProperty(Input.prototype, 'script', {
    configurable: false,
    enumerable: true,
    get: function() {
      if (this.isNull()) {
        return null;
      }
      if (!this._script) {
        this._script = new Script(this._scriptBuffer);
        this._script._isInput = true;
      }
      return this._script;
    }
  });
  
  /**
   * Instantiate an Input from an Object
   *
   * @param {Object} obj - An Object containing satoshis and script
   * @returns {Input} A instance of an Input
   */
  Input.fromObject = function(obj) {
    $.checkArgument(_.isObject(obj));
    var input = new Input();
    return input._fromObject(obj);
  };
  
  Input.prototype._fromObject = function(params) {
    var prevTxId;
    if (_.isString(params.prevTxId) && JSUtil.isHexa(params.prevTxId)) {
      prevTxId = Buffer.from(params.prevTxId, 'hex');
    } else {
      prevTxId = params.prevTxId;
    }
    this.output = params.output ?
      (params.output instanceof Output ? params.output : new Output(params.output)) : undefined;
    this.prevTxId = prevTxId || params.txidbuf;
    this.outputIndex = _.isUndefined(params.outputIndex) ? params.txoutnum : params.outputIndex;
    this.sequenceNumber = _.isUndefined(params.sequenceNumber) ?
      (_.isUndefined(params.seqnum) ? DEFAULT_SEQNUMBER : params.seqnum) : params.sequenceNumber;
    if (_.isUndefined(params.script) && _.isUndefined(params.scriptBuffer)) {
      throw new errors.Transaction.Input.MissingScript();
    }
    this.setScript(params.scriptBuffer || params.script);
    return this;
  };
  
  /**
   * @function
   * @returns {Object} A plain object with the input information
   */
  Input.prototype.toObject = Input.prototype.toJSON = function toObject() {
    var obj = {
      prevTxId: this.prevTxId.toString('hex'),
      outputIndex: this.outputIndex,
      sequenceNumber: this.sequenceNumber,
      script: this._scriptBuffer.toString('hex'),
    };
    // add human readable form if input contains valid script
    if (this.script) {
      obj.scriptString = this.script.toString();
    }
    if (this.output) {
      obj.output = this.output.toObject();
    }
    return obj;
  };
  
  /**
   * Set a script from a Buffer
   * Replace any previously set script
   *
   * @param {Buffer} br
   * @return {Input}
   */
  Input.fromBufferReader = function(br) {
    var input = new Input();
    input.prevTxId = br.readReverse(32);
    input.outputIndex = br.readUInt32LE();
    input._scriptBuffer = br.readVarLengthBuffer();
    input.sequenceNumber = br.readUInt32LE();
    // TODO: return different classes according to which input it is
    // e.g: CoinbaseInput, PublicKeyHashInput, MultiSigScriptHashInput, etc.
    return input;
  };
  
  /**
   * Will return a BufferWriter instance with Input representation as value
   * @params {BufferWriter} writer?
   * @returns {BufferWriter} writer
   */
  Input.prototype.toBufferWriter = function(writer) {
    if (!writer) {
      writer = new BufferWriter();
    }
    writer.writeReverse(this.prevTxId);
    writer.writeUInt32LE(this.outputIndex);
    var script = this._scriptBuffer;
    writer.writeVarintNum(script.length);
    writer.write(script);
    writer.writeUInt32LE(this.sequenceNumber);
    return writer;
  };
  
  /**
   * Set script from a Script, a buffer or it's String representation.
   * Replace any previously set script
   *
   * @param {Script|Buffer|String} script
   * @return {Input}
   */
  Input.prototype.setScript = function(script) {
    this._script = null;
    if (script instanceof Script) {
      this._script = script;
      this._script._isInput = true;
      this._scriptBuffer = script.toBuffer();
    } else if (JSUtil.isHexa(script)) {
      // hex string script
      this._scriptBuffer = Buffer.from(script, 'hex');
    } else if (_.isString(script)) {
      // human readable string script
      this._script = new Script(script);
      this._script._isInput = true;
      this._scriptBuffer = this._script.toBuffer();
    } else if (BufferUtil.isBuffer(script)) {
      // buffer script
      this._scriptBuffer = Buffer.from(script);
    } else {
      throw new TypeError('Invalid argument type: script');
    }
    return this;
  };
  
  /**
   * Retrieve signatures for the provided PrivateKey.
   *
   * @param {Transaction} transaction - the transaction to be signed
   * @param {PrivateKey} privateKey - the private key to use when signing
   * @param {number} inputIndex - the index of this input in the provided transaction
   * @param {number} sigType - defaults to Signature.SIGHASH_ALL
   * @param {Buffer} addressHash - if provided, don't calculate the hash of the
   *     public key associated with the private key provided
   * @abstract
   */
  Input.prototype.getSignatures = function() {
    throw new errors.AbstractMethodInvoked(
      'Trying to sign unsupported output type (only P2PKH and P2SH multisig inputs are supported)' +
      ' for input: ' + JSON.stringify(this)
    );
  };
  
  Input.prototype.isFullySigned = function() {
    throw new errors.AbstractMethodInvoked('Input#isFullySigned');
  };
  
  /**
   * Answer if the input is final based on it's sequence number
   * @return {boolean}
   */
  Input.prototype.isFinal = function() {
    return this.sequenceNumber !== 4294967295;
  };
  
  Input.prototype.addSignature = function() {
    throw new errors.AbstractMethodInvoked('Input#addSignature');
  };
  
  Input.prototype.clearSignatures = function() {
    throw new errors.AbstractMethodInvoked('Input#clearSignatures');
  };
  
  /**
   * Verifies the signature and returns if it's valid or not
   *
   * @param {Transaction} transaction
   * @param {Object} signature
   * @return {boolean}
   */
  Input.prototype.isValidSignature = function(transaction, signature) {
    // FIXME: Refactor signature so this is not necessary
    signature.signature.nhashtype = signature.sigtype;
    return Sighash.verify(
      transaction,
      signature.signature,
      signature.publicKey,
      signature.inputIndex,
      this.output.script
    );
  };
  
  /**
   * @returns {Boolean} true if this is a coinbase input (represents no input)
   */
  Input.prototype.isNull = function() {
    return this.prevTxId.toString('hex') === '0000000000000000000000000000000000000000000000000000000000000000' &&
      this.outputIndex === 0xffffffff;
  };
  
  Input.prototype._estimateSize = function() {
    return this.toBufferWriter().toBuffer().length;
  };
  
  module.exports = Input;
  