/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * The OpenSearch Contributors require contributions made to
 * this file be licensed under the Apache-2.0 license or a
 * compatible open source license.
 */

/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch 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.
 */

/*
 * Modifications Copyright OpenSearch Contributors. See
 * GitHub history for details.
 */

package org.opensearch.painless;

import java.util.Objects;

/**
 * Represents a location in script code (name of script + character offset)
 */
public final class Location {
    private final String sourceName;
    private final int offset;

    /**
     * Create a new Location
     * @param sourceName script's name
     * @param offset character offset of script element
     */
    public Location(String sourceName, int offset) {
        this.sourceName = Objects.requireNonNull(sourceName);
        this.offset = offset;
    }

    /**
     * Return the script's name
     */
    public String getSourceName() {
        return sourceName;
    }

    /**
     * Return the character offset
     */
    public int getOffset() {
        return offset;
    }

    /**
     * Augments an exception with this location's information.
     */
    public RuntimeException createError(RuntimeException exception) {
        StackTraceElement element = new StackTraceElement(WriterConstants.CLASS_NAME, "compile", sourceName, offset + 1);
        StackTraceElement[] oldStack = exception.getStackTrace();
        StackTraceElement[] newStack = new StackTraceElement[oldStack.length + 1];
        System.arraycopy(oldStack, 0, newStack, 1, oldStack.length);
        newStack[0] = element;
        exception.setStackTrace(newStack);
        assert exception.getStackTrace().length == newStack.length : "non-writeable stacktrace for exception: " + exception.getClass();
        return exception;
    }

    // This maximum length is theoretically 65535 bytes, but as it's CESU-8 encoded we don't know how large it is in bytes, so be safe
    private static final int MAX_NAME_LENGTH = 256;

    /** Computes the file name (mostly important for stacktraces) */
    public static String computeSourceName(String scriptName) {
        StringBuilder fileName = new StringBuilder();
        // its an anonymous script, include at least a portion of the source to help identify which one it is
        // but don't create stacktraces with filenames that contain newlines or huge names.

        // truncate to the first newline
        int limit = scriptName.indexOf('\n');
        if (limit >= 0) {
            int limit2 = scriptName.indexOf('\r');
            if (limit2 >= 0) {
                limit = Math.min(limit, limit2);
            }
        } else {
            limit = scriptName.length();
        }

        // truncate to our limit
        limit = Math.min(limit, MAX_NAME_LENGTH);
        fileName.append(scriptName, 0, limit);

        // if we truncated, make it obvious
        if (limit != scriptName.length()) {
            fileName.append(" ...");
        }
        return fileName.toString();
    }
}
