package org.h2.util;

import java.util.regex.Pattern;
import org.h2.engine.Mode.ModeEnum;
import static org.h2.engine.Mode.ModeEnum.*;
import org.h2.message.DbException;

public class ColumnNamerConfiguration {
    private static final String DEFAULT_COMMAND = "DEFAULT";
    private static final String REGULAR_EXPRESSION_MATCH_DISALLOWED = "REGULAR_EXPRESSION_MATCH_DISALLOWED = ";
    private static final String REGULAR_EXPRESSION_MATCH_ALLOWED = "REGULAR_EXPRESSION_MATCH_ALLOWED = ";
    private static final String DEFAULT_COLUMN_NAME_PATTERN = "DEFAULT_COLUMN_NAME_PATTERN = ";
    private static final String MAX_IDENTIFIER_LENGTH = "MAX_IDENTIFIER_LENGTH = ";
    private static final String EMULATE_COMMAND = "EMULATE = ";
    private static final String GENERATE_UNIQUE_COLUMN_NAMES = "GENERATE_UNIQUE_COLUMN_NAMES = ";
    
    private int maxIdentiferLength;
    private String regularExpressionMatchAllowed;
    private String regularExpressionMatchDisallowed;
    private String defaultColumnNamePattern;
    private boolean generateUniqueColumnNames;
    private Pattern compiledRegularExpressionMatchAllowed;
    private Pattern compiledRegularExpressionMatchDisallowed;

    public ColumnNamerConfiguration(int maxIdentiferLength, String regularExpressionMatchAllowed,
            String regularExpressionMatchDisallowed, String defaultColumnNamePattern, boolean generateUniqueColumnNames) {
        
        this.maxIdentiferLength = maxIdentiferLength;
        this.regularExpressionMatchAllowed = regularExpressionMatchAllowed;
        this.regularExpressionMatchDisallowed = regularExpressionMatchDisallowed;
        this.defaultColumnNamePattern = defaultColumnNamePattern;
        this.generateUniqueColumnNames = generateUniqueColumnNames;
        
        compiledRegularExpressionMatchAllowed = Pattern.compile(regularExpressionMatchAllowed);
        compiledRegularExpressionMatchDisallowed = Pattern.compile(regularExpressionMatchDisallowed);
    }

    public int getMaxIdentiferLength() {
        return maxIdentiferLength;
    }

    public void setMaxIdentiferLength(int maxIdentiferLength) {
        this.maxIdentiferLength = Math.max(30,maxIdentiferLength);
        if(maxIdentiferLength!=getMaxIdentiferLength()){
            throw DbException.getInvalidValueException("Illegal value (<30) in SET COLUMN_NAME_RULES","MAX_IDENTIFIER_LENGTH="+maxIdentiferLength);
        }
    }

    public String getRegularExpressionMatchAllowed() {
        return regularExpressionMatchAllowed;
    }

    public void setRegularExpressionMatchAllowed(String regularExpressionMatchAllowed) {
        this.regularExpressionMatchAllowed = regularExpressionMatchAllowed;
    }

    public String getRegularExpressionMatchDisallowed() {
        return regularExpressionMatchDisallowed;
    }

    public void setRegularExpressionMatchDisallowed(String regularExpressionMatchDisallowed) {
        this.regularExpressionMatchDisallowed = regularExpressionMatchDisallowed;
    }

    public String getDefaultColumnNamePattern() {
        return defaultColumnNamePattern;
    }

    public void setDefaultColumnNamePattern(String defaultColumnNamePattern) {
        this.defaultColumnNamePattern = defaultColumnNamePattern;
    }

    public Pattern getCompiledRegularExpressionMatchAllowed() {
        return compiledRegularExpressionMatchAllowed;
    }

    public void setCompiledRegularExpressionMatchAllowed(Pattern compiledRegularExpressionMatchAllowed) {
        this.compiledRegularExpressionMatchAllowed = compiledRegularExpressionMatchAllowed;
    }

    public Pattern getCompiledRegularExpressionMatchDisallowed() {
        return compiledRegularExpressionMatchDisallowed;
    }

    public void setCompiledRegularExpressionMatchDisallowed(Pattern compiledRegularExpressionMatchDisallowed) {
        this.compiledRegularExpressionMatchDisallowed = compiledRegularExpressionMatchDisallowed;
    }
    
