/*
 * Copyright (C) 2014 Jörg Prante
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.xbib.elasticsearch.plugin.jdbc.util;

import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.xcontent.support.XContentMapValues;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import static org.elasticsearch.common.collect.Lists.newLinkedList;
import static org.elasticsearch.common.collect.Maps.newHashMap;

/**
 * The SQL command
 */
public class SQLCommand {

    private String sql;

    private static final Pattern STATEMENT_PATTERN = Pattern.compile("^\\s*(update|insert)", Pattern.CASE_INSENSITIVE);

    private List<Object> params = newLinkedList();

    private Map<String, Object> register = newHashMap();

    private boolean callable;

    public SQLCommand setSQL(String sql) throws IOException {
        if (sql.endsWith(".sql")) {
            Reader r = new InputStreamReader(new FileInputStream(sql), "UTF-8");
            sql = Streams.copyToString(r);
            r.close();
        }
        this.sql = sql;
        return this;
    }

    public String getSQL() {
        return sql;
    }

    public SQLCommand setParameters(List<Object> params) {
        this.params = params;
        return this;
    }

    public List<Object> getParameters() {
        return params;
    }

    public SQLCommand setCallable(boolean callable) {
        this.callable = callable;
        return this;
    }

    public boolean isCallable() {
        return callable;
    }

    public boolean isQuery() {
        if (sql == null) {
            throw new IllegalArgumentException("no SQL found");
        }
        if (STATEMENT_PATTERN.matcher(sql).find()) {
            return false;
        }
        int p1 = sql.toLowerCase().indexOf("select");
        if (p1 < 0) {
            return false;
        }
        int p2 = sql.toLowerCase().indexOf("update");
        if (p2 < 0) {
            return true;
        }
        int p3 = sql.toLowerCase().indexOf("insert");
        return p3 < 0 || p1 < p2 && p1 < p3;
    }

    /**
     * A register is for parameters of a callable statement.
     *
     * @param register a map for registering parameters
     */
    public void setRegister(Map<String, Object> register) {
        this.register = register;
    }

    /**
     * Get the parameters of a callable statement
     *
     * @return the register map
     */
    public Map<String, Object> getRegister() {
        return register;
    }

    @SuppressWarnings({"unchecked"})
    public static List<SQLCommand> parse(Map<String, Object> settings) {
        List<SQLCommand> sql = new LinkedList<SQLCommand>();
        if (!XContentMapValues.isArray(settings.get("sql"))) {
            settings.put("sql", Arrays.asList(settings.get("sql")));
        }
        List<Object> list = (List<Object>) settings.get("sql");
        for (Object entry : list) {
            SQLCommand command = new SQLCommand();
            try {
                if (entry instanceof Map) {
                    Map<String, Object> m = (Map<String, Object>) entry;
                    if (m.containsKey("statement")) {
                        command.setSQL((String) m.get("statement"));
                    }
                    if (m.containsKey("parameter")) {
                        command.setParameters(XContentMapValues.extractRawValues("parameter", m));
                    }
                    if (m.containsKey("callable")) {
                        command.setCallable(XContentMapValues.nodeBooleanValue(m.get("callable")));
                    }
                    if (m.containsKey("register")) {
                        command.setRegister(XContentMapValues.nodeMapValue(m.get("register"), null));
                    }
                } else if (entry instanceof String) {
                    command.setSQL((String) entry);
                }
                sql.add(command);
            } catch (IOException e) {
                throw new IllegalArgumentException("SQL command not found", e);
            }
        }
        return sql;
    }

    public String toString() {
        return "statement=" + sql + " parameter=" + params + " callable=" + callable;
    }

}

@@ -1386,24 +1386,52 @@
 kedList();%0A%0A
+    private boolean write;%0A%0A
     private 
@@ -1471,24 +1471,24 @@
 wHashMap();%0A
-
 %0A    private
@@ -2233,32 +2233,196 @@
 allable;%0A    %7D%0A%0A
+    public SQLCommand setWrite(boolean write) %7B%0A        this.write = write;%0A        return this;%0A    %7D%0A%0A    public boolean isWrite() %7B%0A        return write;%0A    %7D%0A%0A
     public boole
@@ -2529,32 +2529,89 @@
 nd%22);%0A        %7D%0A
+        if (write) %7B%0A            return false;%0A        %7D%0A
         if (STAT
@@ -4348,32 +4348,198 @@
               %7D%0A
+                    if (m.containsKey(%22write%22)) %7B%0A                        command.setWrite(XContentMapValues.nodeBooleanValue(m.get(%22write%22)));%0A                    %7D%0A
                 
@@ -5228,24 +5228,24 @@
 oString() %7B%0A
-
         retu
@@ -5294,16 +5294,36 @@
 rams + %22
+ write=%22 + write + %22
  callabl

add write flag to enforce write connection use of an SQL statement