/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.shardingsphere.core.rewrite.token.pojo;

import lombok.Getter;
import org.apache.shardingsphere.core.parse.sql.segment.dml.expr.ExpressionSegment;
import org.apache.shardingsphere.core.parse.sql.segment.dml.expr.complex.ComplexExpressionSegment;
import org.apache.shardingsphere.core.parse.sql.segment.dml.expr.simple.LiteralExpressionSegment;
import org.apache.shardingsphere.core.parse.sql.segment.dml.expr.simple.ParameterMarkerExpressionSegment;
import org.apache.shardingsphere.core.route.type.RoutingUnit;
import org.apache.shardingsphere.core.rule.DataNode;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Insert values token.
 *
 * @author maxiaoguang
 * @author panjuan
 */
@Getter
public final class InsertValuesToken extends SQLToken implements Substitutable, Alterable {
    
    private final int stopIndex;
    
    private final List<InsertValueToken> insertValueTokens;
    
    public InsertValuesToken(final int startIndex, final int stopIndex) {
        super(startIndex);
        this.stopIndex = stopIndex;
        this.insertValueTokens = new LinkedList<>();
    }
    
    /**
     * Add insert value token.
     * 
     * @param columnNames column names
     * @param columnValues column values
     * @param dataNodes data nodtes
     */
    public void addInsertValueToken(final Collection<String> columnNames, final List<ExpressionSegment> columnValues, final List<DataNode> dataNodes) {
        insertValueTokens.add(new InsertValueToken(columnNames, columnValues, dataNodes));
    }
    
    @Override
    public String toString(final RoutingUnit routingUnit, final Map<String, String> logicAndActualTables) {
        StringBuilder result = new StringBuilder();
        appendUnits(routingUnit, result);
        result.delete(result.length() - 2, result.length());
        return result.toString();
    }
    
    private void appendUnits(final RoutingUnit routingUnit, final StringBuilder result) {
        for (InsertValueToken each : insertValueTokens) {
            if (isToAppendInsertOptimizeResult(routingUnit, each)) {
                result.append(each).append(", ");
            }
        }
    }
    
    private boolean isToAppendInsertOptimizeResult(final RoutingUnit routingUnit, final InsertValueToken unit) {
        if (unit.getDataNodes().isEmpty() || null == routingUnit) {
            return true;
        }
        for (DataNode each : unit.getDataNodes()) {
            if (routingUnit.getTableUnit(each.getDataSourceName(), each.getTableName()).isPresent()) {
                return true;
            }
        }
        return false;
    }
    
    private final class InsertValueToken {
    
        private final Collection<String> columnNames;
    
        private final List<ExpressionSegment> columnValues;
    
        @Getter
        private final List<DataNode> dataNodes;
    
        InsertValueToken(final Collection<String> columnNames, final List<ExpressionSegment> columnValues, final List<DataNode> dataNodes) {
            this.columnNames = columnNames;
            this.columnValues = columnValues;
            this.dataNodes = dataNodes;
        }
    
        @Override
        public String toString() {
            StringBuilder result = new StringBuilder();
            result.append("(");
            for (int i = 0; i < columnNames.size(); i++) {
                result.append(getColumnValue(i)).append(", ");
            }
            result.delete(result.length() - 2, result.length()).append(")");
            return result.toString();
        }
    
        private String getColumnValue(final int index) {
            ExpressionSegment columnValue = columnValues.get(index);
            if (columnValue instanceof ParameterMarkerExpressionSegment) {
                return "?";
            } else if (columnValue instanceof LiteralExpressionSegment) {
                Object literals = ((LiteralExpressionSegment) columnValue).getLiterals();
                return literals instanceof String ? String.format("'%s'", ((LiteralExpressionSegment) columnValue).getLiterals()) : literals.toString();
            }
            return ((ComplexExpressionSegment) columnValue).getText();
        }
    }
}

@@ -1385,37 +1385,8 @@
 e;%0A%0A
-import java.util.Collection;%0A
 impo
@@ -1406,24 +1406,24 @@
 LinkedList;%0A
+
 import java.
@@ -1993,47 +1993,8 @@
  * %0A
-     * @param columnNames column names%0A
     
@@ -2120,46 +2120,8 @@
 nal 
-Collection%3CString%3E columnNames, final 
 List
@@ -2239,29 +2239,16 @@
 ueToken(
-columnNames, 
 columnVa
@@ -3410,67 +3410,8 @@
    %0A
-        private final Collection%3CString%3E columnNames;%0A    %0A
     
@@ -3575,46 +3575,8 @@
 nal 
-Collection%3CString%3E columnNames, final 
 List
@@ -3647,52 +3647,8 @@
 ) %7B%0A
-            this.columnNames = columnNames;%0A
     
@@ -3923,19 +3923,20 @@
 %3C column
-Nam
+Valu
 es.size(

delete columnNames