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

package org.apache.ignite.internal.error.code.generators;

import com.google.common.base.CaseFormat;
import java.io.IOException;
import java.util.List;
import javax.annotation.processing.ProcessingEnvironment;
import org.apache.ignite.internal.error.code.processor.ErrorCodeGroupDescriptor;

/**
 * C# generator for Error Codes.
 */
public class CsharpGenerator extends GenericGenerator {
    private static final String SuffixToChop = "Err";

    public CsharpGenerator(ProcessingEnvironment processingEnvironment, String outFilePath) {
        super(processingEnvironment, outFilePath);
    }

    private static String transfromErrorCodeName(String name) {
        var transformed = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name);
        if (transformed.endsWith(SuffixToChop)) {
            return transformed.substring(0, transformed.length() - SuffixToChop.length());
        }
        return transformed;
    }

    @Override
    void generateFile(List<ErrorCodeGroupDescriptor> descriptors) throws IOException {
        line("// <auto-generated/>");
        line("namespace Apache.Ignite");
        line("{");
        line("    using System;");
        line();
        generateClass(descriptors);
        line("}");
    }

    private void generateClass(List<ErrorCodeGroupDescriptor> descriptors) throws IOException {
        line("    public static partial class ErrorGroups");
        line("    {");
        line("        /// <summary>");
        line("        /// Gets the group name by code.");
        line("        /// </summary>");
        line("        /// <param name=\"groupCode\">Group code.</param>");
        line("        /// <returns>Group name.</returns>");
        line("        public static string GetGroupName(int groupCode) => groupCode switch");
        line("        {");
        for (var descriptor : descriptors) {
            line("            " + descriptor.className + ".GroupCode => " + descriptor.className + ".GroupName,");
        }
        line();
        line("            _ => UnknownGroupName");
        line("        };");
        line();
        line("        /// <summary>");
        line("        /// Gets the group error prefix by code.");
        line("        /// </summary>");
        line("        /// <param name=\"groupCode\">Group code.</param>");
        line("        /// <returns>Group error prefix.</returns>");
        line("        public static string GetErrorPrefix(int groupCode) => groupCode switch");
        line("        {");
        for (var descriptor : descriptors) {
            line("            " + descriptor.className + ".GroupCode => " + descriptor.className + ".ErrorPrefix,");
        }
        line();
        line("            _ => UnknownGroupName");
        line("        };");
        for (var descriptor : descriptors) {
            generateErrorGroupClass(descriptor);
        }
        line("    }");
    }

    private void generateErrorGroupClass(ErrorCodeGroupDescriptor descriptor) throws IOException {
        line();
        line("        /// <summary> " + descriptor.className + " errors. </summary>");
        line("        public static class " + descriptor.className);
        line("        {");

        line("            /// <summary> " + descriptor.className + " group code. </summary>");
        line("            public const short GroupCode = " + descriptor.groupCode + ";");
        line();

        line("            /// <summary> " + descriptor.className + " group name. </summary>");
        line("            public const String GroupName = \"" + descriptor.groupName + "\";");
        line();

        line("            /// <summary> " + descriptor.className + " error prefix. </summary>");
        line("            public const String ErrorPrefix = \"" + descriptor.errorPrefix + "\";");
        line();

        for (int i = 0; i < descriptor.errorCodes.size(); i++) {
            generateErrorCode(descriptor.errorCodes.get(i).name, descriptor.errorCodes.get(i).code);
            if (i != descriptor.errorCodes.size() - 1) {
                line();
            }
        }

        line("        }");
    }

    private void generateErrorCode(String name, int code) throws IOException {
        line(String.format("            /// <summary> %s error. </summary>", transfromErrorCodeName(name)));
        line(String.format("            public const int %s = (GroupCode << %d) | (%d & 0xFFFF);", transfromErrorCodeName(name), groupShift,
                code));
    }
}
