/*
 * Java Genetic Algorithm Library (@__identifier__@).
 * Copyright (c) @__year__@ Franz Wilhelmstötter
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author:
 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at)
 */
package org.jenetics.xml;

import static java.lang.String.format;
import static java.util.Objects.requireNonNull;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.IntFunction;

import javax.xml.stream.XMLStreamException;

import org.jenetics.DoubleGene;
import org.jenetics.IntegerGene;
import org.jenetics.LongGene;
import org.jenetics.xml.stream.AutoCloseableXMLStreamReader;
import org.jenetics.xml.stream.Reader;
import org.jenetics.xml.stream.XML;

/**
 * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
 * @version !__version__!
 * @since !__version__!
 */
public final class Readers {
	private Readers() {}

	@FunctionalInterface
	private static interface GeneCreator<A, G> {
		G create(final A value, final A min, final A max);
	}

	@FunctionalInterface
	private static interface ChromosomeCreator<G, C> {
		C create(final G[] genes);
	}

	// <bit-chromosome length="20" ones-probability="0.5">11100011101011001010</bit-chromosome>

	/**
	 * Bit chromosome reader methods.
	 */
	public static final class BitChromosome {
		public static Reader<org.jenetics.BitChromosome> reader() {
			return Reader.of(
				p -> {
					final int length = Integer.parseInt((String)p[0]);
					final double prob = Double.parseDouble((String)p[1]);
					return org.jenetics.BitChromosome.of((String)p[2]);
				},
				"bit-chromosome",
				Reader.attrs("length", "ones-probability")
			);
		}

		public static org.jenetics.BitChromosome read(final InputStream in)
			throws XMLStreamException
		{
			try (AutoCloseableXMLStreamReader reader = XML.reader(in)) {
				reader.next();
				return reader().read(reader);
			}
		}
	}

	public static final class CharacterChromosome {

	}

	public static final class PermutationChromosome {

	}

	/**
	 * Integer chromosome reader methods.
	 */
	public static final class IntegerChromosome {
		private IntegerChromosome() {}

		public static Reader<org.jenetics.IntegerChromosome> reader() {
			return chromosome(
				"integer-chromosome",
				Integer::parseInt,
				IntegerGene::of,
				IntegerGene[]::new,
				org.jenetics.IntegerChromosome::of
			);
		}

		public static org.jenetics.IntegerChromosome read(final InputStream in)
			throws XMLStreamException
		{
			try (AutoCloseableXMLStreamReader reader = XML.reader(in)) {
				reader.next();
				return reader().read(reader);
			}
		}

	}

	/**
	 * Long chromosome reader methods.
	 */
	public static final class LongChromosome {
		private LongChromosome() {}

		public static Reader<org.jenetics.LongChromosome> reader() {
			return chromosome(
				"long-chromosome",
				Long::parseLong,
				LongGene::of,
				LongGene[]::new,
				org.jenetics.LongChromosome::of
			);
		}

		public static org.jenetics.LongChromosome read(final InputStream in)
			throws XMLStreamException
		{
			try (AutoCloseableXMLStreamReader reader = XML.reader(in)) {
				reader.next();
				return reader().read(reader);
			}
		}

	}

	/**
	 * Double chromosome reader methods.
	 */
	public static final class DoubleChromosome {
		private DoubleChromosome() {}

		public static Reader<org.jenetics.DoubleChromosome> reader() {
			return chromosome(
				"double-chromosome",
				Double::parseDouble,
				DoubleGene::of,
				DoubleGene[]::new,
				org.jenetics.DoubleChromosome::of
			);
		}

		public static org.jenetics.DoubleChromosome read(final InputStream in)
			throws XMLStreamException
		{
			try (AutoCloseableXMLStreamReader reader = XML.reader(in)) {
				reader.next();
				return reader().read(reader);
			}
		}

	}

	public static final class Genotype {

	}

	public static final class Genotypes {

	}

	private static <A, G, C> Reader<C> chromosome(
		final String name,
		final Function<String, A> allele,
		final GeneCreator<A, G> gene,
		final IntFunction<G[]> genes,
		final ChromosomeCreator<G, C> chromosome
	) {
		requireNonNull(name);
		requireNonNull(allele);
		requireNonNull(gene);
		requireNonNull(genes);
		requireNonNull(chromosome);

		return Reader.of(
			p -> {
				final A min = allele.apply((String)p[0]);
				final A max = allele.apply((String)p[1]);
				final int length = Integer.parseInt((String)p[2]);

				@SuppressWarnings("unchecked")
				final List<A> alleles = (List<A>)p[3];
				if (alleles.size() != length) {
					throw new IllegalArgumentException(format(
						"Expected %d alleles, but got %d,",
						length, alleles.size()
					));
				}

				return chromosome.create(
					alleles.stream()
						.map(value -> gene.create(value, min, max))
						.toArray(genes)
				);
			},
			name,
			Reader.attrs("min", "max", "length"),
			Reader.ofList(Reader.of("allele").map(allele))
		);
	}

	public static void main(final String[] args) throws Exception {
		final org.jenetics.BitChromosome bch = org.jenetics.BitChromosome.of(10);

		final ByteArrayOutputStream out = new ByteArrayOutputStream();
		Writers.BitChromosome.write(bch, out);

		final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
		final org.jenetics.BitChromosome bch1 = BitChromosome.read(in);
		System.out.println(bch1);
	}

}

@@ -2177,57 +2177,137 @@
 %09%09%09%09
-return org.jenetics.BitChromosome.of((String)p%5B2%5D
+final String genes = (String)p%5B2%5D;%0A%0A%09%09%09%09%09return org.jenetics.BitChromosome.of(%0A%09%09%09%09%09%09genes.substring(0, length),%0A%09%09%09%09%09%09prob%0A%09%09%09%09%09
 );%0A%09

Fix marshaling.