package it.unibz.inf.ontop.model.type.impl;

import com.google.common.collect.ImmutableMap;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import it.unibz.inf.ontop.model.type.DBTermType;
import it.unibz.inf.ontop.model.type.TermType;
import it.unibz.inf.ontop.model.type.TermTypeAncestry;
import it.unibz.inf.ontop.model.type.TypeFactory;

import java.util.Map;

import static it.unibz.inf.ontop.model.type.DBTermType.Category.DECIMAL;
import static it.unibz.inf.ontop.model.type.impl.NonStringNonNumberNonBooleanNonDatetimeDBTermType.StrictEqSupport.WITH_ALL;

public class SQLServerDBTypeFactory extends DefaultSQLDBTypeFactory {

    protected static final String BIT_STR = "BIT";
    public static final String DATETIME_STR = "DATETIME";
    public static final String DATETIME2_STR = "DATETIME2";
    public static final String DATETIMEOFFSET_STR = "DATETIMEOFFSET";
    public static final String UNIQUEIDENTIFIER_STR = "UNIQUEIDENTIFIER";
    private static final String DEFAULT_DECIMAL_STR = "DECIMAL(38, 19)";


    @AssistedInject
    private SQLServerDBTypeFactory(@Assisted TermType rootTermType, @Assisted TypeFactory typeFactory) {
        super(createSQLServerTypeMap(rootTermType, typeFactory), createSQLServerCodeMap());
    }

    private static Map<String, DBTermType> createSQLServerTypeMap(TermType rootTermType, TypeFactory typeFactory) {
        TermTypeAncestry rootAncestry = rootTermType.getAncestry();

        // Overloads NVARCHAR to insert the precision
        StringDBTermType nvarcharType = new StringDBTermType(NVARCHAR_STR, "NVARCHAR(max)", rootAncestry,
                typeFactory.getXsdStringDatatype());

        // Non-standard (not part of the R2RML standard).
        BooleanDBTermType bitType = new BooleanDBTermType(BIT_STR, rootAncestry,
                typeFactory.getXsdBooleanDatatype());
        // Name for TIMESTAMP
        DatetimeDBTermType datetimeType = new DatetimeDBTermType(DATETIME_STR, rootTermType.getAncestry(),
                typeFactory.getXsdDatetimeDatatype());
        DatetimeDBTermType datetime2Type = new DatetimeDBTermType(DATETIME2_STR, rootTermType.getAncestry(),
                typeFactory.getXsdDatetimeDatatype());
        DatetimeDBTermType dateTimeOffset = new DatetimeDBTermType(DATETIMEOFFSET_STR, rootTermType.getAncestry(),
                typeFactory.getXsdDatetimeDatatype());

        DBTermType uniqueIdType = new NonStringNonNumberNonBooleanNonDatetimeDBTermType(UNIQUEIDENTIFIER_STR,
                rootTermType.getAncestry(), WITH_ALL);

        // Default decimal (otherwise, the default value of DECIMAL would be DECIMAL(19,0)
        // with 0 digits after the point). Still arbitrary.
        NumberDBTermType defaultDecimalType = new NumberDBTermType(DEFAULT_DECIMAL_STR, rootAncestry,
                typeFactory.getXsdDecimalDatatype(), DECIMAL);

        Map<String, DBTermType> map = createDefaultSQLTypeMap(rootTermType, typeFactory);
        map.put(NVARCHAR_STR, nvarcharType);
        map.put(BIT_STR, bitType);
        map.put(DATETIME_STR, datetimeType);
        map.put(DATETIME2_STR, datetime2Type);
        map.put(DATETIMEOFFSET_STR, dateTimeOffset);
        map.put(UNIQUEIDENTIFIER_STR, uniqueIdType);
        map.put(DEFAULT_DECIMAL_STR, defaultDecimalType);
        return map;
    }

    private static ImmutableMap<DefaultTypeCode, String> createSQLServerCodeMap() {
        Map<DefaultTypeCode, String> map = createDefaultSQLCodeMap();
        map.put(DefaultTypeCode.BOOLEAN, BIT_STR);
        map.put(DefaultTypeCode.STRING, NVARCHAR_STR);
        map.put(DefaultTypeCode.DATETIMESTAMP, DATETIME2_STR);
        // By default float is float(53) which is a float with double precision
        map.put(DefaultTypeCode.DOUBLE, FLOAT_STR);
        map.put(DefaultTypeCode.DECIMAL, DEFAULT_DECIMAL_STR);
        return ImmutableMap.copyOf(map);
    }

    @Override
    public String getDBTrueLexicalValue() {
        return "1";
    }

    @Override
    public String getDBFalseLexicalValue() {
        return "0";
    }
}
