package de.retest.recheck.persistence.bin;

import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.objenesis.strategy.StdInstantiatorStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.KryoException;
import com.esotericsoftware.kryo.Registration;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.google.common.collect.TreeMultiset;

import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer;
import de.retest.recheck.persistence.IncompatibleReportVersionException;
import de.retest.recheck.persistence.Persistable;
import de.retest.recheck.persistence.Persistence;
import de.retest.recheck.util.FileUtil;
import de.retest.recheck.util.VersionProvider;

public class KryoPersistence<T extends Persistable> implements Persistence<T> {

	private static final String OLD_RECHECK_VERSION = "an old recheck version (pre 1.5.0)";

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

	private final Kryo kryo;
	private final String version;

	public KryoPersistence() {
		this( createKryo(), VersionProvider.RETEST_VERSION );
	}

	/**
	 * Testing only!
	 *
	 * @param kryo
	 *            {@code Kryo} instance to use.
	 * @param version
	 *            recheck version to use.
	 */
	KryoPersistence( final Kryo kryo, final String version ) {
		this.kryo = kryo;
		this.version = version;
	}

	private static Kryo createKryo() {
		final Kryo kryo = new Kryo();

		kryo.setInstantiatorStrategy( new Kryo.DefaultInstantiatorStrategy( new StdInstantiatorStrategy() ) );

		final Registration registration = kryo.getRegistration( TreeMultiset.class );
		registration.setInstantiator( TreeMultiset::create );

		UnmodifiableCollectionsSerializer.registerSerializers( kryo );

		return kryo;
	}

	@Override
	public void save( final URI identifier, final T element ) throws IOException {
		final Path path = Paths.get( identifier );
		FileUtil.ensureFolder( path.toFile() );
		try ( final Output output = new Output( Files.newOutputStream( path ) ) ) {
			output.writeString( version );
			logger.debug( "Writing {} to {}. Do not write to same identifier or interrupt until done.", element,
					identifier );
			kryo.writeClassAndObject( output, element );
			logger.debug( "Done writing {} to {}", element, identifier );
		}
	}

	private final String VERSION_CHARS = "[\\w\\.\\{\\}\\$]+";

	@SuppressWarnings( "unchecked" )
	@Override
	public T load( final URI identifier ) throws IOException {
		String writerVersion = OLD_RECHECK_VERSION;
		try ( final Input input = new Input( Files.newInputStream( Paths.get( identifier ) ) ) ) {
			writerVersion = input.readString();
			return (T) kryo.readClassAndObject( input );
		} catch ( final KryoException e ) {
			if ( version.equals( writerVersion ) ) {
				throw e;
			}
			if ( writerVersion == null || !writerVersion.matches( VERSION_CHARS ) ) {
				writerVersion = OLD_RECHECK_VERSION;
			}
			throw new IncompatibleReportVersionException( writerVersion, version, identifier, e );
		}
	}

}

@@ -168,16 +168,80 @@
 e.Paths;
+%0Aimport java.util.regex.Matcher;%0Aimport java.util.regex.Pattern;
 %0A%0Aimport
@@ -2551,69 +2551,8 @@
 %09%7D%0A%0A
-%09private final String VERSION_CHARS = %22%5B%5C%5Cw%5C%5C.%5C%5C%7B%5C%5C%7D%5C%5C$%5D+%22;%0A%0A
 %09@Su
@@ -2581,16 +2581,16 @@
 cked%22 )%0A
+
 %09@Overri
@@ -2677,35 +2677,20 @@
 rsion = 
-OLD_RECHECK_VERSION
+null
 ;%0A%09%09try 
@@ -2975,70 +2975,38 @@
 f ( 
-writerVersion == null %7C%7C !writerVersion.matches( VERSION_CHARS
+isUnknownFormat( writerVersion
  ) )
@@ -3144,18 +3144,18 @@
 r, e );%0A
-
 %09%09%7D%0A%09%7D%0A%0A
 %7D%0A
@@ -3152,10 +3152,310 @@
 %09%09%7D%0A%09%7D%0A%0A
+%09private static final Pattern VERSION_CHARS = Pattern.compile( %22%5B%5C%5Cw%5C%5C.%5C%5C%7B%5C%5C%7D%5C%5C$%5D+%22 );%0A%0A%09private static boolean isUnknownFormat( final String writerVersion ) %7B%0A%09%09if ( writerVersion == null ) %7B%0A%09%09%09return true;%0A%09%09%7D%0A%09%09final Matcher m = VERSION_CHARS.matcher( writerVersion );%0A%09%09return !m.matches();%0A%09%7D%0A%0A
 %7D%0A

Improve code after review