package com.fasterxml.jackson.databind.introspect;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyName;

/* Tests mostly for [JACKSON-754]: ability to create "annotation bundles"
 */
public class TestAnnotationBundles extends com.fasterxml.jackson.databind.BaseMapTest
{
    @Retention(RetentionPolicy.RUNTIME)
    @JacksonAnnotationsInside
    @JsonIgnore
    private @interface MyIgnoral { }

    @Retention(RetentionPolicy.RUNTIME)
    @JacksonAnnotationsInside
    @JsonProperty("foobar")
    private @interface MyRename { }

    protected final static class Bean {
        @MyIgnoral
        public String getIgnored() { return "foo"; }
 
        @MyRename
        public int renamed = 13;
    }

    @Retention(RetentionPolicy.RUNTIME)
    @JsonAutoDetect(fieldVisibility=Visibility.NONE,
            getterVisibility=Visibility.NONE, isGetterVisibility=Visibility.NONE)
    @JacksonAnnotationsInside
    public @interface JsonAutoDetectOff {}

    @JsonAutoDetectOff
    public class NoAutoDetect {
      public int getA() { return 13; }
      
      @JsonProperty
      public int getB() { return 5; }
    }

    @Retention(RetentionPolicy.RUNTIME)
    @JacksonAnnotationsInside
    @JsonProperty("_id")
    public @interface Bundle92 {}

    public class Bean92 {
        @Bundle92
        protected String id = "abc";
    }

    @HolderB
    @JacksonAnnotationsInside
    @Retention(RetentionPolicy.RUNTIME)
    static @interface HolderA {}

    @HolderA
    @JacksonAnnotationsInside
    @Retention(RetentionPolicy.RUNTIME)
    static @interface HolderB {}

    static class RecursiveHolder {
        @HolderA public int unimportant = 42;
    }

    static class RecursiveHolder2 {
        @HolderA public int getValue() { return 28; }
    }

    static class RecursiveHolder3 {
        public int x;

        @JsonCreator
        @HolderA
        public RecursiveHolder3(int x) { this.x = x; }
    }
    
    @JsonProperty
    @JacksonAnnotationsInside
    @Retention(RetentionPolicy.RUNTIME)
    static @interface InformativeHolder {
        // doesn't really contribute to the test, but would be impossible without this feature
        boolean important() default true;
    }

    static class InformingHolder {
        @InformativeHolder public int unimportant = 42;
    }

    @SuppressWarnings("serial")
    static class BundleAnnotationIntrospector extends JacksonAnnotationIntrospector {
        @Override
        public PropertyName findNameForSerialization(Annotated a)
        {
            InformativeHolder informativeHolder = a.getAnnotation(InformativeHolder.class);
            if ((informativeHolder != null) && informativeHolder.important()) {
                return PropertyName.construct("important");
            }
            return super.findNameForSerialization(a);
        }
    }

    /*
    /**********************************************************
    /* Test methods
    /**********************************************************
     */

    private final ObjectMapper MAPPER = new ObjectMapper();

    public void testKeepAnnotationBundle() throws Exception
    {
        MAPPER.setAnnotationIntrospector(new BundleAnnotationIntrospector());
        assertEquals("{\"important\":42}", MAPPER.writeValueAsString(new InformingHolder()));
    }

    public void testRecursiveBundlesField() throws Exception {
        assertEquals("{\"unimportant\":42}", MAPPER.writeValueAsString(new RecursiveHolder()));
    }

    public void testRecursiveBundlesMethod() throws Exception {
        assertEquals("{\"value\":28}", MAPPER.writeValueAsString(new RecursiveHolder2()));
    }

    public void testRecursiveBundlesConstructor() throws Exception {
        RecursiveHolder3 result = MAPPER.readValue("17", RecursiveHolder3.class);
        assertNotNull(result);
        assertEquals(17, result.x);
    }
    
    public void testBundledIgnore() throws Exception
    {
        assertEquals("{\"foobar\":13}", MAPPER.writeValueAsString(new Bean()));
    }

    public void testVisibilityBundle() throws Exception
    {
        assertEquals("{\"b\":5}", MAPPER.writeValueAsString(new NoAutoDetect()));
    }
    
    public void testIssue92() throws Exception
    {
        assertEquals("{\"_id\":\"abc\"}", MAPPER.writeValueAsString(new Bean92()));
    }
}
