// Path: idobata4j-core/src/main/java/com/uphyca/idobata/http/TypedInput.java
// public interface TypedInput {
// 
//     /** Returns the mime type. */
//     String mimeType();
// 
//     /** Length in bytes. Returns {@code -1} if length is unknown. */
//     long length();
// 
//     /**
//      * Read bytes as stream. Unless otherwise specified, this method may only be called once. It is
//      * the responsibility of the caller to close the stream.
//      */
//     InputStream in() throws IOException;
// }
// 
// Path: idobata4j-core/src/main/java/com/uphyca/idobata/pusher/PresenceChannelEventListenerAdapter.java
// public class PresenceChannelEventListenerAdapter implements PresenceChannelEventListener {
// 
//     @Override
//     public void onUsersInformationReceived(String channelName, Set<User> users) {
//         //no-op
//     }
// 
//     @Override
//     public void userSubscribed(String channelName, User user) {
//         //no-op
//     }
// 
//     @Override
//     public void userUnsubscribed(String channelName, User user) {
//         //no-op
//     }
// 
//     @Override
//     public void onAuthenticationFailure(String message, Exception e) {
//         //no-op
//     }
// 
//     @Override
//     public void onSubscriptionSucceeded(String channelName) {
//         //no-op
//     }
// 
//     @Override
//     public void onEvent(String channelName, String eventName, String data) {
//         //no-op
//     }
// }
// 
// Path: idobata4j-core/src/main/java/com/uphyca/idobata/transform/ConversionException.java
// public class ConversionException extends Exception {
// 
//     public ConversionException() {
//     }
// 
//     public ConversionException(String message) {
//         super(message);
//     }
// 
//     public ConversionException(String message, Throwable cause) {
//         super(message, cause);
//     }
// 
//     public ConversionException(Throwable cause) {
//         super(cause);
//     }
// }
// 
// Path: idobata4j-core/src/main/java/com/uphyca/idobata/transform/Converter.java
// public interface Converter {
// 
//     /**
//      * Convert an HTTP response body to a concrete object of the specified type.
//      * 
//      * @param body HTTP response body.
//      * @param type Target object type.
//      * @return Instance of {@code type} which will be cast by the caller.
//      * @throws Exception
//      */
//     <T> T convert(TypedInput body, Type type) throws ConversionException, IOException;
// }


import com.pusher.client.Pusher;
import com.pusher.client.channel.PresenceChannel;
import com.pusher.client.connection.ConnectionEventListener;
import com.pusher.client.connection.ConnectionState;
import com.pusher.client.connection.ConnectionStateChange;
import com.uphyca.idobata.event.*;
import com.uphyca.idobata.http.TypedInput;
import com.uphyca.idobata.pusher.PresenceChannelEventListenerAdapter;
import com.uphyca.idobata.transform.ConversionException;
import com.uphyca.idobata.transform.Converter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
            case DISCONNECTED:
                connectionListener.closed(new ConnectionEventValue(ConnectionEvent.CLOSED));
                break;
        }
    }

    @Override
    public void onError(String message, String code, Exception e) {
        publishError(e);
    }

    private static final ErrorListener EMPTY_ERROR_LISTENER = new ErrorListener() {
        @Override
        public void onError(IdobataError error) {
            //no-op
        }
    };

    private static final ConnectionListener EMPTY_CONNECTION_LISTENER = new ConnectionListener() {
        @Override
        public void closed(ConnectionEvent event) {
            //no-op
        }

        @Override
        public void opened(ConnectionEvent event) {
            //no-op
        }
    };

// Path: idobata4j-core/src/main/java/com/uphyca/idobata/http/TypedInput.java
// public interface TypedInput {
// 
//     /** Returns the mime type. */
//     String mimeType();
// 
//     /** Length in bytes. Returns {@code -1} if length is unknown. */
//     long length();
// 
//     /**
//      * Read bytes as stream. Unless otherwise specified, this method may only be called once. It is
//      * the responsibility of the caller to close the stream.
//      */
//     InputStream in() throws IOException;
// }
// 
// Path: idobata4j-core/src/main/java/com/uphyca/idobata/pusher/PresenceChannelEventListenerAdapter.java
// public class PresenceChannelEventListenerAdapter implements PresenceChannelEventListener {
// 
//     @Override
//     public void onUsersInformationReceived(String channelName, Set<User> users) {
//         //no-op
//     }
// 
//     @Override
//     public void userSubscribed(String channelName, User user) {
//         //no-op
//     }
// 
//     @Override
//     public void userUnsubscribed(String channelName, User user) {
//         //no-op
//     }
// 
//     @Override
//     public void onAuthenticationFailure(String message, Exception e) {
//         //no-op
//     }
// 
//     @Override
//     public void onSubscriptionSucceeded(String channelName) {
//         //no-op
//     }
// 
//     @Override
//     public void onEvent(String channelName, String eventName, String data) {
//         //no-op
//     }
// }
// 
// Path: idobata4j-core/src/main/java/com/uphyca/idobata/transform/ConversionException.java
// public class ConversionException extends Exception {
// 
//     public ConversionException() {
//     }
// 
//     public ConversionException(String message) {
//         super(message);
//     }
// 
//     public ConversionException(String message, Throwable cause) {
//         super(message, cause);
//     }
// 
//     public ConversionException(Throwable cause) {
//         super(cause);
//     }
// }
// 
// Path: idobata4j-core/src/main/java/com/uphyca/idobata/transform/Converter.java
// public interface Converter {
// 
//     /**
//      * Convert an HTTP response body to a concrete object of the specified type.
//      * 
//      * @param body HTTP response body.
//      * @param type Target object type.
//      * @return Instance of {@code type} which will be cast by the caller.
//      * @throws Exception
//      */
//     <T> T convert(TypedInput body, Type type) throws ConversionException, IOException;
// }


// Path: idobata4j-core/src/main/java/com/uphyca/idobata/IdobataStreamImpl.java
import com.pusher.client.Pusher;
import com.pusher.client.channel.PresenceChannel;
import com.pusher.client.connection.ConnectionEventListener;
import com.pusher.client.connection.ConnectionState;
import com.pusher.client.connection.ConnectionStateChange;
import com.uphyca.idobata.event.*;
import com.uphyca.idobata.http.TypedInput;
import com.uphyca.idobata.pusher.PresenceChannelEventListenerAdapter;
import com.uphyca.idobata.transform.ConversionException;
import com.uphyca.idobata.transform.Converter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

            case DISCONNECTED:
                connectionListener.closed(new ConnectionEventValue(ConnectionEvent.CLOSED));
                break;
        }
    }

    @Override
    public void onError(String message, String code, Exception e) {
        publishError(e);
    }

    private static final ErrorListener EMPTY_ERROR_LISTENER = new ErrorListener() {
        @Override
        public void onError(IdobataError error) {
            //no-op
        }
    };

    private static final ConnectionListener EMPTY_CONNECTION_LISTENER = new ConnectionListener() {
        @Override
        public void closed(ConnectionEvent event) {
            //no-op
        }

        @Override
        public void opened(ConnectionEvent event) {
            //no-op
        }
    };

    private static final class JsonTypedInput implements TypedInput {