/* Copyright (c) 2007 Timothy Wall, All Rights Reserved
 * 
 * This library 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 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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.  
 */
package com.sun.jna;

import java.util.Arrays;
import java.util.List;

import junit.framework.TestCase;

//@SuppressWarnings("unused")
public class UnionTest extends TestCase {

    public static class TestStructure extends Structure {
        public String value;
        protected List getFieldOrder() {
            return Arrays.asList(new String[] { "value" }); 
        }
    }
    
    public static class BigTestStructure extends Structure {
        public long field1;
        public long field2;
        protected List getFieldOrder() {
            return Arrays.asList(new String[] { "field1", "field2" }); 
        }
    }
    
    public static class IntStructure extends Structure {
        public int value;
        protected List getFieldOrder() {
            return Arrays.asList(new String[] { "value" }); 
        }
    }

    public static class SubIntStructure extends IntStructure {}

    public static interface Func1 extends Callback {
        public void callback();
    }

    public static class SizedUnion extends Union {
        public byte byteField;
        public short shortField;
        public int intField;
        public long longField;
        public TestStructure structField;
        public BigTestStructure structField2;
        public String string;
        public WString wstring;
        public Pointer pointer;
    }
    
    public static class StructUnion extends Union {
        public int intField;
        public TestStructure testStruct;
        public IntStructure intStruct;
        public Func1 func1;
    }

    public void testCalculateSize() {
        Union u = new SizedUnion();
        assertEquals("Union should be size of largest field",
                     new BigTestStructure().size(), u.size());
    }

    public void testFieldOffsets() {
        StructUnion u = new StructUnion();
        u.setType(u.testStruct.getClass());
        u.write();
        assertEquals("Wrong struct member base address", 
                     u.getPointer(), u.testStruct.getPointer());
        u.setType(u.intStruct.getClass());
        u.write();
        assertEquals("Wrong struct member base address (2)", 
                     u.getPointer(), u.intStruct.getPointer());
    }

    public void testWriteUnion() {
        SizedUnion u = new SizedUnion();
        final int VALUE = 0x12345678; 
        u.intField = VALUE;
        u.setType(int.class);
        u.write();
        assertEquals("Wrong value written", VALUE, u.getPointer().getInt(0));
    }
    
    public void testReadUnion() {
        SizedUnion u = new SizedUnion();
        final int VALUE = 0x12345678;
        u.getPointer().setInt(0, VALUE);
        u.read();
        assertEquals("int field not read properly", VALUE, u.intField);
        assertTrue("byte field not read", u.byteField != 0);
        assertTrue("short field not read", u.shortField != 0);
        assertTrue("long field not read", u.longField != 0);
        assertNotNull("Unselected Pointer not read", u.pointer);
        assertNotNull("Union struct field should be initialized", u.structField);
        assertNull("Unselected structure should not be read", u.structField.value);
        assertNull("Unselected String should be null", u.string);
        assertNull("Unselected WString should be null", u.wstring);
    }
    
    public void testWriteTypedUnion() {
        final int VALUE = 0x12345678;
        // write an instance of a direct union class to memory
        StructUnion u = new StructUnion();
        IntStructure intStruct = new IntStructure();
        intStruct.value = VALUE;
        u.setTypedValue(intStruct);
        u.write();
        assertEquals("Wrong value written", VALUE, u.getPointer().getInt(0));
        // write an instance of a sub class of an union class to memory
        u = new StructUnion();
        SubIntStructure subIntStructure = new SubIntStructure();
        subIntStructure.value = VALUE;
        u.setTypedValue(subIntStructure);
        u.write();
        assertEquals("Wrong value written", VALUE, u.getPointer().getInt(0));
        // write an instance of an interface
        u = new StructUnion();
        Func1 func1 = new Func1() {
            public void callback() {
                System.out.println("hi");
            }
        };
        u.setTypedValue(func1);
    }

    public void testReadTypedUnion() {
        StructUnion u = new StructUnion();
        final int VALUE = 0x12345678;
        u.getPointer().setInt(0, VALUE);
        assertEquals("int structure not read properly", VALUE, ((IntStructure) u.getTypedValue(IntStructure.class)).value);
    }

    public void testReadTypeInfo() {
        SizedUnion u = new SizedUnion();
        if (Native.POINTER_SIZE == 4) {
            assertEquals("Type size should be that of longest field if no field active",
                         Structure.getTypeInfo(BigTestStructure.class).getInt(0),
                         u.getTypeInfo().getInt(0));
        }
        else {
            assertEquals("Type size should be that of longest field if no field active",
                         Structure.getTypeInfo(BigTestStructure.class).getLong(0),
                         u.getTypeInfo().getLong(0));
        }
        u.setType(int.class);
        if (Native.POINTER_SIZE == 4) {
            assertEquals("Type size should be that of longest field if field active",
                         Structure.getTypeInfo(BigTestStructure.class).getInt(0),
                         u.getTypeInfo().getInt(0));
        }
        else {
            assertEquals("Type size should be that of longest field if field active",
                         Structure.getTypeInfo(BigTestStructure.class).getLong(0),
                         u.getTypeInfo().getLong(0));
        }
    }
    
    public void testArraysInUnion() {
        class TestUnion extends Union {
            public byte[] bytes = new byte[16];
            public short[] shorts = new short[8];
            public int[] ints = new int[4];
        }
        Union u = new TestUnion();
        u.setType(byte[].class);
        u.setType(short[].class);
        u.setType(int[].class);
    }

    public void testDuplicateFieldTypes() {
        class TestUnion extends Union {
            public int field1;
            public int field2; 
        }
        TestUnion u = new TestUnion();
        u.setType("field1");
        u.field1 = 42;
        u.write();
        u.setType("field2");
        u.read();
        assertEquals("Wrong field value after write/read", 42, u.field2);
    }

    public static void main(String[] args) {
        junit.textui.TestRunner.run(UnionTest.class);
    }
}
