/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 *
 */
package org.apache.qpid.proton.codec;

import static org.junit.Assert.assertEquals;

import java.lang.Character.UnicodeBlock;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;

import org.junit.Test;

import org.apache.qpid.proton.amqp.messaging.AmqpValue;

/**
 * Test the encoding and decoding of {@link StringType} values.
 */
public class StringTypeTest
{
    /**
     * Loop over all the chars in a given {@link UnicodeBlock} and return a
     * {@link Set <String>} containing all the possible values as their
     * {@link String} values.
     *
     * @param block the {@link UnicodeBlock} to loop over
     * @return a {@link Set <String>} containing all the possible values as
     * {@link String} values
     */
    private static Set<String> getAllStringsFromUnicodeBlock(final UnicodeBlock block)
    {
        final Set<String> strings = new HashSet<String>();
        for (int codePoint = 0; codePoint <= Character.MAX_CODE_POINT; codePoint++)
        {
            if (UnicodeBlock.of(codePoint) == block)
            {
                final int charCount = Character.charCount(codePoint);
                final StringBuilder sb = new StringBuilder(
                        charCount);
                if (charCount == 1)
                {
                    sb.append(String.valueOf((char) codePoint));
                }
                else if (charCount == 2)
                {
                    sb.append(Character.highSurrogate(codePoint));
                    sb.append(Character.lowSurrogate(codePoint));
                }
                else
                {
                    throw new IllegalArgumentException("Character.charCount of "
                                                       + charCount + " not supported.");
                }
                strings.add(sb.toString());
            }
        }
        return strings;
    }


    /**
     * Test the encoding and decoding of various complicated Unicode characters
     * which will end up as "surrogate pairs" when encoded to UTF-8
     */
    @Test
    public void calculateUTF8Length()
    {
        for (final String input : generateTestData())
        {
            assertEquals("Incorrect string length calculated for string '"+input+"'",input.getBytes(StandardCharsets.UTF_8).length, StringType.calculateUTF8Length(input));
        }
    }

    /**
     * Test the encoding and decoding of various  Unicode characters
     */
    @Test
    public void encodeDecodeStrings()
    {
        final DecoderImpl decoder = new DecoderImpl();
        final EncoderImpl encoder = new EncoderImpl(decoder);
        AMQPDefinedTypes.registerAllTypes(decoder, encoder);
        final ByteBuffer bb = ByteBuffer.allocate(16);

        for (final String input : generateTestData())
        {
            bb.clear();
            final AmqpValue inputValue = new AmqpValue(input);
            encoder.setByteBuffer(bb);
            encoder.writeObject(inputValue);
            bb.clear();
            decoder.setByteBuffer(bb);
            final AmqpValue outputValue = (AmqpValue) decoder.readObject();
            assertEquals("Failed to round trip String correctly: ", input, outputValue.getValue());
        }
    }

    // build up some test data with a set of suitable Unicode characters
    private Set<String> generateTestData()
    {
        return new HashSet<String>()
            {
                private static final long serialVersionUID = 7331717267070233454L;

                {
                    // non-surrogate pair blocks
                    addAll(getAllStringsFromUnicodeBlock(UnicodeBlock.BASIC_LATIN));
                    addAll(getAllStringsFromUnicodeBlock(UnicodeBlock.LATIN_1_SUPPLEMENT));
                    addAll(getAllStringsFromUnicodeBlock(UnicodeBlock.GREEK));
                    addAll(getAllStringsFromUnicodeBlock(UnicodeBlock.LETTERLIKE_SYMBOLS));
                    // blocks with surrogate pairs
                    addAll(getAllStringsFromUnicodeBlock(UnicodeBlock.MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS));
                    addAll(getAllStringsFromUnicodeBlock(UnicodeBlock.MUSICAL_SYMBOLS));
                    addAll(getAllStringsFromUnicodeBlock(UnicodeBlock.EMOTICONS));
                    addAll(getAllStringsFromUnicodeBlock(UnicodeBlock.PLAYING_CARDS));
                    addAll(getAllStringsFromUnicodeBlock(UnicodeBlock.SUPPLEMENTARY_PRIVATE_USE_AREA_A));
                    addAll(getAllStringsFromUnicodeBlock(UnicodeBlock.SUPPLEMENTARY_PRIVATE_USE_AREA_B));
                }
            };
    }
}

@@ -985,32 +985,23 @@
 charset.
-Standard
 Charset
-s
 ;%0Aimport
@@ -1226,16 +1226,91 @@
 eTest%0A%7B%0A
+    private static final Charset CHARSET_UTF8 = Charset.forName(%22UTF-8%22);%0A%0A
     /**%0A
@@ -2356,34 +2356,75 @@
             
-sb.append(
+//TODO: use Character.highSurrogate(codePoint) and 
 Character.hi
@@ -2425,75 +2425,318 @@
 ter.
-highSurrogate(codePoint));%0A                    sb.append(Character.
+lowSurrogate(codePoint) when Java 7 is baseline%0A                    char highSurrogate = (char) ((codePoint %3E%3E%3E 10) + ('%5CuD800' - (0x010000 %3E%3E%3E 10)));%0A                    char lowSurrogate =  (char) ((codePoint & 0x3ff) + '%5CuDC00');%0A%0A                    sb.append(highSurrogate);%0A                    sb.append(
 lowS
@@ -2743,27 +2743,16 @@
 urrogate
-(codePoint)
 );%0A     
@@ -3477,29 +3477,19 @@
 tes(
-StandardCharsets.
+CHARSET_
 UTF
-_
 8).l
@@ -5151,32 +5151,94 @@
                 
+//TODO: restore when Java 7 is baseline%0A                    //
 addAll(getAllStr
@@ -5413,32 +5413,94 @@
                 
+//TODO: restore when Java 7 is baseline%0A                    //
 addAll(getAllStr
@@ -5558,32 +5558,34 @@
                 
+//
 addAll(getAllStr

update test to make it compile on Java 6