/* 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.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.WeakHashMap;

/** Provides type conversion for instances of {@link NativeMapped}. */
public class NativeMappedConverter implements TypeConverter {
    private static final Map/*<Class,Reference<NativeMappedConverter>>*/ converters = new WeakHashMap();
    private final Class type;
    private final Class nativeType;
    private final NativeMapped instance;

    public static NativeMappedConverter getInstance(Class cls) {
        synchronized(converters) {
            Reference r = (Reference)converters.get(cls);
            NativeMappedConverter nmc = r != null ? (NativeMappedConverter)r.get() : null;
            if (nmc == null) {
                nmc = new NativeMappedConverter(cls);
                converters.put(cls, new SoftReference(nmc));
            }
            return nmc;
        }
    }

    public NativeMappedConverter(Class type) {
        if (!NativeMapped.class.isAssignableFrom(type))
            throw new IllegalArgumentException("Type must derive from "
                                               + NativeMapped.class);
        this.type = type;
        this.instance = defaultValue();
        this.nativeType = instance.nativeType();
    }

    public NativeMapped defaultValue() {
        try {
            return (NativeMapped)type.newInstance();
        }
        catch (InstantiationException e) {
            String msg = "Can't create an instance of " + type
                + ", requires a no-arg constructor: " + e;
            throw new IllegalArgumentException(msg);
        }
        catch (IllegalAccessException e) {
            String msg = "Not allowed to create an instance of " + type
                + ", requires a public, no-arg constructor: " + e;
            throw new IllegalArgumentException(msg);
        }
    }
    public Object fromNative(Object nativeValue, FromNativeContext context) {
        return instance.fromNative(nativeValue, context);
    }

    public Class nativeType() {
        return nativeType;
    }

    public Object toNative(Object value, ToNativeContext context) {
        if (value == null) {
            if (Pointer.class.isAssignableFrom(nativeType)) {
                return null;
            }
            value = defaultValue();
        }
        return ((NativeMapped)value).toNative();
    }
}