/*
 *     Copyright (C) 2015  higherfrequencytrading.com
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package net.openhft.chronicle.wire.type.conversions.binary;

import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.wire.BinaryWire;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.Assert;
import org.junit.Test;

import java.lang.reflect.Field;

/**
 * Created by Rob Austin
 */
public class ConventionsTest {

    @Test
    public void testTypeConversionsMaxValue() throws NoSuchFieldException, IllegalAccessException {

        for (Class type : new Class[]{String.class, Integer.class, Long.class, Short
                .class, Byte
                .class, Float.class, Double.class}) {
            Object extected;
            if (Number.class.isAssignableFrom(type)) {
                System.out.println("" + type + "");
                final Field max_value = type.getField("MAX_VALUE");
                extected = max_value.get(type);
            } else {
                extected = "123"; // small number
            }

            Assert.assertEquals("type=" + type, extected, test(extected, type));
        }
    }

    @Test
    public void testTypeConversionsMinValue() throws IllegalAccessException, NoSuchFieldException {

        for (Class type : new Class[]{String.class, Integer.class, Long.class, Short.class, Byte
                .class, Float.class, Double.class}) {
            Object extected;
            if (Number.class.isAssignableFrom(type)) {
                System.out.println("" + type + "");
                final Field value = type.getField("MIN_VALUE");
                extected = value.get(type);
            } else {
                extected = "123";
            }

            Assert.assertEquals("type=" + type, extected, test(extected, type));
        }
    }

    @Test
    public void testTypeConversionsSmallNumber() {

        for (Class type : new Class[]{String.class, Integer.class, Long.class, Short
                .class, Byte.class}) {

            Object extected = "123"; // small number
            Assert.assertEquals("type=" + type, extected, String.valueOf(test(extected, type)));
        }

        Assert.assertEquals(123.0, test("123", Double.class), 0);
        Assert.assertEquals(123.0, (double) (Float) test("123", Float.class), 0);

    }

    @Test
    public void testTypeConversionsConvertViaString() throws NoSuchFieldException, IllegalAccessException {

        for (Class type : new Class[]{Integer.class, Long.class, Short.class, Byte
                .class}) {
            Object extected;
            if (Number.class.isAssignableFrom(type)) {
                final Field max_value = type.getField("MAX_VALUE");
                extected = max_value.get(type);
            } else {
                extected = 123;
            }

            final Object value = test(extected, String.class);
            final Object actual = test(value, extected.getClass());

            Assert.assertEquals("type=" + type, extected, actual);
        }
    }

    @Test
    public void testTypeConversionsMaxUnsigned() {

        for (long shift : new long[]{8}) {
            long extected = 1L << shift;
            Assert.assertEquals(extected, (long) test(extected, Long.class));
        }
    }

    @Nullable
    public <T> T test(Object source, @NotNull Class<T> destinationType) {

        final BinaryWire wire = new BinaryWire(Bytes.elasticByteBuffer());

        if (source instanceof String)
            wire.getValueOut().text((String) source);
        else if (source instanceof Long)
            wire.getValueOut().int64((Long) source);
        else if (source instanceof Integer)
            wire.getValueOut().int32((Integer) source);
        else if (source instanceof Short)
            wire.getValueOut().int16((Short) source);
        else if (source instanceof Byte)
            wire.getValueOut().int8((Byte) source);
        else if (source instanceof Float)
            wire.getValueOut().float32((Float) source);
        else if (source instanceof Double)
            wire.getValueOut().float64((Double) source);

        if (String.class.isAssignableFrom(destinationType))
            return (T) wire.getValueIn().text();

        if (Long.class.isAssignableFrom(destinationType))
            return (T) (Long) wire.getValueIn().int64();

        if (Integer.class.isAssignableFrom(destinationType))
            return (T) (Integer) wire.getValueIn().int32();

        if (Short.class.isAssignableFrom(destinationType))
            return (T) (Short) wire.getValueIn().int16();

        if (Byte.class.isAssignableFrom(destinationType))
            return (T) (Byte) wire.getValueIn().int8();

        if (Float.class.isAssignableFrom(destinationType))
            return (T) (Float) wire.getValueIn().float32();

        if (Double.class.isAssignableFrom(destinationType))
            return (T) (Double) wire.getValueIn().float64();

        throw new UnsupportedOperationException("");
    }
}
