/* ----------------------------------------------------------------------------
 * This file was automatically generated by SWIG (http://www.swig.org).
 * Version 2.0.6
 *
 * Do not make changes to this file unless you know what you are doing--modify
 * the SWIG interface file instead.
 * ----------------------------------------------------------------------------- */

package io.humble.video;
import io.humble.ferry.*;
/**
 * A source of MediaAudio objects for a FilterGraph.
 */
public class FilterAudioSource extends FilterSource {
  // JNIHelper.swg: Start generated code
  // >>>>>>>>>>>>>>>>>>>>>>>>>>>
  /**
   * This method is only here to use some references and remove
   * a Eclipse compiler warning.
   */
  @SuppressWarnings("unused")
  private void noop()
  {
    Buffer.make(null, 1);
  }

  private volatile long swigCPtr;

  /**
   * Internal Only.
   */
  protected FilterAudioSource(long cPtr, boolean cMemoryOwn) {
    super(VideoJNI.FilterAudioSource_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected FilterAudioSource(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(VideoJNI.FilterAudioSource_SWIGUpcast(cPtr),
     cMemoryOwn, ref);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that obj is proxying for.
   *
   * @param obj The java proxy object for a native object.
   * @return The raw pointer obj is proxying for.
   */
  protected static long getCPtr(FilterAudioSource obj) {
    if (obj == null) return 0;
    return obj.getMyCPtr();
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that we're proxying for.
   *
   * @return The raw pointer we're proxying for.
   */
  protected long getMyCPtr() {
    if (swigCPtr == 0) throw new IllegalStateException("underlying native object already deleted");
    return swigCPtr;
  }

  /**
   * Create a new FilterAudioSource object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public FilterAudioSource copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new FilterAudioSource(swigCPtr, swigCMemOwn, getJavaRefCount());
  }

  /**
   * Compares two values, returning true if the underlying objects in native code are the same object.
   *
   * That means you can have two different Java objects, but when you do a comparison, you'll find out
   * they are the EXACT same object.
   *
   * @return True if the underlying native object is the same.  False otherwise.
   */
  public boolean equals(Object obj) {
    boolean equal = false;
    if (obj instanceof FilterAudioSource)
      equal = (((FilterAudioSource)obj).swigCPtr == this.swigCPtr);
    return equal;
  }

  /**
   * Get a hashable value for this object.
   *
   * @return the hashable value.
   */
  public int hashCode() {
     return (int)swigCPtr;
  }

  // <<<<<<<<<<<<<<<<<<<<<<<<<<<
  // JNIHelper.swg: End generated code

/**
 * Adds audio to this source. NOTE: If you had audio to a FilterSource

 * be careful with re-using or rewriting the underlying data. Filters will

 * try hard to avoid copying data, so if you change the data out from under

 * them unexpected results can occur.

 * @param audio the audio to add. Must be non-null and complete.

 * @throws InvalidArgument if audio is null or audio is not complete.
 */
  public void addAudio(MediaAudio audio) {
    VideoJNI.FilterAudioSource_addAudio(swigCPtr, this, MediaAudio.getCPtr(audio), audio);
  }

}


/* ----------------------------------------------------------------------------
 * This file was automatically generated by SWIG (http://www.swig.org).
 * Version 2.0.6
 *
 * Do not make changes to this file unless you know what you are doing--modify
 * the SWIG interface file instead.
 * ----------------------------------------------------------------------------- */

package io.humble.video;
import io.humble.ferry.*;
/**
 * Super class for Media Resampler objects.

 *

 * A resampler converts a type of media (e.g. Picture) to another

 * type of the same media with some parameters change (e.g. color).
 */
public class MediaResampler extends Configurable {
  // JNIHelper.swg: Start generated code
  // >>>>>>>>>>>>>>>>>>>>>>>>>>>
  /**
   * This method is only here to use some references and remove
   * a Eclipse compiler warning.
   */
  @SuppressWarnings("unused")
  private void noop()
  {
    Buffer.make(null, 1);
  }

  private volatile long swigCPtr;

  /**
   * Internal Only.
   */
  protected MediaResampler(long cPtr, boolean cMemoryOwn) {
    super(VideoJNI.MediaResampler_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected MediaResampler(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(VideoJNI.MediaResampler_SWIGUpcast(cPtr),
     cMemoryOwn, ref);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that obj is proxying for.
   *
   * @param obj The java proxy object for a native object.
   * @return The raw pointer obj is proxying for.
   */
  protected static long getCPtr(MediaResampler obj) {
    if (obj == null) return 0;
    return obj.getMyCPtr();
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that we're proxying for.
   *
   * @return The raw pointer we're proxying for.
   */
  protected long getMyCPtr() {
    if (swigCPtr == 0) throw new IllegalStateException("underlying native object already deleted");
    return swigCPtr;
  }

  /**
   * Create a new MediaResampler object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public MediaResampler copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new MediaResampler(swigCPtr, swigCMemOwn, getJavaRefCount());
  }

  /**
   * Compares two values, returning true if the underlying objects in native code are the same object.
   *
   * That means you can have two different Java objects, but when you do a comparison, you'll find out
   * they are the EXACT same object.
   *
   * @return True if the underlying native object is the same.  False otherwise.
   */
  public boolean equals(Object obj) {
    boolean equal = false;
    if (obj instanceof MediaResampler)
      equal = (((MediaResampler)obj).swigCPtr == this.swigCPtr);
    return equal;
  }

  /**
   * Get a hashable value for this object.
   *
   * @return the hashable value.
   */
  public int hashCode() {
     return (int)swigCPtr;
  }

  // <<<<<<<<<<<<<<<<<<<<<<<<<<<
  // JNIHelper.swg: End generated code

  public MediaResampler.State getState() {
    return MediaResampler.State.swigToEnum(VideoJNI.MediaResampler_getState(swigCPtr, this));
  }

/**
 * Resample in to out based on the resampler parameters.

 *

 * Resamples the in media based on the parameters set when

 * this resampler was constructed.

 *

 * @param out The media we'll resample to.  Check

 *     MediaSampled#isComplete() after the call.

 * @param in The media we'll resample from.

 *

 * @throws InvalidArgument if in our out does not match the parameters this

 *         resampler was set with.

 * @throws InvalidArgument if the sub-class of in our out is of an invalid

 *         type (e.g. passsing audio to a picture resampler).
 */
  public int resample(MediaSampled out, MediaSampled in) {
    return VideoJNI.MediaResampler_resample(swigCPtr, this, MediaSampled.getCPtr(out), out, MediaSampled.getCPtr(in), in);
  }

  public enum State {
    STATE_INITED,
    STATE_OPENED,
    STATE_ERROR,
  ;

    public final int swigValue() {
      return swigValue;
    }

    public static State swigToEnum(int swigValue) {
      State[] swigValues = State.class.getEnumConstants();
      if (swigValue < swigValues.length && swigValue >= 0 && swigValues[swigValue].swigValue == swigValue)
        return swigValues[swigValue];
      for (State swigEnum : swigValues)
        if (swigEnum.swigValue == swigValue)
          return swigEnum;
      throw new IllegalArgumentException("No enum " + State.class + " with value " + swigValue);
    }

    @SuppressWarnings("unused")
    private State() {
      this.swigValue = SwigNext.next++;
    }

    @SuppressWarnings("unused")
    private State(int swigValue) {
      this.swigValue = swigValue;
      SwigNext.next = swigValue+1;
    }

    @SuppressWarnings("unused")
    private State(State swigEnum) {
      this.swigValue = swigEnum.swigValue;
      SwigNext.next = this.swigValue+1;
    }

    private final int swigValue;

    private static class SwigNext {
      private static int next = 0;
    }
  }

}


/* ----------------------------------------------------------------------------
 * This file was automatically generated by SWIG (http://www.swig.org).
 * Version 2.0.6
 *
 * Do not make changes to this file unless you know what you are doing--modify
 * the SWIG interface file instead.
 * ----------------------------------------------------------------------------- */

package io.humble.video;
import io.humble.ferry.*;
/**
 * A class that defines metadata about audio formats.
 */
public class AudioFormat extends RefCounted {
  // JNIHelper.swg: Start generated code
  // >>>>>>>>>>>>>>>>>>>>>>>>>>>
  /**
   * This method is only here to use some references and remove
   * a Eclipse compiler warning.
   */
  @SuppressWarnings("unused")
  private void noop()
  {
    Buffer.make(null, 1);
  }

  private volatile long swigCPtr;

  /**
   * Internal Only.
   */
  protected AudioFormat(long cPtr, boolean cMemoryOwn) {
    super(VideoJNI.AudioFormat_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected AudioFormat(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(VideoJNI.AudioFormat_SWIGUpcast(cPtr),
     cMemoryOwn, ref);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that obj is proxying for.
   *
   * @param obj The java proxy object for a native object.
   * @return The raw pointer obj is proxying for.
   */
  protected static long getCPtr(AudioFormat obj) {
    if (obj == null) return 0;
    return obj.getMyCPtr();
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that we're proxying for.
   *
   * @return The raw pointer we're proxying for.
   */
  protected long getMyCPtr() {
    if (swigCPtr == 0) throw new IllegalStateException("underlying native object already deleted");
    return swigCPtr;
  }

  /**
   * Create a new AudioFormat object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public AudioFormat copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new AudioFormat(swigCPtr, swigCMemOwn, getJavaRefCount());
  }

  /**
   * Compares two values, returning true if the underlying objects in native code are the same object.
   *
   * That means you can have two different Java objects, but when you do a comparison, you'll find out
   * they are the EXACT same object.
   *
   * @return True if the underlying native object is the same.  False otherwise.
   */
  public boolean equals(Object obj) {
    boolean equal = false;
    if (obj instanceof AudioFormat)
      equal = (((AudioFormat)obj).swigCPtr == this.swigCPtr);
    return equal;
  }

  /**
   * Get a hashable value for this object.
   *
   * @return the hashable value.
   */
  public int hashCode() {
     return (int)swigCPtr;
  }

  // <<<<<<<<<<<<<<<<<<<<<<<<<<<
  // JNIHelper.swg: End generated code

/**
 * Return the name of format, or NULL if format is not

 * recognized.
 */
  public static String getName(AudioFormat.Type format) {
    return VideoJNI.AudioFormat_getName(format.swigValue());
  }

/**
 * Return a sample format corresponding to name, or SAMPLE_FMT_NONE

 * on error.
 */
  public static AudioFormat.Type getFormat(String name) {
    return AudioFormat.Type.swigToEnum(VideoJNI.AudioFormat_getFormat(name));
  }

/**
 * Return the planar<->packed alternative form of the given sample format, or

 * SAMPLE_FMT_NONE on error. If the passed sample_fmt is already in the

 * requested planar/packed format, the format returned is the same as the

 * input.
 */
  public static AudioFormat.Type getAlternateSampleFormat(AudioFormat.Type sample_fmt, boolean planar) {
    return AudioFormat.Type.swigToEnum(VideoJNI.AudioFormat_getAlternateSampleFormat(sample_fmt.swigValue(), planar));
  }

/**
 * Get the packed alternative form of the given sample format.

 *

 * If the passed sample_fmt is already in packed format, the format returned is

 * the same as the input.

 *

 * @return the packed alternative form of the given sample format or

 * AV_SAMPLE_FMT_NONE on error.
 */
  public static AudioFormat.Type getPackedSampleFormat(AudioFormat.Type sample_fmt) {
    return AudioFormat.Type.swigToEnum(VideoJNI.AudioFormat_getPackedSampleFormat(sample_fmt.swigValue()));
  }

/**
 * Get the planar alternative form of the given sample format.

 *

 * If the passed sample_fmt is already in planar format, the format returned is

 * the same as the input.

 *

 * @return the planar alternative form of the given sample format or

 * SAMPLE_FMT_NONE on error.
 */
  public static AudioFormat.Type getPlanarSampleFormat(AudioFormat.Type sample_fmt) {
    return AudioFormat.Type.swigToEnum(VideoJNI.AudioFormat_getPlanarSampleFormat(sample_fmt.swigValue()));
  }

/**
 * Return number of bytes per sample.

 *

 * @param sample_fmt the sample format

 * @return number of bytes per sample or zero if unknown for the given

 * sample format
 */
  public static int getBytesPerSample(AudioFormat.Type sample_fmt) {
    return VideoJNI.AudioFormat_getBytesPerSample(sample_fmt.swigValue());
  }

/**
 * Check if the sample format is planar.

 *

 * @param sample_fmt the sample format to inspect

 * @return 1 if the sample format is planar, 0 if it is interleaved
 */
  public static boolean isPlanar(AudioFormat.Type sample_fmt) {
    return VideoJNI.AudioFormat_isPlanar(sample_fmt.swigValue());
  }

/**
 * Get the size of a buffer in bytes that would be required to hold the

 * number of samples of audio in the given format and with the given number of channels.
 */
  public static int getBufferSizeNeeded(int numSamples, int numChannels, AudioFormat.Type format) {
    return VideoJNI.AudioFormat_getBufferSizeNeeded(numSamples, numChannels, format.swigValue());
  }

/**
 * Get the size of a plane of audio bytes that would be required to hold the

 * number of samples of audio in the given format and with the given number of channels.

 *

 * If format is packed, then this method returns the same number as #getBufferSizeNeeded(int, int, Type).

 *

 */
  public static int getDataPlaneSizeNeeded(int numSamples, int numChannels, AudioFormat.Type format) {
    return VideoJNI.AudioFormat_getDataPlaneSizeNeeded(numSamples, numChannels, format.swigValue());
  }

  /**
   * The format we use to represent audio.
   */
  public enum Type {
  /**
   * No format
   */
    SAMPLE_FMT_NONE(VideoJNI.AudioFormat_SAMPLE_FMT_NONE_get()),
  /**
   * unsigned 8 bits
   */
    SAMPLE_FMT_U8(VideoJNI.AudioFormat_SAMPLE_FMT_U8_get()),
  /**
   * signed 16 bits
   */
    SAMPLE_FMT_S16(VideoJNI.AudioFormat_SAMPLE_FMT_S16_get()),
  /**
   * signed 32 bits
   */
    SAMPLE_FMT_S32(VideoJNI.AudioFormat_SAMPLE_FMT_S32_get()),
  /**
   * float
   */
    SAMPLE_FMT_FLT(VideoJNI.AudioFormat_SAMPLE_FMT_FLT_get()),
  /**
   * double
   */
    SAMPLE_FMT_DBL(VideoJNI.AudioFormat_SAMPLE_FMT_DBL_get()),
  /**
   * unsigned 8 bits, planar
   */
    SAMPLE_FMT_U8P(VideoJNI.AudioFormat_SAMPLE_FMT_U8P_get()),
  /**
   * signed 16 bits, planar
   */
    SAMPLE_FMT_S16P(VideoJNI.AudioFormat_SAMPLE_FMT_S16P_get()),
  /**
   * signed 32 bits, planar
   */
    SAMPLE_FMT_S32P(VideoJNI.AudioFormat_SAMPLE_FMT_S32P_get()),
  /**
   * float, planar
   */
    SAMPLE_FMT_FLTP(VideoJNI.AudioFormat_SAMPLE_FMT_FLTP_get()),
  /**
   * double, planar
   */
    SAMPLE_FMT_DBLP(VideoJNI.AudioFormat_SAMPLE_FMT_DBLP_get()),
  ;

    public final int swigValue() {
      return swigValue;
    }

    public static Type swigToEnum(int swigValue) {
      Type[] swigValues = Type.class.getEnumConstants();
      if (swigValue < swigValues.length && swigValue >= 0 && swigValues[swigValue].swigValue == swigValue)
        return swigValues[swigValue];
      for (Type swigEnum : swigValues)
        if (swigEnum.swigValue == swigValue)
          return swigEnum;
      throw new IllegalArgumentException("No enum " + Type.class + " with value " + swigValue);
    }

    @SuppressWarnings("unused")
    private Type() {
      this.swigValue = SwigNext.next++;
    }

    @SuppressWarnings("unused")
    private Type(int swigValue) {
      this.swigValue = swigValue;
      SwigNext.next = swigValue+1;
    }

    @SuppressWarnings("unused")
    private Type(Type swigEnum) {
      this.swigValue = swigEnum.swigValue;
      SwigNext.next = this.swigValue+1;
    }

    private final int swigValue;

    private static class SwigNext {
      private static int next = 0;
    }
  }

}


/* ----------------------------------------------------------------------------
 * This file was automatically generated by SWIG (http://www.swig.org).
 * Version 2.0.6
 *
 * Do not make changes to this file unless you know what you are doing--modify
 * the SWIG interface file instead.
 * ----------------------------------------------------------------------------- */

package io.humble.video;
import io.humble.ferry.*;
/**
 * A Container that MediaPacket objects can be written to.
 */
public class Muxer extends Container {
  // JNIHelper.swg: Start generated code
  // >>>>>>>>>>>>>>>>>>>>>>>>>>>
  /**
   * This method is only here to use some references and remove
   * a Eclipse compiler warning.
   */
  @SuppressWarnings("unused")
  private void noop()
  {
    Buffer.make(null, 1);
  }

  private volatile long swigCPtr;

  /**
   * Internal Only.
   */
  protected Muxer(long cPtr, boolean cMemoryOwn) {
    super(VideoJNI.Muxer_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected Muxer(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(VideoJNI.Muxer_SWIGUpcast(cPtr),
     cMemoryOwn, ref);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that obj is proxying for.
   *
   * @param obj The java proxy object for a native object.
   * @return The raw pointer obj is proxying for.
   */
  protected static long getCPtr(Muxer obj) {
    if (obj == null) return 0;
    return obj.getMyCPtr();
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that we're proxying for.
   *
   * @return The raw pointer we're proxying for.
   */
  protected long getMyCPtr() {
    if (swigCPtr == 0) throw new IllegalStateException("underlying native object already deleted");
    return swigCPtr;
  }

  /**
   * Create a new Muxer object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public Muxer copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new Muxer(swigCPtr, swigCMemOwn, getJavaRefCount());
  }

  /**
   * Compares two values, returning true if the underlying objects in native code are the same object.
   *
   * That means you can have two different Java objects, but when you do a comparison, you'll find out
   * they are the EXACT same object.
   *
   * @return True if the underlying native object is the same.  False otherwise.
   */
  public boolean equals(Object obj) {
    boolean equal = false;
    if (obj instanceof Muxer)
      equal = (((Muxer)obj).swigCPtr == this.swigCPtr);
    return equal;
  }

  /**
   * Get a hashable value for this object.
   *
   * @return the hashable value.
   */
  public int hashCode() {
     return (int)swigCPtr;
  }

  // <<<<<<<<<<<<<<<<<<<<<<<<<<<
  // JNIHelper.swg: End generated code

  @Override
  public String toString() {
    final StringBuilder b = new StringBuilder(super.toString());
    b.append("[")
    .append("url="+getURL()+";")
    .append("format:"+getFormat()+";")
    .append("]");
    return b.toString();
  }

/**
 * Creates a new muxer.

 *

 * One of the three passed in parameter must be non-null. If the muxer requires a URL to write to,

 * then that must be specified.

 *

 * @param format If non null, this will be the format this muxer assumes it is writting packets in.

 * @param filename The filename/url to open. If format is null, this will also be examined to guess actual format.

 * @param formatName The formatname of the muxer to use. This will only be examined if format is null.

 *

 * @return a Muxer

 *

 * @throws InvalidArgument if all parameters are null.
 */
  public static Muxer make(String filename, MuxerFormat format, String formatName) {
    long cPtr = VideoJNI.Muxer_make(filename, MuxerFormat.getCPtr(format), format, formatName);
    return (cPtr == 0) ? null : new Muxer(cPtr, false);
  }

/**
 * Get the URL the Muxer was opened with.

 * May return null if unknown.

 * @return the URL opened, or null.
 */
  public String getURL() {
    return VideoJNI.Muxer_getURL(swigCPtr, this);
  }

/**
 * Get the MuxerFormat associated with this Muxer

 * or null if unknown.
 */
  public MuxerFormat getFormat() {
    long cPtr = VideoJNI.Muxer_getFormat(swigCPtr, this);
    return (cPtr == 0) ? null : new MuxerFormat(cPtr, false);
  }

/**
 * Get the current state of the Muxer.
 */
  public Muxer.State getState() {
    return Muxer.State.swigToEnum(VideoJNI.Muxer_getState(swigCPtr, this));
  }

/**
 * Open the Muxer and write any headers.

 *

 * @param inputOptions muxer-specific options to set before opening the muxer. Can be null.

 * @param outputOptions if non null, the passed in bag will be emptied, and the filled

 *    with any options from inputOptions that could not be set on the muxer.
 */
  public void open(KeyValueBag inputOptions, KeyValueBag outputOptions) throws java.lang.InterruptedException, java.io.IOException {
    VideoJNI.Muxer_open(swigCPtr, this, KeyValueBag.getCPtr(inputOptions), inputOptions, KeyValueBag.getCPtr(outputOptions), outputOptions);
  }

/**
 * Close the muxer and write any trailers.

 *

 * Note: Calls MUST call this method -- it will not automatically be called

 * when the object is finalized as some muxers struggle when you write trailers

 * on a different thread (the finalizer thread) than the header was written on.
 */
  public void close() {
    VideoJNI.Muxer_close(swigCPtr, this);
  }

/**
 * Get the number of streams in this container.
 */
  public int getNumStreams() throws java.lang.InterruptedException, java.io.IOException {
    return VideoJNI.Muxer_getNumStreams(swigCPtr, this);
  }

/**
 * Set the buffer length Humble Video will suggest to FFMPEG for writing output data.

 *

 * If called when a Container is open, the call is ignored and -1 is returned.

 *

 * @param size The suggested buffer size.

 * @throws InvalidArgument if size <= 0
 */
  public void setOutputBufferLength(int size) {
    VideoJNI.Muxer_setOutputBufferLength(swigCPtr, this, size);
  }

/**
 * Return the output buffer length.

 *

 * @return The input buffer length Humble Video told FFMPEG to assume.

 *   0 means FFMPEG should choose it's own size (and it'll probably be 32768).
 */
  public int getOutputBufferLength() {
    return VideoJNI.Muxer_getOutputBufferLength(swigCPtr, this);
  }

/**
 * Adds a new stream that will have packets written to it.

 *

 * Note on thread safety: Callers must ensure that the coder is not encoding or decoding

 * packets at the same time that Muxer#open or Muxer#close is being called.

 *

 * @param coder The coder that will be used for packets written to this stream.

 *

 * @throws InvalidArgument if encoder is null.

 * @throws InvalidArgument if encoder is not open.
 */
  public MuxerStream addNewStream(Coder coder) {
    long cPtr = VideoJNI.Muxer_addNewStream(swigCPtr, this, Coder.getCPtr(coder), coder);
    return (cPtr == 0) ? null : new MuxerStream(cPtr, false);
  }

/**
 * Get the MuxerStream at the given position.
 */
  public MuxerStream getStream(int position) throws java.lang.InterruptedException, java.io.IOException {
    long cPtr = VideoJNI.Muxer_getStream(swigCPtr, this, position);
    return (cPtr == 0) ? null : new MuxerStream(cPtr, false);
  }

/**
 * Writes the given packet to the Muxer.

 *

 * @param packet The packet to write.

 * @param forceInterleave If true, this Muxer will ensure that all packets are interleaved across streams

 *   (i.e. monotonically increasing timestamps in the Muxer container). If false, then the caller

 *   is responsible for ensuring the interleaving is valid for the container. Note this method is faster

 *   if forceInterleave is false.

 *

 * @return true if all data has been flushed, false if data remains to be flushed.

 *

 * @throws InvalidArgument if packet is null.

 * @throws InvalidArgument if packet is not complete.

 * @throws RuntimeException for other errors.
 */
  public boolean write(MediaPacket packet, boolean forceInterleave) {
    return VideoJNI.Muxer_write(swigCPtr, this, MediaPacket.getCPtr(packet), packet, forceInterleave);
  }

  /**
   * Muxers can only be in one of these states.
   */
  public enum State {
  /**
   * Initialized but not yet opened. Transitions to STATE_OPENED or STATE_ERROR.

   * New streams can be added.
   */
    STATE_INITED,
  /**
   * File is opened, and header is written. For most formats,

   * you can no longer add new streams. Check flags to find out if you can.
   */
    STATE_OPENED,
  /**
   * Trailer is written, file is closed and all file-resources have been released. The Muxer

   * should be discarded.
   */
    STATE_CLOSED,
  /**
   * An error has occured.
   */
    STATE_ERROR,
  ;

    public final int swigValue() {
      return swigValue;
    }

    public static State swigToEnum(int swigValue) {
      State[] swigValues = State.class.getEnumConstants();
      if (swigValue < swigValues.length && swigValue >= 0 && swigValues[swigValue].swigValue == swigValue)
        return swigValues[swigValue];
      for (State swigEnum : swigValues)
        if (swigEnum.swigValue == swigValue)
          return swigEnum;
      throw new IllegalArgumentException("No enum " + State.class + " with value " + swigValue);
    }

    @SuppressWarnings("unused")
    private State() {
      this.swigValue = SwigNext.next++;
    }

    @SuppressWarnings("unused")
    private State(int swigValue) {
      this.swigValue = swigValue;
      SwigNext.next = swigValue+1;
    }

    @SuppressWarnings("unused")
    private State(State swigEnum) {
      this.swigValue = swigEnum.swigValue;
      SwigNext.next = this.swigValue+1;
    }

    private final int swigValue;

    private static class SwigNext {
      private static int next = 0;
    }
  }

}


/* ----------------------------------------------------------------------------
 * This file was automatically generated by SWIG (http://www.swig.org).
 * Version 2.0.6
 *
 * Do not make changes to this file unless you know what you are doing--modify
 * the SWIG interface file instead.
 * ----------------------------------------------------------------------------- */

package io.humble.ferry;

/**
 * Internal Only.

 *

 * This object exists so that Native code can get access to

 * thread safe locking objects if they need it.

 *

 * Implements a blocking Mutually-Exclusive lock

 * by wrapping a Java lock.

 *

 * If not running inside Java, lock() and unlock()

 * are NO-OPs.

 *

 */
public class Mutex extends RefCounted {
  // JNIHelper.swg: Start generated code
  // >>>>>>>>>>>>>>>>>>>>>>>>>>>
  /**
   * This method is only here to use some references and remove
   * a Eclipse compiler warning.
   */
  @SuppressWarnings("unused")
  private void noop()
  {
    Buffer.make(null, 1);
  }

  private volatile long swigCPtr;

  /**
   * Internal Only.
   */
  protected Mutex(long cPtr, boolean cMemoryOwn) {
    super(FerryJNI.Mutex_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected Mutex(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(FerryJNI.Mutex_SWIGUpcast(cPtr),
     cMemoryOwn, ref);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that obj is proxying for.
   *
   * @param obj The java proxy object for a native object.
   * @return The raw pointer obj is proxying for.
   */
  protected static long getCPtr(Mutex obj) {
    if (obj == null) return 0;
    return obj.getMyCPtr();
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that we're proxying for.
   *
   * @return The raw pointer we're proxying for.
   */
  protected long getMyCPtr() {
    if (swigCPtr == 0) throw new IllegalStateException("underlying native object already deleted");
    return swigCPtr;
  }

  /**
   * Create a new Mutex object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public Mutex copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new Mutex(swigCPtr, swigCMemOwn, getJavaRefCount());
  }

  /**
   * Compares two values, returning true if the underlying objects in native code are the same object.
   *
   * That means you can have two different Java objects, but when you do a comparison, you'll find out
   * they are the EXACT same object.
   *
   * @return True if the underlying native object is the same.  False otherwise.
   */
  public boolean equals(Object obj) {
    boolean equal = false;
    if (obj instanceof Mutex)
      equal = (((Mutex)obj).swigCPtr == this.swigCPtr);
    return equal;
  }

  /**
   * Get a hashable value for this object.
   *
   * @return the hashable value.
   */
  public int hashCode() {
     return (int)swigCPtr;
  }

  // <<<<<<<<<<<<<<<<<<<<<<<<<<<
  // JNIHelper.swg: End generated code

  public static Mutex make() {
    long cPtr = FerryJNI.Mutex_make();
    return (cPtr == 0) ? null : new Mutex(cPtr, false);
  }

  public void lock() {
    FerryJNI.Mutex_lock(swigCPtr, this);
  }

  public void unlock() {
    FerryJNI.Mutex_unlock(swigCPtr, this);
  }

}


/* ----------------------------------------------------------------------------
 * This file was automatically generated by SWIG (http://www.swig.org).
 * Version 2.0.6
 *
 * Do not make changes to this file unless you know what you are doing--modify
 * the SWIG interface file instead.
 * ----------------------------------------------------------------------------- */

package io.humble.video;
import io.humble.ferry.*;
/**
 * An object that describes Containers that HumbleVideo can read MediaPacket objects

 * from.
 */
public class DemuxerFormat extends ContainerFormat {
  // JNIHelper.swg: Start generated code
  // >>>>>>>>>>>>>>>>>>>>>>>>>>>
  /**
   * This method is only here to use some references and remove
   * a Eclipse compiler warning.
   */
  @SuppressWarnings("unused")
  private void noop()
  {
    Buffer.make(null, 1);
  }

  private volatile long swigCPtr;

  /**
   * Internal Only.
   */
  protected DemuxerFormat(long cPtr, boolean cMemoryOwn) {
    super(VideoJNI.DemuxerFormat_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected DemuxerFormat(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(VideoJNI.DemuxerFormat_SWIGUpcast(cPtr),
     cMemoryOwn, ref);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that obj is proxying for.
   *
   * @param obj The java proxy object for a native object.
   * @return The raw pointer obj is proxying for.
   */
  protected static long getCPtr(DemuxerFormat obj) {
    if (obj == null) return 0;
    return obj.getMyCPtr();
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that we're proxying for.
   *
   * @return The raw pointer we're proxying for.
   */
  protected long getMyCPtr() {
    if (swigCPtr == 0) throw new IllegalStateException("underlying native object already deleted");
    return swigCPtr;
  }

  /**
   * Create a new DemuxerFormat object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public DemuxerFormat copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new DemuxerFormat(swigCPtr, swigCMemOwn, getJavaRefCount());
  }

  /**
   * Compares two values, returning true if the underlying objects in native code are the same object.
   *
   * That means you can have two different Java objects, but when you do a comparison, you'll find out
   * they are the EXACT same object.
   *
   * @return True if the underlying native object is the same.  False otherwise.
   */
  public boolean equals(Object obj) {
    boolean equal = false;
    if (obj instanceof DemuxerFormat)
      equal = (((DemuxerFormat)obj).swigCPtr == this.swigCPtr);
    return equal;
  }

  /**
   * Get a hashable value for this object.
   *
   * @return the hashable value.
   */
  public int hashCode() {
     return (int)swigCPtr;
  }

  // <<<<<<<<<<<<<<<<<<<<<<<<<<<
  // JNIHelper.swg: End generated code

  @Override
  public String toString()
  {
    StringBuilder result = new StringBuilder();

    result.append(this.getClass().getName()+"@"+hashCode()+"[");
    result.append("name:" + getName() + ";");
    result.append("description:"+getLongName() + ";");
    result.append("]");
    return result.toString();
  }

  /**
   * Return a collection of all input formats installed on this system.
   * @return the list.
   */
  public static java.util.Collection<DemuxerFormat>
  getFormats()
  {
    java.util.Collection<DemuxerFormat> retval =
      new java.util.HashSet<DemuxerFormat>();
    int count = getNumFormats();
    for(int i = 0; i< count;++i)
    {
      DemuxerFormat fmt = getFormat(i);
      if (fmt != null)
        retval.add(fmt);
    }
    return retval;
  }

/**
 * Name for format.
 */
  public String getName() {
    return VideoJNI.DemuxerFormat_getName(swigCPtr, this);
  }

/**
 * Descriptive name for the format, meant to be more human-readable

 * than name.
 */
  public String getLongName() {
    return VideoJNI.DemuxerFormat_getLongName(swigCPtr, this);
  }

/**
 * A comma-separated list of supported filename extensions
 */
  public String getExtensions() {
    return VideoJNI.DemuxerFormat_getExtensions(swigCPtr, this);
  }

/**
 * Flags that tell you what capabilities this format supports.

 *

 * @return a bitmask of Flags
 */
  public int getFlags() {
    return VideoJNI.DemuxerFormat_getFlags(swigCPtr, this);
  }

/**
 * Get total number of different codecs this container can output.
 */
  protected int getNumSupportedCodecs() {
    return VideoJNI.DemuxerFormat_getNumSupportedCodecs(swigCPtr, this);
  }

/**
 * Get the CodecId for the n'th codec supported by this container.

 *

 * @param n The n'th codec supported by this codec. Lower n are higher priority.

 *   n must be < #getNumSupportedCodecs()

 * @return the CodecId at the n'th slot, or CodecId.ID_NONE if none.
 */
  protected Codec.ID getSupportedCodecId(int n) {
    return Codec.ID.swigToEnum(VideoJNI.DemuxerFormat_getSupportedCodecId(swigCPtr, this, n));
  }

/**
 * Get the 32-bit Codec Tag for the n'th codec supported by this container.

 *

 * @param n The n'th codec supported by this codec. Lower n are higher priority.

 *   n must be < #getNumSupportedCodecs()

 * @return the codec tag at the n'th slot, or 0 if none.
 */
  protected long getSupportedCodecTag(int n) {
    return VideoJNI.DemuxerFormat_getSupportedCodecTag(swigCPtr, this, n);
  }

/**
 * Find DemuxerFormat based on the short name of the input format.

 * @return An DemuxerFormat or null if none found.
 */
  public static DemuxerFormat findFormat(String shortName) {
    long cPtr = VideoJNI.DemuxerFormat_findFormat(shortName);
    return (cPtr == 0) ? null : new DemuxerFormat(cPtr, false);
  }

/**
 * Get the number of DemuxerFormats this install can demultiplex (read)

 * from.

 *

 * @return the number of formats
 */
  protected static int getNumFormats() {
    return VideoJNI.DemuxerFormat_getNumFormats();
  }

/**
 * Return an object for the DemuxerFormats at the given index.

 *

 * @param index an index for the format list we maintain

 *

 * @return a format object for that input or null if

 *   unknown, index < 0 or index >= #getNumInstalledInputFormats()
 */
  protected static DemuxerFormat getFormat(int index) {
    long cPtr = VideoJNI.DemuxerFormat_getFormat(index);
    return (cPtr == 0) ? null : new DemuxerFormat(cPtr, false);
  }

}


/* ----------------------------------------------------------------------------
 * This file was automatically generated by SWIG (http://www.swig.org).
 * Version 2.0.6
 *
 * Do not make changes to this file unless you know what you are doing--modify
 * the SWIG interface file instead.
 * ----------------------------------------------------------------------------- */

package io.humble.video;
import io.humble.ferry.*;
/**
 * A Container for Media data. This is an abstract class and

 * cannot be instantiated on its own.
 */
public class Container extends Configurable {
  // JNIHelper.swg: Start generated code
  // >>>>>>>>>>>>>>>>>>>>>>>>>>>
  /**
   * This method is only here to use some references and remove
   * a Eclipse compiler warning.
   */
  @SuppressWarnings("unused")
  private void noop()
  {
    Buffer.make(null, 1);
  }

  private volatile long swigCPtr;

  /**
   * Internal Only.
   */
  protected Container(long cPtr, boolean cMemoryOwn) {
    super(VideoJNI.Container_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected Container(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(VideoJNI.Container_SWIGUpcast(cPtr),
     cMemoryOwn, ref);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that obj is proxying for.
   *
   * @param obj The java proxy object for a native object.
   * @return The raw pointer obj is proxying for.
   */
  protected static long getCPtr(Container obj) {
    if (obj == null) return 0;
    return obj.getMyCPtr();
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that we're proxying for.
   *
   * @return The raw pointer we're proxying for.
   */
  protected long getMyCPtr() {
    if (swigCPtr == 0) throw new IllegalStateException("underlying native object already deleted");
    return swigCPtr;
  }

  /**
   * Create a new Container object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public Container copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new Container(swigCPtr, swigCMemOwn, getJavaRefCount());
  }

  /**
   * Compares two values, returning true if the underlying objects in native code are the same object.
   *
   * That means you can have two different Java objects, but when you do a comparison, you'll find out
   * they are the EXACT same object.
   *
   * @return True if the underlying native object is the same.  False otherwise.
   */
  public boolean equals(Object obj) {
    boolean equal = false;
    if (obj instanceof Container)
      equal = (((Container)obj).swigCPtr == this.swigCPtr);
    return equal;
  }

  /**
   * Get a hashable value for this object.
   *
   * @return the hashable value.
   */
  public int hashCode() {
     return (int)swigCPtr;
  }

  // <<<<<<<<<<<<<<<<<<<<<<<<<<<
  // JNIHelper.swg: End generated code

  @Override
  public String toString() {
    final StringBuilder b = new StringBuilder(super.toString());
    b.append("[")
    .append("]");
    return b.toString();
  }

/**
 * The number of streams in this container.

 *
If this container is a Source this will query the stream and find out

 * how many streams are in it.

If the current thread is interrupted while this blocking method

 * is running the method will return with a negative value.

 * To check if the method exited because of an interruption

 * pass the return value to Error#make(int) and then

 * check Error#getType() to see if it is

 * Error.Type#ERROR_INTERRUPTED.

 *

 *

 * @return The number of streams in this container.
 */
  public int getNumStreams() throws java.lang.InterruptedException, java.io.IOException {
    return VideoJNI.Container_getNumStreams(swigCPtr, this);
  }

  /**
   * Do not set these flags -- several are used by the internals of Humble Video.
   */
  public enum Flag {
  /**
   * Generate missing pts even if it requires parsing future frames. *
   */
    FLAG_GENPTS(VideoJNI.Container_FLAG_GENPTS_get()),
  /**
   * Ignore index. *
   */
    FLAG_IGNIDX(VideoJNI.Container_FLAG_IGNIDX_get()),
  /**
   * Do not block when reading packets from input. *
   */
    FLAG_NONBLOCK(VideoJNI.Container_FLAG_NONBLOCK_get()),
  /**
   * Ignore DTS on frames that contain both DTS & PTS *
   */
    FLAG_IGNDTS(VideoJNI.Container_FLAG_IGNDTS_get()),
  /**
   * Do not infer any values from other values, just return what is stored in the container *
   */
    FLAG_NOFILLIN(VideoJNI.Container_FLAG_NOFILLIN_get()),
  /**
   * Do not use AVParsers, you also must set AVFMT_FLAG_NOFILLIN as the fillin code works on frames and no parsing -> no frames. Also seeking to frames can not work if parsing to find frame boundaries has been disabled *
   */
    FLAG_NOPARSE(VideoJNI.Container_FLAG_NOPARSE_get()),
  /**
   * Do not buffer frames when possible *
   */
    FLAG_NOBUFFER(VideoJNI.Container_FLAG_NOBUFFER_get()),
  /**
   * The caller has supplied a custom AVIOContext, don't avio_close() it. *
   */
    FLAG_CUSTOM_IO(VideoJNI.Container_FLAG_CUSTOM_IO_get()),
  /**
   * Discard frames marked corrupted *
   */
    FLAG_DISCARD_CORRUPT(VideoJNI.Container_FLAG_DISCARD_CORRUPT_get()),
  /**
   * Enable RTP MP4A-LATM payload *
   */
    FLAG_MP4A_LATM(VideoJNI.Container_FLAG_MP4A_LATM_get()),
  /**
   * try to interleave outputted packets by dts (using this flag can slow demuxing down) *
   */
    FLAG_SORT_DTS(VideoJNI.Container_FLAG_SORT_DTS_get()),
  /**
   * Enable use of private options by delaying codec open (this could be made default once all code is converted) *
   */
    FLAG_PRIV_OPT(VideoJNI.Container_FLAG_PRIV_OPT_get()),
  /**
   * Don't merge side data but keep it separate. *
   */
    FLAG_KEEP_SIDE_DATA(VideoJNI.Container_FLAG_KEEP_SIDE_DATA_get()),
  ;

    public final int swigValue() {
      return swigValue;
    }

    public static Flag swigToEnum(int swigValue) {
      Flag[] swigValues = Flag.class.getEnumConstants();
      if (swigValue < swigValues.length && swigValue >= 0 && swigValues[swigValue].swigValue == swigValue)
        return swigValues[swigValue];
      for (Flag swigEnum : swigValues)
        if (swigEnum.swigValue == swigValue)
          return swigEnum;
      throw new IllegalArgumentException("No enum " + Flag.class + " with value " + swigValue);
    }

    @SuppressWarnings("unused")
    private Flag() {
      this.swigValue = SwigNext.next++;
    }

    @SuppressWarnings("unused")
    private Flag(int swigValue) {
      this.swigValue = swigValue;
      SwigNext.next = swigValue+1;
    }

    @SuppressWarnings("unused")
    private Flag(Flag swigEnum) {
      this.swigValue = swigEnum.swigValue;
      SwigNext.next = this.swigValue+1;
    }

    private final int swigValue;

    private static class SwigNext {
      private static int next = 0;
    }
  }

}


/* ----------------------------------------------------------------------------
 * This file was automatically generated by SWIG (http://www.swig.org).
 * Version 2.0.6
 *
 * Do not make changes to this file unless you know what you are doing--modify
 * the SWIG interface file instead.
 * ----------------------------------------------------------------------------- */

package io.humble.video;
import io.humble.ferry.*;
/**
 * A ContainerStream object representing a stream contained in a writable Container.
 */
public class MuxerStream extends ContainerStream {
  // JNIHelper.swg: Start generated code
  // >>>>>>>>>>>>>>>>>>>>>>>>>>>
  /**
   * This method is only here to use some references and remove
   * a Eclipse compiler warning.
   */
  @SuppressWarnings("unused")
  private void noop()
  {
    Buffer.make(null, 1);
  }

  private volatile long swigCPtr;

  /**
   * Internal Only.
   */
  protected MuxerStream(long cPtr, boolean cMemoryOwn) {
    super(VideoJNI.MuxerStream_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected MuxerStream(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(VideoJNI.MuxerStream_SWIGUpcast(cPtr),
     cMemoryOwn, ref);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that obj is proxying for.
   *
   * @param obj The java proxy object for a native object.
   * @return The raw pointer obj is proxying for.
   */
  protected static long getCPtr(MuxerStream obj) {
    if (obj == null) return 0;
    return obj.getMyCPtr();
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that we're proxying for.
   *
   * @return The raw pointer we're proxying for.
   */
  protected long getMyCPtr() {
    if (swigCPtr == 0) throw new IllegalStateException("underlying native object already deleted");
    return swigCPtr;
  }

  /**
   * Create a new MuxerStream object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public MuxerStream copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new MuxerStream(swigCPtr, swigCMemOwn, getJavaRefCount());
  }

  /**
   * Compares two values, returning true if the underlying objects in native code are the same object.
   *
   * That means you can have two different Java objects, but when you do a comparison, you'll find out
   * they are the EXACT same object.
   *
   * @return True if the underlying native object is the same.  False otherwise.
   */
  public boolean equals(Object obj) {
    boolean equal = false;
    if (obj instanceof MuxerStream)
      equal = (((MuxerStream)obj).swigCPtr == this.swigCPtr);
    return equal;
  }

  /**
   * Get a hashable value for this object.
   *
   * @return the hashable value.
   */
  public int hashCode() {
     return (int)swigCPtr;
  }

  // <<<<<<<<<<<<<<<<<<<<<<<<<<<
  // JNIHelper.swg: End generated code

  /**
   * info about this stream
   * @return information about this stream
   */

  @Override
  public String toString()
  {
    StringBuilder result = new StringBuilder();

    result.append(this.getClass().getName()+"@"+hashCode()+"[");
    result.append("index:"+getIndex()+";");
    result.append("id:"+getId()+";");
    result.append("framerate:"+getFrameRate()+";");
    result.append("timebase:"+getTimeBase()+";");
    result.append("]");
    return result.toString();
  }

/**
 * Get the Coder that this stream was created with.<br>
 * Note: this can be either an Encoder or a Decoder.
 */
  public Coder getCoder() {
    long cPtr = VideoJNI.MuxerStream_getCoder(swigCPtr, this);
    return (cPtr == 0) ? null : new Coder(cPtr, false);
  }

/**
 * Get the Muxer that this stream belongs to.
 */
  public Muxer getMuxer() {
    long cPtr = VideoJNI.MuxerStream_getMuxer(swigCPtr, this);
    return (cPtr == 0) ? null : new Muxer(cPtr, false);
  }

}


package com.wavesplatform.wavesj;

import com.wavesplatform.wavesj.matcher.CancelOrder;
import com.wavesplatform.wavesj.matcher.DeleteOrder;
import com.wavesplatform.wavesj.matcher.Order;
import com.wavesplatform.wavesj.transactions.*;

import java.util.Collection;

import static com.wavesplatform.wavesj.Asset.isWaves;

public class Transactions {

    public static IssueTransactionV2 makeIssueTx(PrivateKeyAccount sender, byte chainId, String name, String description,
                                                 long quantity, byte decimals, boolean reissuable, String script, long fee, long timestamp) {
        return new IssueTransactionV2(sender, chainId, name, description, quantity, decimals, reissuable, script, fee, timestamp);
    }

    public static IssueTransactionV2 makeIssueTx(PrivateKeyAccount sender, byte chainId, String name, String description, long quantity,
                                                 byte decimals, boolean reissuable, String script, long fee) {
        return makeIssueTx(sender, chainId, name, description, quantity, decimals, reissuable, script, fee, System.currentTimeMillis());
    }

    public static ReissueTransactionV2 makeReissueTx(PrivateKeyAccount sender, byte chainId, String assetId, long quantity,
                                                     boolean reissuable, long fee, long timestamp) {
        if (isWaves(assetId)) {
            throw new IllegalArgumentException("Cannot reissue WAVES");
        }
        return new ReissueTransactionV2(sender, chainId, assetId, quantity, reissuable, fee, timestamp);
    }

    public static ReissueTransactionV2 makeReissueTx(PrivateKeyAccount sender, byte chainId, String assetId, long quantity, boolean reissuable, long fee) {
        return makeReissueTx(sender, chainId, assetId, quantity, reissuable, fee, System.currentTimeMillis());
    }

    public static TransferTransactionV2 makeTransferTx(PrivateKeyAccount sender, String recipient, long amount, String assetId,
                                                       long fee, String feeAssetId, String attachment, long timestamp) {
        return new TransferTransactionV2(sender, recipient, amount, assetId, fee, feeAssetId, attachment == null ? ByteString.EMPTY : new ByteString(attachment.getBytes()), timestamp);
    }

    public static TransferTransactionV2 makeTransferTx(PrivateKeyAccount sender, String recipient, long amount, String assetId,
                                                       long fee, String feeAssetId, String attachment) {
        return makeTransferTx(sender, recipient, amount, assetId, fee, feeAssetId, attachment, System.currentTimeMillis());
    }

    public static BurnTransactionV2 makeBurnTx(PrivateKeyAccount sender, byte chainId, String assetId, long amount, long fee, long timestamp) {
        if (isWaves(assetId)) {
            throw new IllegalArgumentException("Cannot burn WAVES");
        }

        return new BurnTransactionV2(sender, chainId, assetId, amount, fee, timestamp);
    }

    public static BurnTransactionV2 makeBurnTx(PrivateKeyAccount sender, byte chainId, String assetId, long amount, long fee) {
        return makeBurnTx(sender, chainId, assetId, amount, fee, System.currentTimeMillis());
    }

    public static SponsorTransaction makeSponsorTx(PrivateKeyAccount sender, String assetId, long minAssetFee, long fee, long timestamp) {
        if (isWaves(assetId)) {
            throw new IllegalArgumentException("Cannot sponsor WAVES");
        }
        if (minAssetFee < 0) {
            throw new IllegalArgumentException("minAssetFee must be positive or zero");
        }

        return new SponsorTransaction(sender, assetId, minAssetFee, fee, timestamp);
    }

    public static SponsorTransaction makeSponsorTx(PrivateKeyAccount sender, String assetId, long minAssetFee, long fee) {
        return makeSponsorTx(sender, assetId, minAssetFee, fee, System.currentTimeMillis());
    }

    public static LeaseTransactionV2 makeLeaseTx(PrivateKeyAccount sender, String recipient, long amount, long fee, long timestamp) {
        return new LeaseTransactionV2(sender, recipient, amount, fee, timestamp);
    }

    public static LeaseTransaction makeLeaseTx(PrivateKeyAccount sender, String recipient, long amount, long fee) {
        return makeLeaseTx(sender, recipient, amount, fee, System.currentTimeMillis());
    }

    public static LeaseCancelTransaction makeLeaseCancelTx(PrivateKeyAccount sender, byte chainId, String leaseId, long fee, long timestamp) {
        return new LeaseCancelTransactionV2(sender, chainId, leaseId, fee, timestamp);
    }

    public static LeaseCancelTransaction makeLeaseCancelTx(PrivateKeyAccount sender, byte chainId, String leaseId, long fee) {
        return makeLeaseCancelTx(sender, chainId, leaseId, fee, System.currentTimeMillis());
    }

    public static AliasTransaction makeAliasTx(PrivateKeyAccount sender, String alias, byte chainId, long fee, long timestamp) {
        return new AliasTransactionV2(sender, new Alias(alias, chainId), fee, timestamp);
    }

    public static AliasTransaction makeAliasTx(PrivateKeyAccount sender, String alias, byte chainId, long fee) {
        return makeAliasTx(sender, alias, chainId, fee, System.currentTimeMillis());
    }

    public static MassTransferTransaction makeMassTransferTx(PrivateKeyAccount sender, String assetId, Collection<Transfer> transfers,
                                                             long fee, String attachment, long timestamp) {
        return new MassTransferTransaction(sender, assetId, transfers, fee, attachment == null ? ByteString.EMPTY : new ByteString(attachment.getBytes()), timestamp);
    }

    public static MassTransferTransaction makeMassTransferTx(PrivateKeyAccount sender, String assetId, Collection<Transfer> transfers,
                                                             long fee, String attachment) {
        return makeMassTransferTx(sender, assetId, transfers, fee, attachment, System.currentTimeMillis());
    }

    public static DataTransaction makeDataTx(PrivateKeyAccount sender, Collection<DataEntry<?>> data, long fee, long timestamp) {
        return new DataTransaction(sender, data, fee, timestamp);
    }

    public static DataTransaction makeDataTx(PrivateKeyAccount sender, Collection<DataEntry<?>> data, long fee) {
        return makeDataTx(sender, data, fee, System.currentTimeMillis());
    }

    /**
     * Creates a signed SetScript object.
     *
     * @param sender    the account to set the script for
     * @param script    compiled script, base64 encoded
     * @param chainId   chain ID
     * @param fee       object fee
     * @param timestamp operation timestamp
     * @return object object
     * @see Account#MAINNET
     * @see Account#TESTNET
     */
    public static SetScriptTransaction makeScriptTx(PrivateKeyAccount sender, String script, byte chainId, long fee, long timestamp) {
        return new SetScriptTransaction(sender, script, chainId, fee, timestamp);
    }

    public static SetScriptTransaction makeScriptTx(PrivateKeyAccount sender, String script, byte chainId, long fee) {
        return makeScriptTx(sender, script, chainId, fee, System.currentTimeMillis());
    }

    public static Order makeOrderTx(PrivateKeyAccount account, String matcherKey, Order.Type orderType,
                                    AssetPair assetPair, long price, long amount, long expiration, long matcherFee, long timestamp) {
        if (assetPair.getAmountAsset().equals(assetPair.getPriceAsset())) {
            throw new IllegalArgumentException("spendAsset and receiveAsset should not be equal");
        }
        return new Order(orderType, assetPair, amount, price, timestamp,
                expiration, matcherFee, account, new PublicKeyAccount(matcherKey, account.getChainId()));
    }

    public static Order makeOrderTx(PrivateKeyAccount account, String matcherKey, Order.Type orderType,
                                    AssetPair assetPair, long price, long amount, long expiration, long matcherFee) {
        return makeOrderTx(account, matcherKey, orderType, assetPair, price, amount, expiration, matcherFee, System.currentTimeMillis());
    }

    public static CancelOrder makeOrderCancelTx(PrivateKeyAccount account) {
        return makeOrderCancelTx(account, System.currentTimeMillis());
    }

    public static CancelOrder makeOrderCancelTx(PrivateKeyAccount account, long timestamp) {
        return new CancelOrder(account, timestamp);
    }

    public static CancelOrder makeOrderCancelTx(PrivateKeyAccount account, AssetPair assetPair) {
        return makeOrderCancelTx(account, assetPair, System.currentTimeMillis());
    }

    public static CancelOrder makeOrderCancelTx(PrivateKeyAccount account, AssetPair assetPair, long timestamp) {
        return new CancelOrder(account, assetPair, timestamp);
    }

    public static CancelOrder makeOrderCancelTx(PrivateKeyAccount account, AssetPair assetPair, String orderId) {
        return new CancelOrder(account, assetPair, orderId);
    }

    public static DeleteOrder makeDeleteOrder(PrivateKeyAccount account, AssetPair assetPair, String orderId) {
        return new DeleteOrder(account, assetPair, orderId);
    }
}


/*
 * Copyright 2011 Google Inc.
 *
 * 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.
 */

/*
 * This file was taken from BitcoinJ
 * (https://github.com/bitcoinj/bitcoinj/blob/release-0.14/core/src/main/java/org/bitcoinj/core/Base58.java)
 * with the following modifications made to avoid external dependencies:
 * - AddressFormatException was replaced with IllegalArgumentException
 * - decodeChecked() method was removed
 */
package com.wavesplatform.wavesj;

import java.math.BigInteger;
import java.util.Arrays;

public class Base58 {
    public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
    private static final char ENCODED_ZERO = ALPHABET[0];
    private static final int[] INDEXES = new int[128];

    static {
        Arrays.fill(INDEXES, -1);
        for (int i = 0; i < ALPHABET.length; i++) {
            INDEXES[ALPHABET[i]] = i;
        }
    }

    /**
     * Encodes the given bytes as a base58 string (no checksum is appended).
     *
     * @param input the bytes to encode
     * @return the base58-encoded string
     */
    public static String encode(byte[] input) {
        if (input.length == 0) {
            return "";
        }
        // Count leading zeros.
        int zeros = 0;
        while (zeros < input.length && input[zeros] == 0) {
            ++zeros;
        }
        // Convert base-256 digits to base-58 digits (plus conversion to ASCII characters)
        input = Arrays.copyOf(input, input.length); // since we modify it in-place
        char[] encoded = new char[input.length * 2]; // upper bound
        int outputStart = encoded.length;
        for (int inputStart = zeros; inputStart < input.length; ) {
            encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)];
            if (input[inputStart] == 0) {
                ++inputStart; // optimization - skip leading zeros
            }
        }
        // Preserve exactly as many leading encoded zeros in output as there were leading zeros in input.
        while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) {
            ++outputStart;
        }
        while (--zeros >= 0) {
            encoded[--outputStart] = ENCODED_ZERO;
        }
        // Return encoded string (including encoded leading zeros).
        return new String(encoded, outputStart, encoded.length - outputStart);
    }

    /**
     * Decodes the given base58 string into the original data bytes.
     *
     * @param input the base58-encoded string to decode
     * @return the decoded data bytes
     * @throws IllegalArgumentException if the given string is not a valid base58 string
     */
    public static byte[] decode(String input) throws IllegalArgumentException {
        if (input.startsWith("base58:")) input = input.substring(7);
        if (input.length() == 0) return new byte[0];

        // Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits).
        byte[] input58 = new byte[input.length()];
        for (int i = 0; i < input.length(); ++i) {
            char c = input.charAt(i);
            int digit = c < 128 ? INDEXES[c] : -1;
            if (digit < 0) {
                throw new IllegalArgumentException("Illegal character " + c + " at position " + i);
            }
            input58[i] = (byte) digit;
        }
        // Count leading zeros.
        int zeros = 0;
        while (zeros < input58.length && input58[zeros] == 0) {
            ++zeros;
        }
        // Convert base-58 digits to base-256 digits.
        byte[] decoded = new byte[input.length()];
        int outputStart = decoded.length;
        for (int inputStart = zeros; inputStart < input58.length; ) {
            decoded[--outputStart] = divmod(input58, inputStart, 58, 256);
            if (input58[inputStart] == 0) {
                ++inputStart; // optimization - skip leading zeros
            }
        }
        // Ignore extra leading zeroes that were added during the calculation.
        while (outputStart < decoded.length && decoded[outputStart] == 0) {
            ++outputStart;
        }
        // Return decoded data (including original number of leading zeros).
        return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length);
    }

    public static BigInteger decodeToBigInteger(String input) throws IllegalArgumentException {
        return new BigInteger(1, decode(input));
    }

    /**
     * Divides a number, represented as an array of bytes each containing a single digit
     * in the specified base, by the given divisor. The given number is modified in-place
     * to contain the quotient, and the return value is the remainder.
     *
     * @param number     the number to divide
     * @param firstDigit the index within the array of the first non-zero digit
     *                   (this is used for optimization by skipping the leading zeros)
     * @param base       the base in which the number's digits are represented (up to 256)
     * @param divisor    the number to divide by (up to 256)
     * @return the remainder of the division operation
     */
    private static byte divmod(byte[] number, int firstDigit, int base, int divisor) {
        // this is just long division which accounts for the base of the input digits
        int remainder = 0;
        for (int i = firstDigit; i < number.length; i++) {
            int digit = (int) number[i] & 0xFF;
            int temp = remainder * base + digit;
            number[i] = (byte) (temp / divisor);
            remainder = temp % divisor;
        }
        return (byte) remainder;
    }
}


package com.wavesplatform.wavesj;

import org.whispersystems.curve25519.Curve25519;
import org.whispersystems.curve25519.java.curve_sigs;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.util.Arrays;

import static com.wavesplatform.wavesj.Hash.*;

public class PrivateKeyAccount extends PublicKeyAccount {

    private static final Curve25519 cipher = Curve25519.getInstance(Curve25519.BEST);

    private static final String[] SEED_WORDS = {
            "abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd", "abuse", "access",
            "accident", "account", "accuse", "achieve", "acid", "acoustic", "acquire", "across", "act", "action",
            "actor", "actress", "actual", "adapt", "add", "addict", "address", "adjust", "admit", "adult", "advance",
            "advice", "aerobic", "affair", "afford", "afraid", "again", "age", "agent", "agree", "ahead", "aim", "air",
            "airport", "aisle", "alarm", "album", "alcohol", "alert", "alien", "all", "alley", "allow", "almost",
            "alone", "alpha", "already", "also", "alter", "always", "amateur", "amazing", "among", "amount", "amused",
            "analyst", "anchor", "ancient", "anger", "angle", "angry", "animal", "ankle", "announce", "annual",
            "another", "answer", "antenna", "antique", "anxiety", "any", "apart", "apology", "appear", "apple",
            "approve", "april", "arch", "arctic", "area", "arena", "argue", "arm", "armed", "armor", "army", "around",
            "arrange", "arrest", "arrive", "arrow", "art", "artefact", "artist", "artwork", "ask", "aspect", "assault",
            "asset", "assist", "assume", "asthma", "athlete", "atom", "attack", "attend", "attitude", "attract",
            "auction", "audit", "august", "aunt", "author", "auto", "autumn", "average", "avocado", "avoid", "awake",
            "aware", "away", "awesome", "awful", "awkward", "axis", "baby", "bachelor", "bacon", "badge", "bag",
            "balance", "balcony", "ball", "bamboo", "banana", "banner", "bar", "barely", "bargain", "barrel", "base",
            "basic", "basket", "battle", "beach", "bean", "beauty", "because", "become", "beef", "before", "begin",
            "behave", "behind", "believe", "below", "belt", "bench", "benefit", "best", "betray", "better", "between",
            "beyond", "bicycle", "bid", "bike", "bind", "biology", "bird", "birth", "bitter", "black", "blade", "blame",
            "blanket", "blast", "bleak", "bless", "blind", "blood", "blossom", "blouse", "blue", "blur", "blush",
            "board", "boat", "body", "boil", "bomb", "bone", "bonus", "book", "boost", "border", "boring", "borrow",
            "boss", "bottom", "bounce", "box", "boy", "bracket", "brain", "brand", "brass", "brave", "bread", "breeze",
            "brick", "bridge", "brief", "bright", "bring", "brisk", "broccoli", "broken", "bronze", "broom", "brother",
            "brown", "brush", "bubble", "buddy", "budget", "buffalo", "build", "bulb", "bulk", "bullet", "bundle",
            "bunker", "burden", "burger", "burst", "bus", "business", "busy", "butter", "buyer", "buzz", "cabbage",
            "cabin", "cable", "cactus", "cage", "cake", "call", "calm", "camera", "camp", "can", "canal", "cancel",
            "candy", "cannon", "canoe", "canvas", "canyon", "capable", "capital", "captain", "car", "carbon", "card",
            "cargo", "carpet", "carry", "cart", "case", "cash", "casino", "castle", "casual", "cat", "catalog", "catch",
            "category", "cattle", "caught", "cause", "caution", "cave", "ceiling", "celery", "cement", "census",
            "century", "cereal", "certain", "chair", "chalk", "champion", "change", "chaos", "chapter", "charge",
            "chase", "chat", "cheap", "check", "cheese", "chef", "cherry", "chest", "chicken", "chief", "child",
            "chimney", "choice", "choose", "chronic", "chuckle", "chunk", "churn", "cigar", "cinnamon", "circle",
            "citizen", "city", "civil", "claim", "clap", "clarify", "claw", "clay", "clean", "clerk", "clever", "click",
            "client", "cliff", "climb", "clinic", "clip", "clock", "clog", "close", "cloth", "cloud", "clown", "club",
            "clump", "cluster", "clutch", "coach", "coast", "coconut", "code", "coffee", "coil", "coin", "collect",
            "color", "column", "combine", "come", "comfort", "comic", "common", "company", "concert", "conduct",
            "confirm", "congress", "connect", "consider", "control", "convince", "cook", "cool", "copper", "copy",
            "coral", "core", "corn", "correct", "cost", "cotton", "couch", "country", "couple", "course", "cousin",
            "cover", "coyote", "crack", "cradle", "craft", "cram", "crane", "crash", "crater", "crawl", "crazy",
            "cream", "credit", "creek", "crew", "cricket", "crime", "crisp", "critic", "crop", "cross", "crouch",
            "crowd", "crucial", "cruel", "cruise", "crumble", "crunch", "crush", "cry", "crystal", "cube", "culture",
            "cup", "cupboard", "curious", "current", "curtain", "curve", "cushion", "custom", "cute", "cycle", "dad",
            "damage", "damp", "dance", "danger", "daring", "dash", "daughter", "dawn", "day", "deal", "debate",
            "debris", "decade", "december", "decide", "decline", "decorate", "decrease", "deer", "defense", "define",
            "defy", "degree", "delay", "deliver", "demand", "demise", "denial", "dentist", "deny", "depart", "depend",
            "deposit", "depth", "deputy", "derive", "describe", "desert", "design", "desk", "despair", "destroy",
            "detail", "detect", "develop", "device", "devote", "diagram", "dial", "diamond", "diary", "dice", "diesel",
            "diet", "differ", "digital", "dignity", "dilemma", "dinner", "dinosaur", "direct", "dirt", "disagree",
            "discover", "disease", "dish", "dismiss", "disorder", "display", "distance", "divert", "divide", "divorce",
            "dizzy", "doctor", "document", "dog", "doll", "dolphin", "domain", "donate", "donkey", "donor", "door",
            "dose", "double", "dove", "draft", "dragon", "drama", "drastic", "draw", "dream", "dress", "drift", "drill",
            "drink", "drip", "drive", "drop", "drum", "dry", "duck", "dumb", "dune", "during", "dust", "dutch", "duty",
            "dwarf", "dynamic", "eager", "eagle", "early", "earn", "earth", "easily", "east", "easy", "echo", "ecology",
            "economy", "edge", "edit", "educate", "effort", "egg", "eight", "either", "elbow", "elder", "electric",
            "elegant", "element", "elephant", "elevator", "elite", "else", "embark", "embody", "embrace", "emerge",
            "emotion", "employ", "empower", "empty", "enable", "enact", "end", "endless", "endorse", "enemy", "energy",
            "enforce", "engage", "engine", "enhance", "enjoy", "enlist", "enough", "enrich", "enroll", "ensure",
            "enter", "entire", "entry", "envelope", "episode", "equal", "equip", "era", "erase", "erode", "erosion",
            "error", "erupt", "escape", "essay", "essence", "estate", "eternal", "ethics", "evidence", "evil", "evoke",
            "evolve", "exact", "example", "excess", "exchange", "excite", "exclude", "excuse", "execute", "exercise",
            "exhaust", "exhibit", "exile", "exist", "exit", "exotic", "expand", "expect", "expire", "explain", "expose",
            "express", "extend", "extra", "eye", "eyebrow", "fabric", "face", "faculty", "fade", "faint", "faith",
            "fall", "false", "fame", "family", "famous", "fan", "fancy", "fantasy", "farm", "fashion", "fat", "fatal",
            "father", "fatigue", "fault", "favorite", "feature", "february", "federal", "fee", "feed", "feel", "female",
            "fence", "festival", "fetch", "fever", "few", "fiber", "fiction", "field", "figure", "file", "film",
            "filter", "final", "find", "fine", "finger", "finish", "fire", "firm", "first", "fiscal", "fish", "fit",
            "fitness", "fix", "flag", "flame", "flash", "flat", "flavor", "flee", "flight", "flip", "float", "flock",
            "floor", "flower", "fluid", "flush", "fly", "foam", "focus", "fog", "foil", "fold", "follow", "food",
            "foot", "force", "forest", "forget", "fork", "fortune", "forum", "forward", "fossil", "foster", "found",
            "fox", "fragile", "frame", "frequent", "fresh", "friend", "fringe", "frog", "front", "frost", "frown",
            "frozen", "fruit", "fuel", "fun", "funny", "furnace", "fury", "future", "gadget", "gain", "galaxy",
            "gallery", "game", "gap", "garage", "garbage", "garden", "garlic", "garment", "gas", "gasp", "gate",
            "gather", "gauge", "gaze", "general", "genius", "genre", "gentle", "genuine", "gesture", "ghost", "giant",
            "gift", "giggle", "ginger", "giraffe", "girl", "give", "glad", "glance", "glare", "glass", "glide",
            "glimpse", "globe", "gloom", "glory", "glove", "glow", "glue", "goat", "goddess", "gold", "good", "goose",
            "gorilla", "gospel", "gossip", "govern", "gown", "grab", "grace", "grain", "grant", "grape", "grass",
            "gravity", "great", "green", "grid", "grief", "grit", "grocery", "group", "grow", "grunt", "guard", "guess",
            "guide", "guilt", "guitar", "gun", "gym", "habit", "hair", "half", "hammer", "hamster", "hand", "happy",
            "harbor", "hard", "harsh", "harvest", "hat", "have", "hawk", "hazard", "head", "health", "heart", "heavy",
            "hedgehog", "height", "hello", "helmet", "help", "hen", "hero", "hidden", "high", "hill", "hint", "hip",
            "hire", "history", "hobby", "hockey", "hold", "hole", "holiday", "hollow", "home", "honey", "hood", "hope",
            "horn", "horror", "horse", "hospital", "host", "hotel", "hour", "hover", "hub", "huge", "human", "humble",
            "humor", "hundred", "hungry", "hunt", "hurdle", "hurry", "hurt", "husband", "hybrid", "ice", "icon", "idea",
            "identify", "idle", "ignore", "ill", "illegal", "illness", "image", "imitate", "immense", "immune",
            "impact", "impose", "improve", "impulse", "inch", "include", "income", "increase", "index", "indicate",
            "indoor", "industry", "infant", "inflict", "inform", "inhale", "inherit", "initial", "inject", "injury",
            "inmate", "inner", "innocent", "input", "inquiry", "insane", "insect", "inside", "inspire", "install",
            "intact", "interest", "into", "invest", "invite", "involve", "iron", "island", "isolate", "issue", "item",
            "ivory", "jacket", "jaguar", "jar", "jazz", "jealous", "jeans", "jelly", "jewel", "job", "join", "joke",
            "journey", "joy", "judge", "juice", "jump", "jungle", "junior", "junk", "just", "kangaroo", "keen", "keep",
            "ketchup", "key", "kick", "kid", "kidney", "kind", "kingdom", "kiss", "kit", "kitchen", "kite", "kitten",
            "kiwi", "knee", "knife", "knock", "know", "lab", "label", "labor", "ladder", "lady", "lake", "lamp",
            "language", "laptop", "large", "later", "latin", "laugh", "laundry", "lava", "law", "lawn", "lawsuit",
            "layer", "lazy", "leader", "leaf", "learn", "leave", "lecture", "left", "leg", "legal", "legend", "leisure",
            "lemon", "lend", "length", "lens", "leopard", "lesson", "letter", "level", "liar", "liberty", "library",
            "license", "life", "lift", "light", "like", "limb", "limit", "link", "lion", "liquid", "list", "little",
            "live", "lizard", "load", "loan", "lobster", "local", "lock", "logic", "lonely", "long", "loop", "lottery",
            "loud", "lounge", "love", "loyal", "lucky", "luggage", "lumber", "lunar", "lunch", "luxury", "lyrics",
            "machine", "mad", "magic", "magnet", "maid", "mail", "main", "major", "make", "mammal", "man", "manage",
            "mandate", "mango", "mansion", "manual", "maple", "marble", "march", "margin", "marine", "market",
            "marriage", "mask", "mass", "master", "match", "material", "math", "matrix", "matter", "maximum", "maze",
            "meadow", "mean", "measure", "meat", "mechanic", "medal", "media", "melody", "melt", "member", "memory",
            "mention", "menu", "mercy", "merge", "merit", "merry", "mesh", "message", "metal", "method", "middle",
            "midnight", "milk", "million", "mimic", "mind", "minimum", "minor", "minute", "miracle", "mirror", "misery",
            "miss", "mistake", "mix", "mixed", "mixture", "mobile", "model", "modify", "mom", "moment", "monitor",
            "monkey", "monster", "month", "moon", "moral", "more", "morning", "mosquito", "mother", "motion", "motor",
            "mountain", "mouse", "move", "movie", "much", "muffin", "mule", "multiply", "muscle", "museum", "mushroom",
            "music", "must", "mutual", "myself", "mystery", "myth", "naive", "name", "napkin", "narrow", "nasty",
            "nation", "nature", "near", "neck", "need", "negative", "neglect", "neither", "nephew", "nerve", "nest",
            "net", "network", "neutral", "never", "news", "next", "nice", "night", "noble", "noise", "nominee",
            "noodle", "normal", "north", "nose", "notable", "note", "nothing", "notice", "novel", "now", "nuclear",
            "number", "nurse", "nut", "oak", "obey", "object", "oblige", "obscure", "observe", "obtain", "obvious",
            "occur", "ocean", "october", "odor", "off", "offer", "office", "often", "oil", "okay", "old", "olive",
            "olympic", "omit", "once", "one", "onion", "online", "only", "open", "opera", "opinion", "oppose",
            "option", "orange", "orbit", "orchard", "order", "ordinary", "organ", "orient", "original", "orphan",
            "ostrich", "other", "outdoor", "outer", "output", "outside", "oval", "oven", "over", "own", "owner",
            "oxygen", "oyster", "ozone", "pact", "paddle", "page", "pair", "palace", "palm", "panda", "panel", "panic",
            "panther", "paper", "parade", "parent", "park", "parrot", "party", "pass", "patch", "path", "patient",
            "patrol", "pattern", "pause", "pave", "payment", "peace", "peanut", "pear", "peasant", "pelican", "pen",
            "penalty", "pencil", "people", "pepper", "perfect", "permit", "person", "pet", "phone", "photo", "phrase",
            "physical", "piano", "picnic", "picture", "piece", "pig", "pigeon", "pill", "pilot", "pink", "pioneer",
            "pipe", "pistol", "pitch", "pizza", "place", "planet", "plastic", "plate", "play", "please", "pledge",
            "pluck", "plug", "plunge", "poem", "poet", "point", "polar", "pole", "police", "pond", "pony", "pool",
            "popular", "portion", "position", "possible", "post", "potato", "pottery", "poverty", "powder", "power",
            "practice", "praise", "predict", "prefer", "prepare", "present", "pretty", "prevent", "price", "pride",
            "primary", "print", "priority", "prison", "private", "prize", "problem", "process", "produce", "profit",
            "program", "project", "promote", "proof", "property", "prosper", "protect", "proud", "provide", "public",
            "pudding", "pull", "pulp", "pulse", "pumpkin", "punch", "pupil", "puppy", "purchase", "purity", "purpose",
            "purse", "push", "put", "puzzle", "pyramid", "quality", "quantum", "quarter", "question", "quick", "quit",
            "quiz", "quote", "rabbit", "raccoon", "race", "rack", "radar", "radio", "rail", "rain", "raise", "rally",
            "ramp", "ranch", "random", "range", "rapid", "rare", "rate", "rather", "raven", "raw", "razor", "ready",
            "real", "reason", "rebel", "rebuild", "recall", "receive", "recipe", "record", "recycle", "reduce",
            "reflect", "reform", "refuse", "region", "regret", "regular", "reject", "relax", "release", "relief",
            "rely", "remain", "remember", "remind", "remove", "render", "renew", "rent", "reopen", "repair", "repeat",
            "replace", "report", "require", "rescue", "resemble", "resist", "resource", "response", "result", "retire",
            "retreat", "return", "reunion", "reveal", "review", "reward", "rhythm", "rib", "ribbon", "rice", "rich",
            "ride", "ridge", "rifle", "right", "rigid", "ring", "riot", "ripple", "risk", "ritual", "rival", "river",
            "road", "roast", "robot", "robust", "rocket", "romance", "roof", "rookie", "room", "rose", "rotate",
            "rough", "round", "route", "royal", "rubber", "rude", "rug", "rule", "run", "runway", "rural", "sad",
            "saddle", "sadness", "safe", "sail", "salad", "salmon", "salon", "salt", "salute", "same", "sample", "sand",
            "satisfy", "satoshi", "sauce", "sausage", "save", "say", "scale", "scan", "scare", "scatter", "scene",
            "scheme", "school", "science", "scissors", "scorpion", "scout", "scrap", "screen", "script", "scrub", "sea",
            "search", "season", "seat", "second", "secret", "section", "security", "seed", "seek", "segment", "select",
            "sell", "seminar", "senior", "sense", "sentence", "series", "service", "session", "settle", "setup",
            "seven", "shadow", "shaft", "shallow", "share", "shed", "shell", "sheriff", "shield", "shift", "shine",
            "ship", "shiver", "shock", "shoe", "shoot", "shop", "short", "shoulder", "shove", "shrimp", "shrug",
            "shuffle", "shy", "sibling", "sick", "side", "siege", "sight", "sign", "silent", "silk", "silly", "silver",
            "similar", "simple", "since", "sing", "siren", "sister", "situate", "six", "size", "skate", "sketch", "ski",
            "skill", "skin", "skirt", "skull", "slab", "slam", "sleep", "slender", "slice", "slide", "slight", "slim",
            "slogan", "slot", "slow", "slush", "small", "smart", "smile", "smoke", "smooth", "snack", "snake", "snap",
            "sniff", "snow", "soap", "soccer", "social", "sock", "soda", "soft", "solar", "soldier", "solid",
            "solution", "solve", "someone", "song", "soon", "sorry", "sort", "soul", "sound", "soup", "source", "south",
            "space", "spare", "spatial", "spawn", "speak", "special", "speed", "spell", "spend", "sphere", "spice",
            "spider", "spike", "spin", "spirit", "split", "spoil", "sponsor", "spoon", "sport", "spot", "spray",
            "spread", "spring", "spy", "square", "squeeze", "squirrel", "stable", "stadium", "staff", "stage", "stairs",
            "stamp", "stand", "start", "state", "stay", "steak", "steel", "stem", "step", "stereo", "stick", "still",
            "sting", "stock", "stomach", "stone", "stool", "story", "stove", "strategy", "street", "strike", "strong",
            "struggle", "student", "stuff", "stumble", "style", "subject", "submit", "subway", "success", "such",
            "sudden", "suffer", "sugar", "suggest", "suit", "summer", "sun", "sunny", "sunset", "super", "supply",
            "supreme", "sure", "surface", "surge", "surprise", "surround", "survey", "suspect", "sustain", "swallow",
            "swamp", "swap", "swarm", "swear", "sweet", "swift", "swim", "swing", "switch", "sword", "symbol",
            "symptom", "syrup", "system", "table", "tackle", "tag", "tail", "talent", "talk", "tank", "tape", "target",
            "task", "taste", "tattoo", "taxi", "teach", "team", "tell", "ten", "tenant", "tennis", "tent", "term",
            "test", "text", "thank", "that", "theme", "then", "theory", "there", "they", "thing", "this", "thought",
            "three", "thrive", "throw", "thumb", "thunder", "ticket", "tide", "tiger", "tilt", "timber", "time", "tiny",
            "tip", "tired", "tissue", "title", "toast", "tobacco", "today", "toddler", "toe", "together", "toilet",
            "token", "tomato", "tomorrow", "tone", "tongue", "tonight", "tool", "tooth", "top", "topic", "topple",
            "torch", "tornado", "tortoise", "toss", "total", "tourist", "toward", "tower", "town", "toy", "track",
            "trade", "traffic", "tragic", "train", "transfer", "trap", "trash", "travel", "tray", "treat", "tree",
            "trend", "trial", "tribe", "trick", "trigger", "trim", "trip", "trophy", "trouble", "truck", "true",
            "truly", "trumpet", "trust", "truth", "try", "tube", "tuition", "tumble", "tuna", "tunnel", "turkey",
            "turn", "turtle", "twelve", "twenty", "twice", "twin", "twist", "two", "type", "typical", "ugly",
            "umbrella", "unable", "unaware", "uncle", "uncover", "under", "undo", "unfair", "unfold", "unhappy",
            "uniform", "unique", "unit", "universe", "unknown", "unlock", "until", "unusual", "unveil", "update",
            "upgrade", "uphold", "upon", "upper", "upset", "urban", "urge", "usage", "use", "used", "useful", "useless",
            "usual", "utility", "vacant", "vacuum", "vague", "valid", "valley", "valve", "van", "vanish", "vapor",
            "various", "vast", "vault", "vehicle", "velvet", "vendor", "venture", "venue", "verb", "verify", "version",
            "very", "vessel", "veteran", "viable", "vibrant", "vicious", "victory", "video", "view", "village",
            "vintage", "violin", "virtual", "virus", "visa", "visit", "visual", "vital", "vivid", "vocal", "voice",
            "void", "volcano", "volume", "vote", "voyage", "wage", "wagon", "wait", "walk", "wall", "walnut", "want",
            "warfare", "warm", "warrior", "wash", "wasp", "waste", "water", "wave", "way", "wealth", "weapon", "wear",
            "weasel", "weather", "web", "wedding", "weekend", "weird", "welcome", "west", "wet", "whale", "what",
            "wheat", "wheel", "when", "where", "whip", "whisper", "wide", "width", "wife", "wild", "will", "win",
            "window", "wine", "wing", "wink", "winner", "winter", "wire", "wisdom", "wise", "wish", "witness", "wolf",
            "woman", "wonder", "wood", "wool", "word", "work", "world", "worry", "worth", "wrap", "wreck", "wrestle",
            "wrist", "write", "wrong", "yard", "year", "yellow", "you", "young", "youth", "zebra", "zero", "zone", "zoo"
    };

    private final byte[] privateKey;

    private PrivateKeyAccount(byte[] privateKey, byte chainId) {
        super(publicKey(privateKey), chainId);
        this.privateKey = privateKey;
    }

    public static PrivateKeyAccount fromSeed(String seed, int nonce, byte chainId) {
        return new PrivateKeyAccount(privateKey(seed, nonce), chainId);
    }

    public static PrivateKeyAccount fromPrivateKey(String privateKey, byte chainId) {
        return new PrivateKeyAccount(Base58.decode(privateKey), chainId);
    }

    public final byte[] getPrivateKey() {
        return Arrays.copyOf(privateKey, privateKey.length);
    }

    /**
     * Signs a object and returns Base58-encoded signature.
     * <p>Example usage of this method in a multisig scenario where 3 signers are to sign a lease object:
     * <code>
     * PublicKeyAccount leaser = ...
     * PrivateKeyAccount signer = ...
     * Transaction tx = makeLeaseTx(leaser, recipient, amount, fee);  // produces unsigned object
     * tx = tx.setProof(0, signer.sign(tx));  // sets the signature as proof 0
     * node.send(tx);
     * </code>
     *
     * @param tx object to sign
     * @return Base58-encoded signature
     */
    public String sign(Transaction tx) {
        return sign(tx.getBytes());
    }

    public String sign(byte[] bytes) {
        return Base58.encode(cipher.calculateSignature(getPrivateKey(), bytes));
    }

    /**
     * Generates a 15-word random seed. This method implements the BIP-39 algorithm with 160 bits of entropy.
     *
     * @return the seed as a String
     */
    public static String generateSeed() {
        byte[] bytes = new byte[21];
        new SecureRandom().nextBytes(bytes);
        byte[] rhash = hash(bytes, 0, 20, SHA256);
        bytes[20] = rhash[0];
        BigInteger rand = new BigInteger(bytes);
        BigInteger mask = new BigInteger(new byte[]{0, 0, 7, -1}); // 11 lower bits
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 15; i++) {
            sb.append(i > 0 ? ' ' : "")
                    .append(SEED_WORDS[rand.and(mask).intValue()]);
            rand = rand.shiftRight(11);
        }
        return sb.toString();
    }

    private static byte[] privateKey(String seed, int nonce) {
        // account seed from seed & nonce
        ByteBuffer buf = ByteBuffer.allocate(seed.getBytes().length + 4);
        buf.putInt(nonce).put(seed.getBytes());
        byte[] accountSeed = secureHash(buf.array(), 0, buf.array().length);

        // private key from account seed & chainId
        byte[] hashedSeed = hash(accountSeed, 0, accountSeed.length, SHA256);
        byte[] privateKey = Arrays.copyOf(hashedSeed, 32);
        privateKey[0] &= 248;
        privateKey[31] &= 127;
        privateKey[31] |= 64;

        return privateKey;
    }

    private static byte[] publicKey(byte[] privateKey) {
        byte[] publicKey = new byte[32];
        curve_sigs.curve25519_keygen(publicKey, privateKey);
        return publicKey;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        if (!super.equals(o)) return false;

        PrivateKeyAccount that = (PrivateKeyAccount) o;

        return Arrays.equals(getPrivateKey(), that.getPrivateKey());
    }

    @Override
    public int hashCode() {
        int result = super.hashCode();
        result = 31 * result + Arrays.hashCode(getPrivateKey());
        return result;
    }
}