/*
 * 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.felix.dm.runtime.itest.components;

import java.util.HashMap;
import java.util.Map;

import org.apache.felix.dm.annotation.api.Component;
import org.apache.felix.dm.annotation.api.Composition;
import org.apache.felix.dm.annotation.api.Destroy;
import org.apache.felix.dm.annotation.api.Init;
import org.apache.felix.dm.annotation.api.Property;
import org.apache.felix.dm.annotation.api.Registered;
import org.apache.felix.dm.annotation.api.ServiceDependency;
import org.apache.felix.dm.annotation.api.Start;
import org.apache.felix.dm.annotation.api.Stop;
import org.apache.felix.dm.itest.util.Ensure;
import org.osgi.framework.ServiceRegistration;

/**
 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
 */
@SuppressWarnings("rawtypes")
public class CompositeAnnotations {
    public interface C1Service {
    }
    
    public interface Dependency extends Runnable {        
    }

    /**
     * This service is also composed of the Component object.
     */
    @Component
    public static class C1 implements C1Service {
        public final static String ENSURE = "CompositeAnnotations.C1";
        
        /* We are composed of this object, which will also be injected with our dependencies */
        private final C2 m_c2 = new C2();

        /* This dependency filter will be configured from our init method */
        @ServiceDependency(name = "D")
        public volatile Dependency m_runnable;

        /* Object used to check that methods are called in the proper sequence */
        @ServiceDependency(filter = "(name=" + ENSURE + ")")
        private volatile Ensure m_sequencer;

        /**
         *  Dynamically configure our "D" dependency, using a dependency customization map 
         */
        @Init
        Map<String, String> init() {
            m_sequencer.step(1);
            // Configure a filter for our dependency whose name is "D"
            Map<String, String> customization = new HashMap<String, String>();
            customization.put("D.filter", "(foo=bar2)");
            customization.put("D.required", "true");
            return customization;
        }

        /**
         * Return the list of object our service is composed of
         */
        @Composition
        Object[] getComposition() {
            return new Object[]{this, m_c2};
        }

        /** 
         * Our Service is starting, and our Composites will also be 
         */
        @Start
        void start() {
            m_sequencer.step(3);
            m_runnable.run(); /* step 4 */
            // Our Component.start() method should be called once this method returns.
        }

        /**
         * Our provided service has been registered into the OSGi service registry.
         */
        @Registered
        void registered(ServiceRegistration sr) {
            m_sequencer.step(7);
        }

        /**
         *  Our Service is stopping, and our Composites will also be 
         */
        @Stop
        void stop() {
            m_sequencer.step(9);
            // Our Component.stop() method should be called once this method returns.
        }

        /**
         * Our Service is destroying, and our Composites will also be.
         */
        @Destroy
        void destroy() {
            m_sequencer.step(11);
            // Our Component.destroy() method should be called once this method returns.
        }
    }

    /**
     * The CompositeService is also made up of this Class.
     */
    public static class C2 {
        // Injected dependency (from CompositeService)
        private volatile Ensure m_sequencer;

        // Injected dependency (from CompositeService)
        public volatile Dependency m_runnable;

        // lifecycle callback (same method as the one from CompositeService)
        void init() {
            m_sequencer.step(2);
        }

        // lifecycle callback (same method as the one from CompositeService)
        void start() {
            m_sequencer.step(5);
            m_runnable.run(); /* step 6 */
        }

        void registered(ServiceRegistration sr) {
            if (sr == null) {
                m_sequencer.throwable(new Exception("null service registration"));
            }
            m_sequencer.step(8);
        }

        // lifecycle callback (same method as the one from CompositeService)
        void stop() {
            m_sequencer.step(10);
        }

        // lifecycle callback (same method as the one from CompositeService)
        void destroy() {
            m_sequencer.step(12);
        }
    }

    @Component
    @Property(name = "foo", value = "bar1")
    public static class Dependency1 implements Dependency {
        public final static String ENSURE = "CompositeAnnotations.Dependency1";

        @ServiceDependency(filter = "(name=" + ENSURE + ")")
        volatile Ensure m_sequencer;

        @Start
        void start() {
            System.out.println("Dependency1.start");
        }

        public void run() {
            m_sequencer.step(Integer.MAX_VALUE); // Makes the test fail.
        }
    }

    @Component
    @Property(name = "foo", value = "bar2")
    public static class Dependency2 implements Dependency {
        public final static String ENSURE = "CompositeAnnotations.Dependency2";

        @ServiceDependency(filter = "(name=" + ENSURE + ")")
        volatile Ensure m_sequencer;

        @Start
        void start() {
            System.out.println("Dependency2.start");
        }

        public void run() {
            m_sequencer.step();
        }
    }
}
