package com.alibaba.qlexpress4.runtime.data.convert;

import com.alibaba.qlexpress4.proxy.QLambdaInvocationHandler;
import com.alibaba.qlexpress4.runtime.Nothing;
import com.alibaba.qlexpress4.runtime.QLambda;
import com.alibaba.qlexpress4.runtime.operator.number.NumberMath;
import com.alibaba.qlexpress4.utils.CacheUtil;

import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.math.BigInteger;

/**
 * Author: TaoKan
 */
public class ObjTypeConvertor {
    
    public static QConverted cast(Object value, Class<?> type) {
        // no need to convert
        if (noNeedConvert(value, type)) {
            return converted(value);
        }
        if (type == Character.class || type == char.class) {
            return castChar(value);
        }
        if (CacheUtil.isFunctionInterface(type)) {
            return castFunctionInter(value, type);
        }
        // unboxed boxed
        if (type == boolean.class || type == Boolean.class) {
            if (value instanceof Boolean) {
                return converted(value);
            }
            return unConvertible();
        }
        if (type == byte.class || type == Byte.class) {
            if (value instanceof Byte) {
                return converted(value);
            }
            if (value instanceof Number) {
                return converted(((Number)value).byteValue());
            }
            return unConvertible();
        }
        if (type == short.class || type == Short.class) {
            if (value instanceof Short) {
                return converted(value);
            }
            if (value instanceof Number) {
                return converted(((Number)value).shortValue());
            }
            return unConvertible();
        }
        if (type == int.class || type == Integer.class) {
            if (value instanceof Integer) {
                return converted(value);
            }
            if (value instanceof Number) {
                return converted(((Number)value).intValue());
            }
            return unConvertible();
        }
        if (type == long.class || type == Long.class) {
            if (value instanceof Long) {
                return converted(value);
            }
            if (value instanceof Number) {
                return converted(((Number)value).longValue());
            }
            return unConvertible();
        }
        if (type == float.class || type == Float.class) {
            if (value instanceof Float) {
                return converted(value);
            }
            if (value instanceof Number) {
                return converted(((Number)value).floatValue());
            }
            return unConvertible();
        }
        if (type == double.class || type == Double.class) {
            if (value instanceof Double) {
                return converted(value);
            }
            if (value instanceof Number) {
                return converted(((Number)value).doubleValue());
            }
            return unConvertible();
        }
        if (type == BigInteger.class) {
            if (value instanceof Number) {
                return converted(NumberMath.toBigInteger((Number)value));
            }
            return unConvertible();
        }
        if (type == BigDecimal.class) {
            if (value instanceof Number) {
                return converted(NumberMath.toBigDecimal((Number)value));
            }
            return unConvertible();
        }
        return unConvertible();
    }
    
    private static QConverted castFunctionInter(Object value, Class<?> functionInter) {
        if (value instanceof QLambda) {
            return converted(Proxy.newProxyInstance(functionInter.getClassLoader(),
                new Class<?>[] {functionInter},
                new QLambdaInvocationHandler((QLambda)value)));
        }
        return unConvertible();
    }
    
    private static QConverted castChar(Object value) {
        if (value instanceof Character) {
            return converted(value);
        }
        if (value instanceof Number) {
            return converted((char)((Number)value).intValue());
        }
        if (value instanceof String) {
            String strValue = (String)value;
            if (strValue.length() == 1) {
                return converted(strValue.charAt(0));
            }
            return unConvertible();
        }
        return unConvertible();
    }
    
    private static boolean noNeedConvert(Object value, Class<?> type) {
        return type == null || value == null || type.isInstance(value);
    }
    
    private static QConverted converted(Object converted) {
        return new QConverted(true, converted);
    }
    
    private static QConverted unConvertible() {
        return new QConverted(false, null);
    }
    
    public static class QConverted {
        
        private final boolean convertible;
        
        private final Object converted;
        
        public QConverted(boolean convertible, Object converted) {
            this.convertible = convertible;
            this.converted = converted;
        }
        
        public boolean isConvertible() {
            return convertible;
        }
        
        public Object getConverted() {
            return converted;
        }
    }
    
}