    public void configure(String stringValue) {
        try{
            if(stringValue.equalsIgnoreCase(DEFAULT_COMMAND)){
                configure(REGULAR);
            } else if(stringValue.startsWith(EMULATE_COMMAND)){
                 configure(ModeEnum.valueOf(unquoteString(stringValue.substring(EMULATE_COMMAND.length()))));
            } else if(stringValue.startsWith(MAX_IDENTIFIER_LENGTH)){
                int maxLength = Integer.parseInt(stringValue.substring(MAX_IDENTIFIER_LENGTH.length()));
                setMaxIdentiferLength(maxLength);
            } else if(stringValue.startsWith(GENERATE_UNIQUE_COLUMN_NAMES)){
                setGenerateUniqueColumnNames(Integer.parseInt(stringValue.substring(GENERATE_UNIQUE_COLUMN_NAMES.length()))==1);            
            } else if(stringValue.startsWith(DEFAULT_COLUMN_NAME_PATTERN)){
                setDefaultColumnNamePattern(unquoteString(stringValue.substring(DEFAULT_COLUMN_NAME_PATTERN.length())));            
            } else if(stringValue.startsWith(REGULAR_EXPRESSION_MATCH_ALLOWED)){
                setRegularExpressionMatchAllowed(unquoteString(stringValue.substring(REGULAR_EXPRESSION_MATCH_ALLOWED.length())));            
            } else if(stringValue.startsWith(REGULAR_EXPRESSION_MATCH_DISALLOWED)){
                setRegularExpressionMatchDisallowed(unquoteString(stringValue.substring(REGULAR_EXPRESSION_MATCH_DISALLOWED.length())));            
            }
            else
            {
                throw DbException.getInvalidValueException("SET COLUMN_NAME_RULES: unknown id:"+stringValue,
                        stringValue);
    
            }
           
            recompilePatterns();
        }
        //Including NumberFormatException|PatternSyntaxException
        catch(RuntimeException e){
            throw DbException.getInvalidValueException("SET COLUMN_NAME_RULES:"+e.getMessage(),
                    stringValue);

        }        
    }

    private void recompilePatterns() {
        try{
            // recompile RE patterns
            setCompiledRegularExpressionMatchAllowed(Pattern.compile(getRegularExpressionMatchAllowed()));
            setCompiledRegularExpressionMatchDisallowed(Pattern.compile(getRegularExpressionMatchDisallowed()));
        }
        catch(Exception e){
            configure(REGULAR);
            throw e;
        }
    }    
    
    public static ColumnNamerConfiguration getDefault(){
        return new ColumnNamerConfiguration(Integer.MAX_VALUE, "(?m)(?s).+", "(?m)(?s)[\\x00]", "_UNNAMED_$$",false);
    }
    
    private static String unquoteString(String s){
        if(s.startsWith("'") && s.endsWith("'")){
            s = s.substring(1, s.length()-1);
            return s;
        }
        return s;
    }

    public boolean isGenerateUniqueColumnNames() {
        return generateUniqueColumnNames;
    }

    public void setGenerateUniqueColumnNames(boolean generateUniqueColumnNames) {
        this.generateUniqueColumnNames = generateUniqueColumnNames;
    }

    public void configure(ModeEnum modeEnum) {
        switch(modeEnum){
        case Oracle:
            // Nonquoted identifiers can contain only alphanumeric characters 
            // from your database character set and the underscore (_), dollar sign ($), and pound sign (#). 
            setMaxIdentiferLength(128);
            setRegularExpressionMatchAllowed("(?m)(?s)\"?[A-Za-z0-9_\\$#]+\"?");
            setRegularExpressionMatchDisallowed("(?m)(?s)[^A-Za-z0-9_\"\\$#]");
            setDefaultColumnNamePattern("_UNNAMED_$$");  
            setGenerateUniqueColumnNames(false);            
            break;
            
        case MSSQLServer:
            // https://docs.microsoft.com/en-us/sql/sql-server/maximum-capacity-specifications-for-sql-server
            setMaxIdentiferLength(128);
            setRegularExpressionMatchAllowed("(?m)(?s)[A-Za-z0-9_\\[\\]]+");// allows [] around names
            setRegularExpressionMatchDisallowed("(?m)(?s)[^A-Za-z0-9_\\[\\]]");
            setDefaultColumnNamePattern("_UNNAMED_$$");
            setGenerateUniqueColumnNames(false);            
            break;
            
        case PostgreSQL:
            setMaxIdentiferLength(63);// this default can be changed to 128 by postgres config
            setRegularExpressionMatchAllowed("(?m)(?s)[A-Za-z0-9_\\$]+");
            setRegularExpressionMatchDisallowed("(?m)(?s)[^A-Za-z0-9_\\$]");
            setDefaultColumnNamePattern("_UNNAMED_$$");
            setGenerateUniqueColumnNames(false);            
            break;

        case MySQL:
            //https://dev.mysql.com/doc/refman/5.7/en/identifiers.html
            setMaxIdentiferLength(64);
            setRegularExpressionMatchAllowed("(?m)(?s)`?[A-Za-z0-9_`\\$]+`?");
            setRegularExpressionMatchDisallowed("(?m)(?s)[^A-Za-z0-9_`\\$]");
            setDefaultColumnNamePattern("_UNNAMED_$$");
            setGenerateUniqueColumnNames(false);
            break;
            
        default:
        case REGULAR:
        case DB2:
        case Derby:
        case HSQLDB:
        case Ignite:
            setMaxIdentiferLength(Integer.MAX_VALUE);
            setRegularExpressionMatchAllowed("(?m)(?s).+");
            setRegularExpressionMatchDisallowed("(?m)(?s)[\\x00]");
            setDefaultColumnNamePattern("_UNNAMED_$$");
            setGenerateUniqueColumnNames(false);
            break;
        }
        recompilePatterns();                
    }
        
}