/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * Copyright 2006-2024 the original author or authors.
 *
 * 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
 *
 *     https://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.kordamp.json.util;

import org.apache.commons.lang3.StringUtils;
import org.kordamp.json.JSONException;

/**
 * Transforms a string into a valid Java identifier.<br>
 * There are five predefined strategies:
 * <ul>
 * <li>NOOP: does not perform transformation.</li>
 * <li>CAMEL_CASE: follows the camel case convention, deletes non
 * JavaIndentifierPart chars.</li>
 * <li>UNDERSCORE: transform whitespace and non JavaIdentifierPart chars to
 * '_'.</li>
 * <li>WHITESPACE: deletes whitespace and non JavaIdentifierPart chars.</li>
 * <li>STRICT: always throws a JSONException, does not perform transformation.</li>
 * </ul>
 *
 * @author Andres Almiray
 */
public abstract class JavaIdentifierTransformer {
    /**
     * CamelCase transformer 'camel case' => 'camelCase'
     */
    public static final JavaIdentifierTransformer CAMEL_CASE = new CamelCaseJavaIdentifierTransformer();
    /**
     * Noop transformer '@invalid' => '@invalid'
     */
    public static final JavaIdentifierTransformer NOOP = new NoopJavaIdentifierTransformer();
    /**
     * Strict transformer '@invalid' => JSONException
     */
    public static final JavaIdentifierTransformer STRICT = new StrictJavaIdentifierTransformer();
    /**
     * Underscore transformer 'under score' => 'under_score'
     */
    public static final JavaIdentifierTransformer UNDERSCORE = new UnderscoreJavaIdentifierTransformer();
    /**
     * Whitespace transformer 'white space' => 'whitespace'
     */
    public static final JavaIdentifierTransformer WHITESPACE = new WhiteSpaceJavaIdentifierTransformer();

    public abstract String transformToJavaIdentifier(String str);

    /**
     * Removes all non JavaIdentifier chars from the start of the string.
     *
     * @throws JSONException if the resulting string has zero length.
     */
    protected final String shaveOffNonJavaIdentifierStartChars(String str) {
        String str2 = str;
        // shave off first char if not valid
        boolean ready = false;
        while (!ready) {
            if (!Character.isJavaIdentifierStart(str2.charAt(0))) {
                str2 = str2.substring(1);
                if (str2.length() == 0) {
                    throw new JSONException("Can't convert '" + str + "' to a valid Java identifier");
                }
            } else {
                ready = true;
            }
        }
        return str2;
    }

    private static final class CamelCaseJavaIdentifierTransformer extends JavaIdentifierTransformer {
        public String transformToJavaIdentifier(String str) {
            if (str == null) {
                return null;
            }

            String str2 = shaveOffNonJavaIdentifierStartChars(str);

            char[] chars = str2.toCharArray();
            int pos = 0;
            StringBuffer buf = new StringBuffer();
            boolean toUpperCaseNextChar = false;
            while (pos < chars.length) {
                if (!Character.isJavaIdentifierPart(chars[pos])
                    || Character.isWhitespace(chars[pos])) {
                    toUpperCaseNextChar = true;
                } else {
                    if (toUpperCaseNextChar) {
                        buf.append(Character.toUpperCase(chars[pos]));
                        toUpperCaseNextChar = false;
                    } else {
                        buf.append(chars[pos]);
                    }
                }
                pos++;
            }
            return buf.toString();
        }
    }

    private static final class NoopJavaIdentifierTransformer extends JavaIdentifierTransformer {
        public String transformToJavaIdentifier(String str) {
            return str;
        }
    }

    private static final class StrictJavaIdentifierTransformer extends JavaIdentifierTransformer {
        public String transformToJavaIdentifier(String str) {
            throw new JSONException("'" + str + "' is not a valid Java identifier.");
        }
    }

    private static final class UnderscoreJavaIdentifierTransformer extends JavaIdentifierTransformer {
        public String transformToJavaIdentifier(String str) {
            if (str == null) {
                return null;
            }
            String str2 = shaveOffNonJavaIdentifierStartChars(str);

            char[] chars = str2.toCharArray();
            int pos = 0;
            StringBuffer buf = new StringBuffer();
            boolean toUnderScorePreviousChar = false;
            while (pos < chars.length) {
                if (!Character.isJavaIdentifierPart(chars[pos])
                    || Character.isWhitespace(chars[pos])) {
                    toUnderScorePreviousChar = true;
                } else {
                    if (toUnderScorePreviousChar) {
                        buf.append("_");
                        toUnderScorePreviousChar = false;
                    }
                    buf.append(chars[pos]);
                }
                pos++;
            }
            if (buf.charAt(buf.length() - 1) == '_') {
                buf.deleteCharAt(buf.length() - 1);
            }
            return buf.toString();
        }
    }

    private static final class WhiteSpaceJavaIdentifierTransformer extends JavaIdentifierTransformer {
        public String transformToJavaIdentifier(String str) {
            if (str == null) {
                return null;
            }
            String str2 = shaveOffNonJavaIdentifierStartChars(str);
            str2 = StringUtils.deleteWhitespace(str2);
            char[] chars = str2.toCharArray();
            int pos = 0;
            StringBuffer buf = new StringBuffer();
            while (pos < chars.length) {
                if (Character.isJavaIdentifierPart(chars[pos])) {
                    buf.append(chars[pos]);
                }
                pos++;
            }
            return buf.toString();
        }
    }
}