package com.kryptnostic.rhizome.hazelcast.serializers;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Optional;
import com.google.common.collect.Maps;
import com.google.common.primitives.Ints;
import com.hazelcast.nio.ObjectDataInput;
import com.hazelcast.nio.ObjectDataOutput;
import com.kryptnostic.rhizome.hazelcast.objects.OrderedUUIDSet;
import com.kryptnostic.rhizome.hazelcast.objects.UUIDSet;

public class RhizomeUtils {

    static final Logger logger = LoggerFactory.getLogger( RhizomeUtils.class );

    public static class Serializers {

        public static <T extends Enum<T>> void serializeEnumSet( ObjectDataOutput out, Class<T> clazz, Set<T> set )
                throws IOException {
            T[] enumConstants = clazz.getEnumConstants();
            if ( enumConstants == null ) {
                throw new IllegalArgumentException( "This method may only be called for Enum classes" );
            }
            for ( T t : enumConstants ) {
                out.writeBoolean( set.contains( t ) );
            }
        }

        public static <T extends Enum<T>> EnumSet<T> deSerializeEnumSet( ObjectDataInput in, Class<T> clazz )
                throws IOException {
            T[] enumConstants = clazz.getEnumConstants();
            if ( enumConstants == null ) {
                throw new IllegalArgumentException( "This method may only be called for Enum classes" );
            }
            EnumSet<T> elements = EnumSet.<T> noneOf( clazz );
            for ( T t : enumConstants ) {
                if ( in.readBoolean() ) {
                    elements.add( t );
                }
            }
            return elements;
        }

        public static <T> void serializeOptional(
                ObjectDataOutput out,
                Optional<T> object,
                IoPerformingBiConsumer<ObjectDataOutput, T> serializer ) throws IOException {
            out.writeBoolean( object.isPresent() );
            if ( object.isPresent() ) {
                T value = object.get();
                serializer.accept( out, value );
            }
        }

        public static <T> Optional<T> deserializeToOptional(
                ObjectDataInput in,
                IoPerformingFunction<ObjectDataInput, T> deserializer ) throws IOException {
            Optional<T> object = Optional.absent();
            if ( in.readBoolean() ) {
                object = Optional.of( deserializer.apply( in ) );
            }
            return object;
        }

    }

    public static class Sets {
        public static OrderedUUIDSet newOrderedUUIDSetWithExpectedSize( int expected ) {
            return new OrderedUUIDSet( expectedSize( expected ) );
        }

        public static UUIDSet newUUIDSetWithExpectedSize( int expected ) {
            return new UUIDSet( expectedSize( expected ) );
        }

        public static int expectedSize( int expectedSize ) {
            if ( expectedSize < 0 ) {
                throw new IllegalArgumentException( "expectedSize cannot be negative but was: " + expectedSize );
            }
            if ( expectedSize < 3 ) {
                return expectedSize + 1;
            }
            if ( expectedSize < Ints.MAX_POWER_OF_TWO ) {
                return expectedSize + expectedSize / 3;
            }
            return Integer.MAX_VALUE;
        }
    }

    public static class Streams {

        public static void writeStringStringMap( ObjectDataOutput out, Map<String, String> object )
                throws IOException {
            int size = object.size();
            Set<String> keys = object.keySet();
            Collection<String> vals = object.values();
            RhizomeUtils.Streams.writeStringArray( out, keys.toArray( new String[ size ] ) );
            RhizomeUtils.Streams.writeStringArray( out, vals.toArray( new String[ size ] ) );
        }

        public static Map<String, String> readStringStringMap( ObjectDataInput in ) throws IOException {
            String[] keys = RhizomeUtils.Streams.readStringArray( in );
            String[] vals = RhizomeUtils.Streams.readStringArray( in );
            Map<String, String> map = Maps.newHashMapWithExpectedSize( keys.length );
            for ( int i = 0; i < keys.length; i++ ) {
                map.put( keys[ i ], vals[ i ] );
            }
            return map;
        }

        public static void writeStringArray( ObjectDataOutput out, String[] strings ) throws IOException {
            out.writeInt( strings.length );
            for ( String string : strings ) {
                out.writeUTF( string );
            }
        }

        public static String[] readStringArray( ObjectDataInput in ) throws IOException {
            int size = in.readInt();
            String[] strings = new String[ size ];

            for ( int i = 0; i < size; i++ ) {
                strings[ i ] = in.readUTF();
            }
            return strings;
        }

        public static void writeByteArray( ObjectOutput out, byte[] bytes ) throws IOException {
            out.writeInt( bytes.length );
            out.write( bytes, 0, bytes.length );
        }

        public static byte[] readByteArray( ObjectInput in ) throws IOException {
            int size = in.readInt();
            byte[] result = new byte[ size ];
            in.readFully( result );
            return result;
        }

        public static String loadResourceToString( final String path ) {
            String resource = null;
            try ( final InputStream stream = Thread.currentThread().getContextClassLoader()
                    .getResourceAsStream( path ) ) {
                resource = IOUtils.toString( stream );
            } catch ( final IOException | NullPointerException e ) {
                logger.error( "Failed to load resource from " + path, e );
                return null;
            }

            return resource;
        }
    }
}

@@ -81,36 +81,8 @@
 on;%0A
-import java.io.InputStream;%0A
 impo
@@ -130,24 +130,87 @@
 jectOutput;%0A
+import java.net.URL;%0Aimport java.nio.charset.StandardCharsets;%0A
 import java.
@@ -301,46 +301,8 @@
 t;%0A%0A
-import org.apache.commons.io.IOUtils;%0A
 impo
@@ -434,16 +434,55 @@
 t.Maps;%0A
+import com.google.common.io.Resources;%0A
 import c
@@ -5777,22 +5777,19 @@
         
-String
+URL
  resourc
@@ -5796,210 +5796,132 @@
 e = 
-null;%0A            try ( final InputStream stream = Thread.currentThread().getContextClassLoader()%0A                    .getResourceAsStream( path ) ) %7B%0A                resource = IOUtils.toString( stream
+Resources.getResource( path );%0A            try %7B%0A                return Resources.toString( resource, StandardCharsets.UTF_8
  );%0A
@@ -5964,31 +5964,8 @@
 ion 
-%7C NullPointerException 
 e ) 
@@ -6070,16 +6070,16 @@
 n null;%0A
+
         
@@ -6087,38 +6087,8 @@
    %7D
-%0A%0A            return resource;
 %0A   

use Guava Resources instead of our own stuff