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

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

import java.util.ArrayList;
import java.util.List;
import net.ranides.assira.reflection.IAttribute;
import net.ranides.assira.reflection.IClass;
import net.ranides.assira.reflection.IMethod;
import net.ranides.assira.reflection.util.ClassUtils;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class RBeanPropertyInfo {
    
    private static final IClass<?> IC_INT = IClass.typeinfo(int.class);
    
    private final String name;
    
    private final List<IMethod> vsetters = new ArrayList<>(2);
    private final List<IMethod> isetters = new ArrayList<>(2);

    private IMethod vgetter;
    private IMethod vsetter;
    
    private IMethod igetter;
    private IMethod isetter;
    
    private IClass<?> type;
    private IClass<?> item;

    public RBeanPropertyInfo(String name) {
        this.name = name;
    }
    
    public String name() {
        return name;
    }
    
    public IMethod vgetter() {
        return vgetter;
    }
    
    public IMethod vsetter() {
        return vsetter;
    }
    
    public IMethod igetter() {
        return igetter;
    }
    
    public IMethod isetter() {
        return isetter;
    }
    
    public IClass<?> type() {
        return type;
    }
    
    public IClass<?> item() {
        return item;
    }
    
    void vgetter(IMethod m) {
        vgetter = m;
    }
    
    void vsetter(IMethod m) {
        vsetters.add(m);
    }
    
    void igetter(IMethod m) {
        igetter = m;
    }
    
    void isetter(IMethod m) {
        isetters.add(m);
    }
    
    void validate() {
        initVSetter();
        initItem();
        initISetter();
        initType();
    }

    private void initVSetter() throws IllegalArgumentException {
        if(vgetter != null) {
            type = vgetter.returns();
            vsetter = vsetters.stream().filter(m -> m.matches(type)).findFirst().orElse(null);
        } else {
            if( vsetters.size() > 1) {
                throw new IllegalArgumentException("Ambigous property setters for: " + name);
            }
            if( vsetters.size() == 1) {
                vsetter = vsetters.get(0);
                type = vsetter.arguments().types().first().get();
            }
		}
    }
    
    private void initItem() throws IllegalArgumentException {
        if(type!=null && IAttribute.ARRAY.matches(type)) {
            item = type.component();
        }
        if(igetter != null) {
            IClass<?> iitem = igetter.returns();
            if( item!=null && !iitem.equals(item) ) {
                throw new IllegalArgumentException("Invalid array getter for: " + name);
            }
            item = iitem;
        }
    }
    
    private void initISetter() throws IllegalArgumentException {
        if(item != null) {
            isetter = isetters.stream().filter(m -> m.matches(IC_INT, item)).findFirst().orElse(null);
        } else {
            if( isetters.size() > 1) {
                throw new IllegalArgumentException("Ambigous property setters for: " + name);
            }
            if( isetters.size() == 1) {
                isetter = isetters.get(0);
                item = isetter.arguments().types().list().get(1);
            }
        }
    }
    
    private void initType() throws IllegalArgumentException {
        if( null != item && null!=type) {
            IClass<?> ctype = null;
            if(IAttribute.ARRAY.matches(type)) {
                ctype = type.component();
            }
            if(type.isSubclass(IClass.generic(List.class, item))) {
                ctype = type.method("get").get().returns();
            }
            if( !item.equals(ctype)) {
                throw new IllegalArgumentException("Invalid type of indexed property: " + name);
            }
        }
        if(null == type && null==item) {
            throw new IllegalArgumentException("Unknown type of indexed property: " + name);
        }
        if(null == type) {
            type = IClass.generic(List.class, ClassUtils.box(item));
        }
        
    }

}
