/**
 * 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.metamodel.jdbc.dialects;

import java.util.List;
import org.apache.metamodel.MetaModelException;
import org.apache.metamodel.jdbc.JdbcDataContext;
import org.apache.metamodel.query.FromItem;
import org.apache.metamodel.query.Query;
import org.apache.metamodel.query.SelectItem;
import org.apache.metamodel.schema.ColumnType;

/**
 * Query rewriter for Apache Hive
 */
public class HiveQueryRewriter extends DefaultQueryRewriter {

    private int majorVersion;

    public HiveQueryRewriter(JdbcDataContext dataContext) {
        super(dataContext);
        String version = dataContext.getDatabaseVersion();
        String[] parts = version.split("\\.");
        if(parts.length < 2) {
            throw new RuntimeException("Illegal Hive Version: " + version + " (expected A.B.* format)");
        } else {
            majorVersion = Integer.valueOf(parts[0]);
        }
    }

    @Override
    public final boolean isFirstRowSupported(final Query query) {
        switch (majorVersion){
            case 2:
            case 3:
                return true;
            default:
                return super.isFirstRowSupported(query);
        }
    }

    @Override
    public final boolean isMaxRowsSupported() {
        switch (majorVersion){
            case 2:
            case 3:
                return true;
            default:
                return super.isMaxRowsSupported();
        }
    }

    /**
     * {@inheritDoc}
     *
     * If the Max rows and/or First row property of the query is set, then we
     * will use the database's LIMIT and OFFSET functions.
     */
    @Override
    public String rewriteQuery(Query query) {
        switch (majorVersion){
            case 2:
            case 3:
                return rewriteQueryForHive2(query);
            default:
                return rewriteQueryForHive1(query);
        }

    }

    private String rewriteQueryForHive1(Query query){

        Integer maxRows = query.getMaxRows();
        Integer firstRow = query.getFirstRow();

        if(firstRow != null && firstRow > 1){
            if(query.getOrderByClause().getItemCount() == 0){
                throw new MetaModelException("OFFSET requires an ORDER BY clause");
            }
        }


        if (maxRows == null && (firstRow == null || firstRow.intValue() == 1)) {
            return super.rewriteQuery(query);
        }

        if ((firstRow == null || firstRow.intValue() == 1) && maxRows != null && maxRows > 0) {
            // We prefer to use the "LIMIT n" approach, if
            // firstRow is not specified.
            return super.rewriteQuery(query) + " LIMIT " + maxRows;
        }

        final Query innerQuery = query.clone();
        innerQuery.setFirstRow(null);
        innerQuery.setMaxRows(null);

        final Query outerQuery = new Query();
        final FromItem subQuerySelectItem = new FromItem(innerQuery).setAlias("metamodel_subquery");
        outerQuery.from(subQuerySelectItem);

        final List<SelectItem> innerSelectItems = innerQuery.getSelectClause().getItems();
        for (SelectItem selectItem : innerSelectItems) {
            outerQuery.select(new SelectItem(selectItem, subQuerySelectItem));
        }


        final String rewrittenOrderByClause = rewriteOrderByClause(innerQuery, innerQuery.getOrderByClause());
        final String rowOver = "ROW_NUMBER() " + "OVER(" + rewrittenOrderByClause + ")";
        innerQuery.select(new SelectItem(rowOver, "metamodel_row_number"));
        innerQuery.getOrderByClause().removeItems();

        final String baseQueryString = rewriteQuery(outerQuery);

        if (maxRows == null) {
            return baseQueryString + " WHERE metamodel_row_number > " + (firstRow - 1);
        }

        return baseQueryString + " WHERE metamodel_row_number BETWEEN " + firstRow + " AND "
                + (firstRow - 1 + maxRows);

    }

    private String rewriteQueryForHive2(Query query){
        Integer maxRows = query.getMaxRows();
        Integer firstRow = query.getFirstRow();

        if(firstRow != null && firstRow > 1){
            if(query.getOrderByClause().getItemCount() == 0){
                throw new MetaModelException("OFFSET requires an ORDER BY clause");
            }
        }

        String queryString = super.rewriteQuery(query);

        if (maxRows != null || firstRow != null) {

            if (maxRows == null) {
                maxRows = Integer.MAX_VALUE;
            }
            queryString = queryString + " LIMIT " + maxRows;

            if (firstRow != null && firstRow > 1) {
                // offset is 0-based
                int offset = firstRow - 1;
                queryString = queryString + " OFFSET " + offset;
            }
        }

        return queryString;

    }

    @Override
    public String rewriteColumnType(ColumnType columnType, Integer columnSize) {
        if (columnType == ColumnType.INTEGER) {
            return "INT";
        }

        if(columnType == ColumnType.STRING) {
            return "STRING";
        }

        // Hive does not support VARCHAR without a width, nor VARCHAR(MAX).
        // Returning max allowable column size instead.
        if (columnType == ColumnType.VARCHAR && columnSize == null) {
            return super.rewriteColumnType(columnType, 65535);
        }
        return super.rewriteColumnType(columnType, columnSize);
    }
    
    @Override
    public boolean isTransactional() {
        return false;
    }
    
	@Override
	public boolean isPrimaryKeySupported() {
		return false;
	}
}

@@ -1802,34 +1802,35 @@
 h (majorVersion)
+ 
 %7B%0A
-
             case
@@ -2060,32 +2060,33 @@
 h (majorVersion)
+ 
 %7B%0A            ca
@@ -2499,16 +2499,17 @@
 Version)
+ 
 %7B%0A      
@@ -2737,16 +2737,17 @@
 y query)
+ 
 %7B%0A%0A     
@@ -2872,32 +2872,33 @@
 && firstRow %3E 1)
+ 
 %7B%0A            if
@@ -2935,32 +2935,33 @@
 temCount() == 0)
+ 
 %7B%0A              
@@ -4749,16 +4749,17 @@
 y query)
+ 
 %7B%0A      
@@ -4891,16 +4891,17 @@
 Row %3E 1)
+ 
 %7B%0A      

fix code style