/*
 * The Kuali Financial System, a comprehensive financial management system for higher education.
 *
 * Copyright 2005-2016 The Kuali Foundation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kuali.kfs.module.purap.dataaccess.impl;

import org.apache.commons.lang.StringUtils;
import org.apache.ojb.broker.PersistenceBroker;
import org.apache.ojb.broker.metadata.ClassDescriptor;
import org.apache.ojb.broker.metadata.CollectionDescriptor;
import org.apache.ojb.broker.metadata.FieldDescriptor;
import org.apache.ojb.broker.platforms.PlatformMySQLImpl;
import org.apache.ojb.broker.query.Query;
import org.apache.ojb.broker.query.QueryByCriteria;

import java.util.Map;

/**
 * This class improves the default order by in OJB by enforcing consistency between Oracle and MySQLs handling of Null values in a
 * column. Oracle by default sorts nulls last while MySQL does nulls first (i.e. 1,2,3,null MySQL:null,1,2,3; Oracle:1,2,3,null To
 * get Mysql to sort correctly we need to negate the field that is being Sorted on (i.e. ORDER BY -column DESC = 1,2,3,null while
 * ORDER BY column DESC = 3,2,1,null) the oracle default for ORDER BY is "NULLS LAST" which the above MySQL tweak should make it
 * like. This could be improved to pass in nullsFirst to decide which way to display but that would be beyond what ojb currently
 * does
 */
public class PurapItemQueryCustomizer extends KualiQueryCustomizerDefaultImpl {
    protected static final String MYSQL_NEGATION = "-";
    public final static String ORDER_BY_FIELD = "orderByField.";
    public final static String ASCENDING = "ASC";
    public final static String DESCENDING = "DESC";

    /**
     * In addition to what the referenced method does, this also fixes a mysql order by issue (see class comments)
     *
     * @see org.apache.ojb.broker.accesslayer.QueryCustomizerDefaultImpl#customizeQuery(java.lang.Object,
     * org.apache.ojb.broker.PersistenceBroker, org.apache.ojb.broker.metadata.CollectionDescriptor,
     * org.apache.ojb.broker.query.QueryByCriteria)
     */
    @Override
    public Query customizeQuery(Object anObject, PersistenceBroker broker, CollectionDescriptor cod, QueryByCriteria query) {
        boolean platformMySQL = broker.serviceSqlGenerator().getPlatform() instanceof PlatformMySQLImpl;

        Map<String, String> attributes = getAttributes();
        for (String attributeName : attributes.keySet()) {
            if (!attributeName.startsWith(ORDER_BY_FIELD)) {
                continue;
            }

            String fieldName = attributeName.substring(ORDER_BY_FIELD.length());
            ClassDescriptor itemClassDescriptor = broker.getClassDescriptor(cod.getItemClass());
            FieldDescriptor orderByFieldDescriptior = itemClassDescriptor.getFieldDescriptorByName(fieldName);

            // the column to sort on derived from the property name
            String orderByColumnName = orderByFieldDescriptior.getColumnName();

            // ascending or descending
            String fieldValue = attributes.get(attributeName);
            boolean ascending = (StringUtils.equals(fieldValue, ASCENDING));
            // throw an error if not ascending or descending
            if (!ascending && StringUtils.equals(fieldValue, DESCENDING)) {
                throw new RuntimeException("neither ASC nor DESC was specified in ojb file for " + fieldName);
            }

            if (platformMySQL) {
                // by negating the column name in MySQL we can get nulls last (ascending or descending)
                String mysqlPrefix = (ascending) ? MYSQL_NEGATION : "";
                query.addOrderBy(mysqlPrefix + orderByColumnName, false);
            } else {
                query.addOrderBy(orderByColumnName, ascending);
            }
        }
        return query;
    }
}
