package cn.ibizlab.codegen.templating.handlebars;

import cn.ibizlab.codegen.utils.Inflector;
import cn.ibizlab.codegen.utils.StringAdvUtils;
import com.github.jknack.handlebars.Helper;
import com.github.jknack.handlebars.Options;
import com.github.jknack.handlebars.TagType;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.Locale;

public enum StringHelpers implements Helper<Object> {

    /**
     * Indicates the string starts with the defined value.
     * For example:
     *
     * <pre>
     * {{startsWith a b ["insensitive"]}}
     * </pre>
     *
     * <pre>
     * {{startsWith a text='b' [insensitive=true]}}
     * </pre>
     *
     * Render 'yes' or 'no':
     * <pre>
     *   {{#startsWith a b}}
     *     yes
     *   {{else}}
     *     no
     *   {{/startsWith}}
     * </pre>
     *
     * Render 'true' or 'false':
     * <pre>
     *   {{startsWith a b}}
     * </pre>
     *
     * Render 'y' or 'n':
     * <pre>
     *   {{startsWith a b yes='y' no='n'}}
     * </pre>
     *
     * If value is "handlebars.java", the output will be "Handlebars.java".
     */
    startsWith {
        @Override
        public Object apply(Object value, Options options) throws IOException {
            String match = options.param(0, options.hash("text", ""));
            if (match.length() < 1) {
                return false;
            }

            boolean caseInsensitive = options.hash("insensitive", false);
            boolean result = caseInsensitive ? value.toString().toLowerCase(Locale.ROOT).startsWith(match.toLowerCase(Locale.ROOT)) : value.toString().startsWith(match);

            if (options.tagType == TagType.SECTION) {
                return result ? options.fn() : options.inverse();
            }

            return result
                    ? options.hash("yes", true)
                    : options.hash("no", false);
        }

        @Override
        protected CharSequence safeApply(Object context, Options options) {
            return null;
        }
    },
    lowerCase {
        @Override
        protected CharSequence safeApply(final Object value, final Options options) {
            return value.toString().toLowerCase();
        }
    },
    upperCase {
        @Override
        protected CharSequence safeApply(final Object value, final Options options) {
            return value.toString().toUpperCase();
        }
    },
    pascalCase {
        @Override
        protected CharSequence safeApply(final Object value, final Options options) {
            return StringAdvUtils.pascalcase(value.toString());
        }
    },
    capFirst {
        @Override
        protected CharSequence safeApply(final Object value, final Options options) {
            return StringAdvUtils.pascalcase(value.toString());
        }
    },
    camelCase {
        @Override
        protected CharSequence safeApply(final Object value, final Options options) {
            return StringAdvUtils.camelcase(value.toString());
        }
    },
    spinalCase {
        @Override
        protected CharSequence safeApply(final Object value, final Options options) {
            return StringAdvUtils.spinalcase(value.toString());
        }
    },
    snakeCase {
        @Override
        protected CharSequence safeApply(final Object value, final Options options) {
            return StringAdvUtils.snakecase(value.toString());
        }
    },
    pluralize {
        @Override
        protected CharSequence safeApply(final Object value, final Options options) {
            return Inflector.getInstance().pluralize(StringAdvUtils.camelcase(value.toString()).toLowerCase());
        }
    };



    @Override
    public Object apply(final Object context, final Options options) throws IOException {
        if (options.isFalsy(context)) {
            Object param = options.param(0, null);
            return param == null ? null : param.toString();
        }
        return safeApply(context, options);
    }

    /**
     * Apply the helper to the context.
     *
     * @param context The context object (param=0).
     * @param options The options object.
     * @return A string result.
     */
    protected abstract CharSequence safeApply(Object context, Options options);


}
