package io.ipfs.api;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.stream.*;

public interface NamedStreamable
{
    InputStream getInputStream() throws IOException;

    Optional<String> getName();

    List<NamedStreamable> getChildren();

    boolean isDirectory();

    default byte[] getContents() throws IOException {
        InputStream in = getInputStream();
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        byte[] tmp = new byte[4096];
        int r;
        while ((r=in.read(tmp))>= 0)
            bout.write(tmp, 0, r);
        return bout.toByteArray();
    }

    class FileWrapper implements NamedStreamable {
        private final File source;

        public FileWrapper(File source) {
            if (! source.exists())
                throw new IllegalStateException("File does not exist: " + source);
            this.source = source;
        }

        public InputStream getInputStream() throws IOException {
            return new FileInputStream(source);
        }

        public boolean isDirectory() {
            return source.isDirectory();
        }

        @Override
        public List<NamedStreamable> getChildren() {
            return isDirectory() ?
                    Stream.of(source.listFiles())
                            .map(NamedStreamable.FileWrapper::new)
                            .collect(Collectors.toList()) :
                    Collections.emptyList();
        }

        public Optional<String> getName() {
            try {
                return Optional.of(URLEncoder.encode(source.getName(), "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }
    }

    class ByteArrayWrapper implements NamedStreamable {
        private final Optional<String> name;
        private final byte[] data;

        public ByteArrayWrapper(byte[] data) {
            this(Optional.empty(), data);
        }

        public ByteArrayWrapper(String name, byte[] data) {
            this(Optional.of(name), data);
        }

        public ByteArrayWrapper(Optional<String> name, byte[] data) {
            this.name = name;
            this.data = data;
        }

        public boolean isDirectory() {
            return false;
        }

        public InputStream getInputStream() throws IOException {
            return new ByteArrayInputStream(data);
        }

        @Override
        public List<NamedStreamable> getChildren() {
            return Collections.emptyList();
        }

        public Optional<String> getName() {
            return name;
        }
    }

    class DirWrapper implements NamedStreamable {

        private final String name;
        private final List<NamedStreamable> children;

        public DirWrapper(String name, List<NamedStreamable> children) {
            this.name = name;
            this.children = children;
        }

        @Override
        public InputStream getInputStream() throws IOException {
            throw new IllegalStateException("Cannot get an input stream for a directory!");
        }

        @Override
        public Optional<String> getName() {
            return Optional.of(name);
        }

        @Override
        public List<NamedStreamable> getChildren() {
            return children;
        }

        @Override
        public boolean isDirectory() {
            return true;
        }
    }
}

@@ -1736,32 +1736,922 @@
        %7D%0A    %7D%0A%0A
+    class InputStreamWrapper implements NamedStreamable %7B%0A        private final Optional%3CString%3E name;%0A        private final InputStream data;%0A%0A        public InputStreamWrapper(InputStream data) %7B%0A            this(Optional.empty(), data);%0A        %7D%0A%0A        public InputStreamWrapper(String name, InputStream data) %7B%0A            this(Optional.of(name), data);%0A        %7D%0A%0A        public InputStreamWrapper(Optional%3CString%3E name, InputStream data) %7B%0A            this.name = name;%0A            this.data = data;%0A        %7D%0A%0A        public boolean isDirectory() %7B%0A            return false;%0A        %7D%0A%0A        public InputStream getInputStream() %7B%0A            return data;%0A        %7D%0A%0A        @Override%0A        public List%3CNamedStreamable%3E getChildren() %7B%0A            return Collections.emptyList();%0A        %7D%0A%0A        public Optional%3CString%3E getName() %7B%0A            return name;%0A        %7D%0A    %7D%0A%0A
     class ByteAr

Add InputStreamWrapper implementation of NamedStreamable