/*
 * 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.bc.batch.dataaccess.impl;

import java.util.ArrayList;

/**
 * implements a type to hold the raw SQL for each step in a process.  allows us to have an insertion point for system parameters which can only be
 * incorporated into the SQL at run-time.  provides a method to insert the parameter string and return the finished SQL as a string,
 * and another to just return SQL as a string without inserting anything, therefore permitting most of the SQL to be built in a constructor and finished at run time.
 */

public class SQLForStep {

    // locations in the StringBuilder in which a run-time parameter string is to be incorporated
    // (maybe an int[] will be faster when a user is waiting for the SQL to be built
    private int[] insertionPoints = {};
    // string buffers are thread-safe, while string builders are not
    // string builders are more efficient, so use one here and never modify it in our public methods
    private StringBuilder sqlBuilder;

    public SQLForStep(StringBuilder sqlBuilder) {
        // use this constructor when the SQL needs no insertions
        // make a copy of the static SQL, so this type acts as a container for SQL to be passed ot other methods and used repeatedly.
        this.sqlBuilder = new StringBuilder(sqlBuilder);
    }

    public SQLForStep(StringBuilder sqlBuilder, ArrayList<Integer> insertionPoints) {
        // use this constructor when there are run-time strings to be inserted into the SQL
        this.sqlBuilder = new StringBuilder(sqlBuilder);
        int pointCount = insertionPoints.size();
        this.insertionPoints = new int[pointCount];
        for (int idx = 0; idx < pointCount; idx++) {
            this.insertionPoints[idx] = (int) insertionPoints.get(idx);
        }
    }

    public String getSQL(ArrayList<String> parameterToInsert) {
        // make a copy of the SQL, so this routine is thread-safe, and evey caller gets her own copy of the static SQL to change.
        // string buffers are thread-safe, while string builders are not, but making a copy puts everything on the stack of the local thread anyway,
        // so we don't need the string buffer synchronization and can use the more efficient StringBuilder.
        if (parameterToInsert.size() != insertionPoints.length) {
            // throw an out of bounds exception if there is a mismatch
            // we can't build the SQL
            throw (new IndexOutOfBoundsException("the number of strings to be inserted into SQL does not match the number of insertion points"));
        }
        StringBuilder unfinishedSQL = new StringBuilder(sqlBuilder);
        // insertions will add to the length of the String
        // we need to increase the value of the insertion point based on the leghtns
        int lengthSoFar = 0;
        for (int idx = 0; idx < insertionPoints.length; idx++) {
            String parameterString = parameterToInsert.get(idx);
            unfinishedSQL.insert(insertionPoints[idx] + lengthSoFar, parameterString);
            lengthSoFar = lengthSoFar + parameterString.length();
        }
        return (unfinishedSQL.toString());
    }

    public String getSQL() {
        // no changes needed to the StringBuilder.  just return a string built from the SQL StringBuilder.
        return (sqlBuilder.toString());
    }
}
