/*
 * 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.merge.dal.desc;

import org.apache.shardingsphere.core.execute.sql.execute.result.QueryResult;
import org.apache.shardingsphere.core.merge.dql.common.MemoryMergedResult;
import org.apache.shardingsphere.core.merge.dql.common.MemoryQueryResultRow;
import org.apache.shardingsphere.core.optimize.api.statement.OptimizedStatement;
import org.apache.shardingsphere.core.rule.ShardingRule;
import org.apache.shardingsphere.core.strategy.encrypt.EncryptTable;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Merged result for desc table.
 *
 * @author liya
 */
public class DescribeTableMergedResult extends MemoryMergedResult {

    private static final Map<String, Integer> LABEL_AND_INDEX_MAP = new HashMap<>(6, 1);

    static {
        LABEL_AND_INDEX_MAP.put("Field", 1);
        LABEL_AND_INDEX_MAP.put("Type", 2);
        LABEL_AND_INDEX_MAP.put("Null", 3);
        LABEL_AND_INDEX_MAP.put("Key", 4);
        LABEL_AND_INDEX_MAP.put("Default", 5);
        LABEL_AND_INDEX_MAP.put("Extra", 6);
    }

    private final ShardingRule shardingRule;

    private final OptimizedStatement optimizedStatement;

    private final Iterator<MemoryQueryResultRow> memoryResultSetRows;

    public DescribeTableMergedResult(final ShardingRule shardingRule, final List<QueryResult> queryResults, final OptimizedStatement optimizedStatement) throws SQLException {
        super(LABEL_AND_INDEX_MAP);
        this.shardingRule = shardingRule;
        this.optimizedStatement = optimizedStatement;
        this.memoryResultSetRows = init(queryResults);
    }

    private Iterator<MemoryQueryResultRow> init(final List<QueryResult> queryResults) throws SQLException {
        List<MemoryQueryResultRow> result = new LinkedList<>();
        for (QueryResult each : queryResults) {
            while (each.next()) {
                MemoryQueryResultRow memoryQueryResultRow = optimize(each);
                if (memoryQueryResultRow != null) {
                    result.add(memoryQueryResultRow);
                }
            }
        }
        if (!result.isEmpty()) {
            setCurrentResultSetRow(result.get(0));
        }
        return result.iterator();
    }

    private MemoryQueryResultRow optimize(final QueryResult queryResult) throws SQLException {
        MemoryQueryResultRow memoryQueryResultRow = new MemoryQueryResultRow(queryResult);
        String logicTableName = optimizedStatement.getTables().getSingleTableName();
        Map<String, EncryptTable> tables = shardingRule.getEncryptRule().getTables();
        boolean encrypted = tables.containsKey(logicTableName);
        if (encrypted) {
            EncryptTable encryptTable = tables.get(logicTableName);
            String columnName = memoryQueryResultRow.getCell(1).toString();
            if (encryptTable.getAssistedQueryColumns().contains(columnName)) {
                return null;
            }
            if (encryptTable.getCipherColumns().contains(columnName)) {
                memoryQueryResultRow.setCell(1, encryptTable.getLogicColumn(columnName));
            }
        }
        return memoryQueryResultRow;
    }

    @Override
    public boolean next() throws SQLException {
        if (memoryResultSetRows.hasNext()) {
            setCurrentResultSetRow(memoryResultSetRows.next());
            return true;
        }
        return false;
    }
}

@@ -852,16 +852,56 @@
 .desc;%0A%0A
+import com.google.common.base.Optional;%0A
 import o
@@ -2801,32 +2801,41 @@
                 
+Optional%3C
 MemoryQueryResul
@@ -2830,32 +2830,33 @@
 ryQueryResultRow
+%3E
  memoryQueryResu
@@ -2923,16 +2923,20 @@
 tRow
- != null
+.isPresent()
 ) %7B%0A
@@ -2986,16 +2986,22 @@
 esultRow
+.get()
 );%0A     
@@ -3184,16 +3184,25 @@
 private 
+Optional%3C
 MemoryQu
@@ -3213,16 +3213,17 @@
 esultRow
+%3E
  optimiz
@@ -3878,12 +3878,25 @@
 urn 
-null
+Optional.absent()
 ;%0A  
@@ -4108,16 +4108,28 @@
  return 
+Optional.of(
 memoryQu
@@ -4140,16 +4140,17 @@
 esultRow
+)
 ;%0A    %7D%0A

use Optional instead of null