/*
 *  This file is part of the ETECTURE Open Source Community Projects.
 *
 *  Copyright (c) 2013 by:
 *
 *  ETECTURE GmbH
 *  Darmstädter Landstraße 112
 *  60598 Frankfurt
 *  Germany
 *
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *
 *  3. Neither the name of the author nor the names of its contributors may be
 *     used to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 *  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 *  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package de.etecture.opensource.dynamicresources.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.inject.spi.PassivationCapable;
import javax.enterprise.util.Nonbinding;

/**
 *
 * @param <T>
 * @author rhk
 * @version
 * @since
 */
public abstract class AbstractBean<T> implements Bean<T>, PassivationCapable {

    private final Class<T> beanClass;
    private final BeanManager beanManager;
    private final String name;
    private final Set<Type> beanTypes;
    private final Set<Annotation> qualifier = new HashSet<>();
    private final Set<Class<? extends Annotation>> stereotypes = new HashSet<>();
    private final Class<? extends Annotation> scope;
    private final boolean alternative;
    private final boolean nullable;
    private final String id = UUID.randomUUID().toString();

    public AbstractBean(BeanManager beanManager, Class<T> beanClass, String name,
                        Class<? extends Annotation> scope, boolean alternative,
                        boolean nullable,
                        Type... beanTypes) {
        this.beanManager = beanManager;
        this.beanClass = beanClass;
        this.name = name;
        this.scope = scope;
        this.alternative = alternative;
        this.nullable = nullable;
        this.beanTypes = new HashSet<>(Arrays.asList(beanTypes));
    }

    @Override
    public String getId() {
        return id;
    }

    @Override
    public Class<?> getBeanClass() {
        return beanClass;
    }

    @Override
    public Set<InjectionPoint> getInjectionPoints() {
        return Collections.emptySet();
    }

    @Override
    public boolean isNullable() {
        return nullable;
    }

    @Override
    public Set<Type> getTypes() {
        return Collections.unmodifiableSet(beanTypes);
    }

    @Override
    public Set<Annotation> getQualifiers() {
        return Collections.unmodifiableSet(qualifier);
    }

    @Override
    public Class<? extends Annotation> getScope() {
        return scope;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public Set<Class<? extends Annotation>> getStereotypes() {
        return Collections.unmodifiableSet(stereotypes);
    }

    @Override
    public boolean isAlternative() {
        return alternative;
    }

    void addQualifier(Annotation qa) {
        this.qualifier.add(qa);
    }

    void addStereotype(Class<? extends Annotation> s) {
        this.stereotypes.add(s);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("Bean: ");
        sb.append("@").append(scope.getSimpleName()).append(" ");
        for (Annotation q : qualifier) {
            sb.append("@");
            sb.append(q.annotationType().getSimpleName());
            if (q.annotationType().getDeclaredMethods().length > 0) {
                sb.append("(");
                boolean first = true;
                for (Method method : q.annotationType().getDeclaredMethods()) {
                    if (!method.isAnnotationPresent(Nonbinding.class)) {
                        if (!first) {
                            sb.append(", ");
                        } else {
                            first = false;
                        }
                        try {
                            sb.append(method.getName()).append("=").append(
                                    method
                                    .invoke(q));
                        } catch (IllegalAccessException |
                                IllegalArgumentException |
                                InvocationTargetException ex) {
                            sb.append("<unknown>");
                        }
                    }
                }
                sb.append(")");
            }
            sb.append(" ");
        }
        sb.append("\"").append(name).append("\" ");
        sb.append(beanClass.getName());
        return sb.toString();
    }
}
