/*
 * 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.shadow.rewrite.token.generator.impl;

import lombok.Setter;
import org.apache.shardingsphere.shadow.rewrite.token.generator.BaseShadowSQLTokenGenerator;
import org.apache.shardingsphere.shadow.rewrite.token.pojo.ShadowInsertValuesToken;
import org.apache.shardingsphere.sql.parser.binder.segment.insert.values.InsertValueContext;
import org.apache.shardingsphere.sql.parser.binder.statement.SQLStatementContext;
import org.apache.shardingsphere.sql.parser.binder.statement.dml.InsertStatementContext;
import org.apache.shardingsphere.sql.parser.sql.segment.dml.assignment.InsertValuesSegment;
import org.apache.shardingsphere.infra.rewrite.sql.token.generator.OptionalSQLTokenGenerator;
import org.apache.shardingsphere.infra.rewrite.sql.token.pojo.generic.InsertValue;
import org.apache.shardingsphere.infra.rewrite.sql.token.pojo.generic.InsertValuesToken;

import java.util.Collection;
import java.util.Iterator;

/**
 * Insert values token generator for shadow.
 */
@Setter
public final class ShadowInsertValuesTokenGenerator extends BaseShadowSQLTokenGenerator implements OptionalSQLTokenGenerator<InsertStatementContext> {
    
    @Override
    protected boolean isGenerateSQLTokenForShadow(final SQLStatementContext sqlStatementContext) {
        return sqlStatementContext instanceof InsertStatementContext && (((InsertStatementContext) sqlStatementContext).getSqlStatement()).getColumnNames().contains(getShadowRule().getColumn());
    }
    
    @Override
    public InsertValuesToken generateSQLToken(final InsertStatementContext insertStatementContext) {
        Collection<InsertValuesSegment> insertValuesSegments = insertStatementContext.getSqlStatement().getValues();
        InsertValuesToken result = new ShadowInsertValuesToken(getStartIndex(insertValuesSegments), getStopIndex(insertValuesSegments));
        for (InsertValueContext each : insertStatementContext.getInsertValueContexts()) {
            InsertValue insertValueToken = new InsertValue(each.getValueExpressions());
            Iterator<String> descendingColumnNames = insertStatementContext.getDescendingColumnNames();
            while (descendingColumnNames.hasNext()) {
                String columnName = descendingColumnNames.next();
                if (getShadowRule().getColumn().equals(columnName)) {
                    removeValueToken(insertValueToken, insertStatementContext, columnName);
                }
            }
            result.getInsertValues().add(insertValueToken);
        }
        return result;
    }
    
    private void removeValueToken(final InsertValue insertValueToken, final InsertStatementContext insertStatementContext, final String columnName) {
        int columnIndex = insertStatementContext.getColumnNames().indexOf(columnName);
        insertValueToken.getValues().remove(columnIndex);
    }
    
    private int getStartIndex(final Collection<InsertValuesSegment> segments) {
        int result = segments.iterator().next().getStartIndex();
        for (InsertValuesSegment each : segments) {
            result = Math.min(result, each.getStartIndex());
        }
        return result;
    }
    
    private int getStopIndex(final Collection<InsertValuesSegment> segments) {
        int result = segments.iterator().next().getStopIndex();
        for (InsertValuesSegment each : segments) {
            result = Math.max(result, each.getStopIndex());
        }
        return result;
    }
}

@@ -890,16 +890,185 @@
 Setter;%0A
+import org.apache.shardingsphere.infra.rewrite.sql.token.generator.aware.PreviousSQLTokensAware;%0Aimport org.apache.shardingsphere.infra.rewrite.sql.token.pojo.SQLToken;%0A
 import o
@@ -1144,32 +1144,32 @@
 TokenGenerator;%0A
-
 import org.apach
@@ -1914,16 +1914,66 @@
 terator;
+%0Aimport java.util.List;%0Aimport java.util.Optional;
 %0A%0A/**%0A *
@@ -2175,18 +2175,93 @@
 Context%3E
- %7B
+, PreviousSQLTokensAware %7B%0A    %0A    private List%3CSQLToken%3E previousSQLTokens;
 %0A    %0A  
@@ -2684,32 +2684,1188 @@
 ementContext) %7B%0A
+        Optional%3CSQLToken%3E insertValuesToken = findPreviousSQLToken(InsertValuesToken.class);%0A        if (insertValuesToken.isPresent()) %7B%0A            processPreviousSQLToken(insertStatementContext, (InsertValuesToken) insertValuesToken.get());%0A            return (InsertValuesToken) insertValuesToken.get();%0A        %7D%0A        return generateNewSQLToken(insertStatementContext);%0A    %7D%0A    %0A    private void processPreviousSQLToken(final InsertStatementContext insertStatementContext, final InsertValuesToken insertValuesToken) %7B%0A        for (InsertValue insertValueToken : insertValuesToken.getInsertValues()) %7B%0A            Iterator%3CString%3E descendingColumnNames = insertStatementContext.getDescendingColumnNames();%0A            while (descendingColumnNames.hasNext()) %7B%0A                String columnName = descendingColumnNames.next();%0A                if (getShadowRule().getColumn().equals(columnName)) %7B%0A                    removeValueToken(insertValueToken, insertStatementContext, columnName);%0A                %7D%0A            %7D%0A        %7D%0A    %7D%0A    %0A    private InsertValuesToken generateNewSQLToken(final InsertStatementContext insertStatementContext) %7B%0A
         Collecti
@@ -4760,32 +4760,32 @@
 ken);%0A        %7D%0A
-
         return r
@@ -4794,32 +4794,333 @@
 ult;%0A    %7D%0A    %0A
+    private Optional%3CSQLToken%3E findPreviousSQLToken(final Class%3C?%3E sqlToken) %7B%0A        for (SQLToken each : previousSQLTokens) %7B%0A            if (sqlToken.isAssignableFrom(each.getClass())) %7B%0A                return Optional.of(each);%0A            %7D%0A        %7D%0A        return Optional.empty();%0A    %7D%0A    %0A
     private void

fix ShadowInsertValuesTokenGenerator (#5677)