/*
 * Copyright 1999-2015 dangdang.com.
 * <p>
 * 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.
 * </p>
 */

package com.dangdang.ddframe.rdb.sharding.parsing.parser.statement.dql.select;

import com.dangdang.ddframe.rdb.sharding.parsing.parser.context.OrderItem;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.context.limit.Limit;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.context.selectitem.AggregationSelectItem;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.context.selectitem.SelectItem;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.statement.dql.DQLStatement;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.token.OffsetToken;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.token.RowCountToken;
import com.dangdang.ddframe.rdb.sharding.parsing.parser.token.SQLToken;
import com.google.common.base.Preconditions;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Select SQL语句对象.
 *
 * @author zhangliang
 */
@Getter
@Setter
@ToString(callSuper = true)
public final class SelectStatement extends DQLStatement {
    
    private boolean containStar;
    
    private int selectListLastPosition;
    
    private int groupByLastPosition;
    
    private final Set<SelectItem> items = new HashSet<>();
    
    private final List<OrderItem> groupByItems = new LinkedList<>();
    
    private final List<OrderItem> orderByItems = new LinkedList<>();
    
    private Limit limit;
    
    @Getter(AccessLevel.NONE)
    private SelectStatement subQueryStatement;
    
    /**
     * 获取聚合选择项集合.
     *
     * @return 聚合选择项
     */
    public List<AggregationSelectItem> getAggregationSelectItems() {
        List<AggregationSelectItem> result = new LinkedList<>();
        for (SelectItem each : items) {
            if (each instanceof AggregationSelectItem) {
                AggregationSelectItem aggregationSelectItem = (AggregationSelectItem) each;
                result.add(aggregationSelectItem);
                for (AggregationSelectItem derivedEach: aggregationSelectItem.getDerivedAggregationSelectItems()) {
                    result.add(derivedEach);
                }
            }
        }
        return result;
    }
    
    /**
     * 判断是否分组和排序项一致.
     *
     * @return 是否分组和排序项一致
     */
    public boolean isSameGroupByAndOrderByItems() {
        return !getGroupByItems().isEmpty() && getGroupByItems().equals(getOrderByItems());
    }
    
    /**
     * 为选择项设置索引.
     * 
     * @param columnLabelIndexMap 列标签索引字典
     */
    public void setIndexForItems(final Map<String, Integer> columnLabelIndexMap) {
        setIndexForAggregationItem(columnLabelIndexMap);
        setIndexForOrderItem(columnLabelIndexMap, orderByItems);
        setIndexForOrderItem(columnLabelIndexMap, groupByItems);
    }
    
    private void setIndexForAggregationItem(final Map<String, Integer> columnLabelIndexMap) {
        for (AggregationSelectItem each : getAggregationSelectItems()) {
            Preconditions.checkState(columnLabelIndexMap.containsKey(each.getColumnLabel()), String.format("Can't find index: %s, please add alias for aggregate selections", each));
            each.setIndex(columnLabelIndexMap.get(each.getColumnLabel()));
            for (AggregationSelectItem derived : each.getDerivedAggregationSelectItems()) {
                Preconditions.checkState(columnLabelIndexMap.containsKey(derived.getColumnLabel()), String.format("Can't find index: %s", derived));
                derived.setIndex(columnLabelIndexMap.get(derived.getColumnLabel()));
            }
        }
    }
    
    private void setIndexForOrderItem(final Map<String, Integer> columnLabelIndexMap, final List<OrderItem> orderItems) {
        for (OrderItem each : orderItems) {
            if (-1 != each.getIndex()) {
                continue;
            }
            Preconditions.checkState(columnLabelIndexMap.containsKey(each.getColumnLabel()), String.format("Can't find index: %s", each));
            if (columnLabelIndexMap.containsKey(each.getColumnLabel())) {
                each.setIndex(columnLabelIndexMap.get(each.getColumnLabel()));
            }
        }
    }
    
    /**
     * 判断是否包含子查询.
     * 
     * @return 是否包含子查询
     */
    public boolean containsSubQuery() {
        return null != subQueryStatement;
    }
    
    /**
     * 获取子查询的Select SQL语句对象.
     * 
     * @return 子查询的Select SQL语句对象
     */
    public SelectStatement getSubQueryStatement() {
        SelectStatement result = this;
        boolean isRootQueryContainsStar = result.isContainStar();
        Limit limit = result.getLimit();
        List<SQLToken> limitSQLTokens = new LinkedList<>();
        for (SQLToken each : result.getSqlTokens()) {
            if (each instanceof RowCountToken || each instanceof OffsetToken) {
                limitSQLTokens.add(each);
            }
        }
        while (result.containsSubQuery()) {
            result = result.subQueryStatement;
            if (null == limit) {
                limit = result.getLimit();
            }
            if (null != result.getLimit() && null != result.getLimit().getRowCount()) {
                limit.setRowCount(result.getLimit().getRowCount());
            }
            if (null != result.getLimit() && null != result.getLimit().getOffset()) {
                limit.setOffset(result.getLimit().getOffset());
            }
            for (SQLToken each : result.getSqlTokens()) {
                if (each instanceof RowCountToken || each instanceof OffsetToken) {
                    limitSQLTokens.add(each);
                }
            }
        }
        if (!isRootQueryContainsStar) {
            result.getOrderByItems().clear();
            result.getGroupByItems().clear();
        }
        result.setLimit(limit);
        int count = 0;
        List<Integer> toBeRemovedIndexes = new LinkedList<>();
        for (SQLToken each : result.getSqlTokens()) {
            if (each instanceof RowCountToken || each instanceof OffsetToken) {
                toBeRemovedIndexes.add(count);
            }
            count++;
        }
        for (int each : toBeRemovedIndexes) {
            result.getSqlTokens().remove(each);
        }
        result.getSqlTokens().addAll(limitSQLTokens);
        return result;
    }
}

@@ -5161,78 +5161,199 @@
 t = 
-this;%0A        boolean isRootQueryContainsStar = result.isContainStar()
+processLimitForSubQuery();%0A        processOrderByItems(result);%0A        return result;%0A    %7D%0A    %0A    private SelectStatement processLimitForSubQuery() %7B%0A        SelectStatement result = this
 ;%0A  
@@ -6400,150 +6400,8 @@
   %7D%0A
-        if (!isRootQueryContainsStar) %7B%0A            result.getOrderByItems().clear();%0A            result.getGroupByItems().clear();%0A        %7D%0A
     
@@ -6927,10 +6927,220 @@
 ;%0A    %7D%0A
+    %0A    private void processOrderByItems(final SelectStatement result) %7B%0A        if (!containStar) %7B%0A            result.getOrderByItems().clear();%0A            result.getGroupByItems().clear();%0A        %7D%0A    %7D%0A
 %7D%0A

refactor select sub query 3rd version