/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package io.imgn.common.io;

import static java.util.Objects.requireNonNull;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * @author Olivier
 */
public final class ByteStreams {

  private ByteStreams() {
  }

  static byte[] createBuffer() {
    return new byte[8192];
  }

  public static long copy(InputStream from, OutputStream to) throws IOException {
    requireNonNull(from);
    requireNonNull(to);
    byte[] buffer = createBuffer();
    long total = 0;
    while (true) {
      int read = from.read(buffer);
      if (read == -1) {
        break;
      }
      to.write(buffer, 0, read);
      total += read;
    }
    return total;
  }

  public static byte[] toByteArray(InputStream in) throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream(Math.max(32, in.available()));
    copy(in, out);
    return out.toByteArray();
  }

  public static long exhaust(InputStream in) throws IOException {
    long total = 0;
    long read;
    byte[] buf = createBuffer();
    while ((read = in.read(buf)) != -1) {
      total += read;
    }
    return total;
  }

  static long skipUpTo(InputStream in, final long toSkip) throws IOException {
    long totalSkipped = 0;
    byte[] buffer = createBuffer();
    while (totalSkipped < toSkip) {
      long remaining = toSkip - totalSkipped;
      long skipped = skipSafely(in, remaining);
      if (skipped == 0) {
        int skip = (int) Math.min(remaining, buffer.length);
        if ((skipped = in.read(buffer, 0, skip)) == -1) {
          break;
        }
      }
      totalSkipped += skipped;
    }

    return totalSkipped;
  }

  private static long skipSafely(InputStream in, long toSkip) throws IOException {
    int available = in.available();
    return available == 0 ? 0 : in.skip(Math.min(available, toSkip));
  }

  public static int read(InputStream in, byte[] bytes, int offset, int length) throws IOException {
    requireNonNull(in);
    requireNonNull(bytes);
    if (length < 0) {
      throw new IndexOutOfBoundsException("length is negative");
    }
    int total = 0;
    while (total < length) {
      int result = in.read(bytes, offset + total, length - total);
      if (result == -1) {
        break;
      }
      total += result;
    }
    return total;
  }
}
