package com.salesmanager.shop.utils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import br.ufrgs.inf.prosoft.cache.SingleCache;
import br.ufrgs.inf.prosoft.cache.Parameters;

public class BeanUtils
{
 private BeanUtils(){
        
    }
    
    public static BeanUtils newInstance(){
        return new BeanUtils();
    }

public static SingleCache<Parameters, Object> getPropertyValueCache = new SingleCache<>("BeanUtils.getPropertyValue");
    
    @SuppressWarnings( "nls" )
    public Object getPropertyValue( Object bean, String property )
        // throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
    {
        return getPropertyValueCache.computeIfAbsent(new Parameters(bean, property),  () -> {
        
            if (bean == null) {
                // throw new IllegalArgumentException("No bean specified");
                throw new RuntimeException("No bean specified");
            }
            if(property == null){
                
                // throw new IllegalArgumentException("No name specified for bean class '" + bean.getClass() + "'");
                throw new RuntimeException("No name specified for bean class '" + bean.getClass() + "'");
            }
try {

            Class<?> beanClass = bean.getClass();
            PropertyDescriptor propertyDescriptor = getPropertyDescriptor( beanClass, property );
            if ( propertyDescriptor == null )
            {
                // throw new IllegalArgumentException( "No such property " + property + " for " + beanClass + " exists" );
                throw new RuntimeException( "No such property " + property + " for " + beanClass + " exists" );
            }

            Method readMethod = propertyDescriptor.getReadMethod();
            if ( readMethod == null )
            {
                // throw new IllegalStateException( "No getter available for property " + property + " on " + beanClass );
                throw new RuntimeException( "No getter available for property " + property + " on " + beanClass );
            }
            return readMethod.invoke( bean );
} catch (InvocationTargetException | IllegalAccessException ex) {
    throw new RuntimeException(ex);
}
        }, 1200000);
    }

public static SingleCache<Parameters, PropertyDescriptor> getPropertyDescriptorCache = new SingleCache<>("BeanUtils.getPropertyDescriptor");

    private PropertyDescriptor getPropertyDescriptor( Class<?> beanClass, String propertyname )
        // throws IntrospectionException
    {
        return getPropertyDescriptorCache.computeIfAbsent(new Parameters(beanClass, propertyname),  () -> {
            try {

            BeanInfo beanInfo = Introspector.getBeanInfo( beanClass );
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            PropertyDescriptor propertyDescriptor = null;
            for ( int i = 0; i < propertyDescriptors.length; i++ )
            {
                PropertyDescriptor currentPropertyDescriptor = propertyDescriptors[i];
                if ( currentPropertyDescriptor.getName().equals( propertyname ) )
                {
                    propertyDescriptor = currentPropertyDescriptor;
                }

            }
            return propertyDescriptor;
            } catch (IntrospectionException ex) {
                throw new RuntimeException(ex);
            }
        }, 1200000);
    }
    
}
