/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */

package net.ranides.assira.reflection.impl.bean;

import lombok.experimental.UtilityClass;
import net.ranides.assira.collection.maps.CacheMap;
import net.ranides.assira.reflection.*;

import java.util.ArrayList;
import java.util.Collection;

/**
 * Factory class for BeanModel.
 *
 * It creates BeanModel, BeanMethod and BeanProperty implementations.
 *
 * It caches results using class as a key.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
@UtilityClass
public final class FBeanModel {
    
    private static final CacheMap<IClass<?>,BeanModel> CACHE = CacheMap.getInstance(RBeanModel::new);

    /**
     * Verifies if specified class meets bean requirements. Skips caching.
     *
     * Internal method used by BeanModel#verify
     *
     * @param type type
     * @return BeanModel
     */
    public static Collection<BeanException> verify(IClass<?> type) {
        Collection<BeanException> errors = new ArrayList<>();
        try {
            new RBeanModel(type, errors);
        } catch (BeanException e) {
            errors.add(e);
        } catch (Exception e) {
            errors.add(new BeanException(type, e));
        }
        return errors;
    }

    /**
     * Returns BeanModel for specified class. Caches results.
     *
     * Internal method used by BeanModel#typeinfo
     *
     * @param type type
     * @return BeanModel
     */
    public static BeanModel resolve(IClass<?> type) {
        return CACHE.get(type);
    }

    /**
     * Wraps provided methods by BeanMethod adapter.
     * If collection size is one, it returns simple delegator.
     * If collection size is greater, it returns multiple dispatch implementation.
     *
     * Internal method used by RBeanModel
     *
     * @param methods methods
     * @return BeanMethod
     */
    public static BeanMethod resolve(Collection<BeanException> errors, Collection<IMethod> methods) {
        if(methods.size() > 1) {
            return new RBeanMethods(errors, methods);
        } else {
            return new RBeanMethod(errors, methods.iterator().next());
        }
    }

    /**
     * Creates new BeanProperty.
     * Selects different implementations for array properties, indexed properties or ordinary fields.
     * It uses information collected inside RBeanPropertyBuilder, does not inspect bean on its own.
     *
     * Internal method used by RBeanModel
     *
     * @param info info
     * @return BeanProperty
     */
    public static BeanProperty resolve(Collection<BeanException> errors, RBeanPropertyBuilder info) {
        if(!info.validate(errors)) {
            return null;
        }
        if( info.type().attributes().has(IAttribute.ARRAY) ) {
            return new RPArray(info);
        } 
        if( info.item()!=null ) {
            return new RPIndexed(info);
        } 
        return new RPField(info);
    }

    /**
     * Internal shared method: creates default exception thrown by classes inside this package.
     *
     * @param message message
     * @return RuntimeException
     */
    public static UnsupportedOperationException newUnsupportedOperationException(String message) {
        return new UnsupportedOperationException(new IReflectiveException(message));
    }


}
