using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Util;
using CppSharp.AST;
using CppSharp.AST.Extensions;
using CppSharp.Generators.CSharp;

namespace CppSharp.Generators
{
    public abstract class CodeGenerator : BlockGenerator, IAstVisitor<bool>
    {
        public BindingContext Context { get; }

        public DriverOptions Options => Context.Options;

        public List<TranslationUnit> TranslationUnits { get; }

        public TranslationUnit TranslationUnit => TranslationUnits[0];

        public abstract string FileExtension { get; }

        public virtual string FilePath =>
            $"{TranslationUnit.FileNameWithoutExtension}.{FileExtension}";

        /// <summary>
        /// Gets the comment style kind for regular comments.
        /// </summary>
        public virtual CommentKind CommentKind
        {
            get
            {
                if (!Options.CommentKind.HasValue)
                    return CommentKind.BCPL;

                return Options.CommentKind.Value;
            }
        }

        /// <summary>
        /// Gets the comment style kind for documentation comments.
        /// </summary>
        public virtual CommentKind DocumentationCommentKind => CommentKind.BCPLSlash;

        public ISet<object> Visited { get; } = new HashSet<object>();
        public AstVisitorOptions VisitOptions { get; }
            = new AstVisitorOptions(VisitFlags.Any & ~VisitFlags.PropertyAccessors);

        protected CodeGenerator(BindingContext context)
        {
            Context = context;
        }

        protected CodeGenerator(BindingContext context, TranslationUnit unit)
            : this(context, new List<TranslationUnit> { unit })
        {
        }

        protected CodeGenerator(BindingContext context, IEnumerable<TranslationUnit> units)
        {
            Context = context;
            if (units != null)
                TranslationUnits = new List<TranslationUnit>(units);
        }

        public abstract void Process();

        public virtual void GenerateFilePreamble(CommentKind kind, string generatorName = "CppSharp")
        {
            var lines = new List<string>
            {
                "----------------------------------------------------------------------------",
                "<auto-generated>",
                $"This is autogenerated code by {generatorName}.",
                "Do not edit this file or all your changes will be lost after re-generation.",
                "</auto-generated>",
                "----------------------------------------------------------------------------"
            };

            PushBlock(BlockKind.Header);
            GenerateMultiLineComment(lines, kind);
            PopBlock();
        }

        #region Declaration generation

        public virtual void GenerateDeclarationCommon(Declaration decl)
        {
            if (decl.Comment != null)
                GenerateComment(decl.Comment);

            GenerateDebug(decl);
        }

        public virtual void GenerateDebug(Declaration decl)
        {
            if (Options.GenerateDebugOutput && !string.IsNullOrWhiteSpace(decl.DebugText))
                foreach (var line in Regex.Split(decl.DebugText.Trim(), "\r?\n"))
                    WriteLine($"// DEBUG: {line}");
        }

        #endregion

        #region Comment generation

        public virtual void GenerateSummary(string comment)
        {
            if (string.IsNullOrWhiteSpace(comment))
                return;

            var lines = new List<string>
            {
                "<summary>",
                $"{comment}",
                "</summary>"
            };

            GenerateMultiLineComment(lines, DocumentationCommentKind);
        }

        public virtual void GenerateInlineSummary(RawComment comment)
        {
            GenerateComment(comment);
        }

        public virtual void GenerateComment(RawComment comment)
        {
            if (comment.FullComment != null)
            {
                PushBlock(BlockKind.BlockComment);
                ActiveBlock.Text.Print(comment.FullComment, DocumentationCommentKind);
                PopBlock();
                return;
            }

            if (string.IsNullOrWhiteSpace(comment.BriefText))
                return;

            var lines = new List<string>();

            if (comment.BriefText.Contains("\n"))
            {
                lines.Add("<summary>");
                foreach (string line in HtmlEncoder.HtmlEncode(comment.BriefText).Split(
                                            Environment.NewLine.ToCharArray()))
                {
                    if (string.IsNullOrWhiteSpace(line))
                        continue;

                    lines.Add($"<para>{line}</para>");
                }
                lines.Add("</summary>");
            }
            else
            {
                lines.Add($"<summary>{comment.BriefText}</summary>");
            }

            GenerateMultiLineComment(lines, CommentKind);
        }

        public virtual void GenerateMultiLineComment(List<string> lines, CommentKind kind)
        {
            PushBlock(BlockKind.BlockComment);

            var lineCommentPrologue = Comment.GetLineCommentPrologue(kind);
            if (!string.IsNullOrWhiteSpace(lineCommentPrologue))
                WriteLine("{0}", lineCommentPrologue);

            var multiLineCommentPrologue = Comment.GetMultiLineCommentPrologue(kind);
            foreach (var line in lines)
                WriteLine("{0} {1}", multiLineCommentPrologue, line);

            var lineCommentEpilogue = Comment.GetLineCommentEpilogue(kind);
            if (!string.IsNullOrWhiteSpace(lineCommentEpilogue))
                WriteLine("{0}", lineCommentEpilogue);

            PopBlock();
        }

        #endregion

        #region Enum generation

        public virtual void GenerateEnumItems(Enumeration @enum)
        {
            for (int i = 0; i < @enum.Items.Count; i++)
            {
                var item = @enum.Items[i];
                if (!item.IsGenerated)
                    continue;

                item.Visit(this);
                WriteLine(i == @enum.Items.Count - 1 ? string.Empty : ",");
            }
        }

        public virtual bool VisitEnumItemDecl(Enumeration.Item item)
        {
            if (item.Comment != null)
                GenerateInlineSummary(item.Comment);

            Write(item.Name);

            var @enum = item.Namespace as Enumeration;
            if (item.ExplicitValue)
                Write(" = {0}", @enum.GetItemValueAsString(item));

            return true;
        }

        #endregion

        #region Class generation

        public virtual void GenerateClassSpecifier(Class @class)
        {
        }

        #endregion

        #region Method generation

        public enum MethodSpecifierKind
        {
            Declaration,
            Definition
        }

        public virtual void GenerateMethodSpecifier(Method method,
            MethodSpecifierKind? kind = null)
        {
        }

        #endregion

        #region Visitor methods

        public virtual bool AlreadyVisited(CppSharp.AST.Type type)
        {
            return !Visited.Add(type);
        }

        public virtual bool AlreadyVisited(Declaration decl)
        {
            return !Visited.Add(decl);
        }

        public virtual bool VisitDeclaration(Declaration decl)
        {
            return !AlreadyVisited(decl);
        }

        public virtual bool VisitTranslationUnit(TranslationUnit unit)
        {
            return VisitNamespace(unit);
        }

        public virtual bool VisitDeclContext(DeclarationContext context)
        {
            foreach (var decl in context.Declarations)
            {
                if (decl is Function)
                    continue;

                if (decl is Class && !VisitOptions.VisitNamespaceClasses)
                    continue;

                if (decl is Enumeration && !VisitOptions.VisitNamespaceEnums)
                    continue;

                if (decl is Event && !VisitOptions.VisitNamespaceEvents)
                    continue;

                if (decl is Variable && !VisitOptions.VisitNamespaceVariables)
                    continue;

                if (decl.IsGenerated)
                    decl.Visit(this);
            }

            if (VisitOptions.VisitNamespaceFunctions)
                VisitDeclContextFunctions(context);

            return true;
        }

        public virtual void VisitDeclContextFunctions(DeclarationContext context)
        {
            foreach (var decl in context.Functions)
                decl.Visit(this);
        }

        public virtual bool VisitClassDecl(Class @class)
        {
            if (@class.IsIncomplete)
                return false;

            return VisitClassDeclContext(@class);
        }

        public virtual bool VisitClassDeclContext(Class @class)
        {
            if (!VisitDeclContext(@class))
                return false;

            foreach (var field in @class.Fields.Where(f => !ASTUtils.CheckIgnoreField(f)))
            {
                field.Visit(this);
            }

            foreach (var property in @class.Properties.Where(p => !ASTUtils.CheckIgnoreProperty(p)))
            {
                property.Visit(this);
            }

            VisitClassConstructors(@class);
            VisitClassMethods(@class);

            return true;
        }

        public virtual void VisitClassMethods(Class @class)
        {
            foreach (var method in @class.Methods.Where(c => !ASTUtils.CheckIgnoreMethod(c)))
            {
                if (method.IsConstructor)
                    continue;

                method.Visit(this);
            }
        }

        public virtual void VisitClassConstructors(Class @class)
        {
            foreach (var ctor in @class.Constructors.Where(c => !ASTUtils.CheckIgnoreMethod(c)))
            {
                ctor.Visit(this);
            }
        }

        public virtual bool VisitFieldDecl(Field field)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitFunctionDecl(Function function)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitMethodDecl(Method method)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitParameterDecl(Parameter parameter)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitTypedefNameDecl(TypedefNameDecl typedef)
        {
            return typedef.Type.Visit(this, typedef.QualifiedType.Qualifiers);
        }

        public virtual bool VisitTypedefDecl(TypedefDecl typedef)
        {
            return VisitTypedefNameDecl(typedef);
        }

        public virtual bool VisitTypeAliasDecl(TypeAlias typeAlias)
        {
            return VisitTypedefNameDecl(typeAlias);
        }

        public virtual bool VisitEnumDecl(Enumeration @enum)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitVariableDecl(Variable variable)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitMacroDefinition(MacroDefinition macro)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitNamespace(Namespace @namespace)
        {
            return VisitDeclContext(@namespace);
        }

        public virtual bool VisitEvent(Event @event)
        {
            return true;
        }

        public virtual bool VisitProperty(Property property)
        {
            if (!VisitDeclaration(property))
                return false;

            if (VisitOptions.VisitPropertyAccessors)
            {
                if (property.GetMethod != null)
                    property.GetMethod.Visit(this);

                if (property.SetMethod != null)
                    property.SetMethod.Visit(this);
            }

            return true;
        }

        public virtual bool VisitFriend(Friend friend)
        {
            return true;
        }

        public virtual bool VisitClassTemplateDecl(ClassTemplate template)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitClassTemplateSpecializationDecl(ClassTemplateSpecialization specialization)
        {
            return VisitClassDecl(specialization);
        }

        public virtual bool VisitFunctionTemplateDecl(FunctionTemplate template)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitFunctionTemplateSpecializationDecl(FunctionTemplateSpecialization specialization)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitVarTemplateDecl(VarTemplate template)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitVarTemplateSpecializationDecl(VarTemplateSpecialization template)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitTemplateTemplateParameterDecl(TemplateTemplateParameter templateTemplateParameter)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitTemplateParameterDecl(TypeTemplateParameter templateParameter)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitNonTypeTemplateParameterDecl(NonTypeTemplateParameter nonTypeTemplateParameter)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitTypeAliasTemplateDecl(TypeAliasTemplate typeAliasTemplate)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitTagType(TagType tag, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitUnresolvedUsingDecl(UnresolvedUsingTypename unresolvedUsingTypename)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitArrayType(ArrayType array, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitFunctionType(FunctionType function, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitPointerType(PointerType pointer, TypeQualifiers quals)
        {
            if (pointer.Pointee == null)
                return false;

            return pointer.QualifiedPointee.Visit(this);
        }

        public virtual bool VisitMemberPointerType(MemberPointerType member, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitBuiltinType(BuiltinType builtin, TypeQualifiers quals)
        {
            return VisitPrimitiveType(builtin.Type, quals);
        }

        public virtual bool VisitTypedefType(TypedefType typedef, TypeQualifiers quals)
        {
            return typedef.Declaration.Visit(this);
        }

        public virtual bool VisitAttributedType(AttributedType attributed, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitDecayedType(DecayedType decayed, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitTemplateSpecializationType(TemplateSpecializationType template, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitDependentTemplateSpecializationType(DependentTemplateSpecializationType template, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitPrimitiveType(PrimitiveType type, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitDeclaration(Declaration decl, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitTemplateParameterType(TemplateParameterType param, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitTemplateParameterSubstitutionType(TemplateParameterSubstitutionType param, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitInjectedClassNameType(InjectedClassNameType injected, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitDependentNameType(DependentNameType dependent, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitPackExpansionType(PackExpansionType packExpansionType, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitUnaryTransformType(UnaryTransformType unaryTransformType, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitUnresolvedUsingType(UnresolvedUsingType unresolvedUsingType, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitVectorType(VectorType vectorType, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCILType(CILType type, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitUnsupportedType(UnsupportedType type, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitQualifiedType(QualifiedType type)
        {
            return type.Type.Visit(this, type.Qualifiers);
        }

        public virtual bool VisitStmt(Stmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitDeclStmt(DeclStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitNullStmt(NullStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCompoundStmt(CompoundStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitSwitchCase(SwitchCase stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCaseStmt(CaseStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitDefaultStmt(DefaultStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitLabelStmt(LabelStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitAttributedStmt(AttributedStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitIfStmt(IfStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitSwitchStmt(SwitchStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitWhileStmt(WhileStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitDoStmt(DoStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitForStmt(ForStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitGotoStmt(GotoStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitIndirectGotoStmt(IndirectGotoStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitContinueStmt(ContinueStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitBreakStmt(BreakStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitReturnStmt(ReturnStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitAsmStmt(AsmStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitGCCAsmStmt(GCCAsmStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitMSAsmStmt(MSAsmStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitSEHExceptStmt(SEHExceptStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitSEHFinallyStmt(SEHFinallyStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitSEHTryStmt(SEHTryStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitSEHLeaveStmt(SEHLeaveStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCapturedStmt(CapturedStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXCatchStmt(CXXCatchStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXTryStmt(CXXTryStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXForRangeStmt(CXXForRangeStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitMSDependentExistsStmt(MSDependentExistsStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCoroutineBodyStmt(CoroutineBodyStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCoreturnStmt(CoreturnStmt stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitExpr(Expr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitFullExpr(FullExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitConstantExpr(ConstantExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitOpaqueValueExpr(OpaqueValueExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitDeclRefExpr(DeclRefExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitIntegerLiteral(IntegerLiteral stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitFixedPointLiteral(FixedPointLiteral stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCharacterLiteral(CharacterLiteral stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitFloatingLiteral(FloatingLiteral stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitImaginaryLiteral(ImaginaryLiteral stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitStringLiteral(StringLiteral stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitPredefinedExpr(PredefinedExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitParenExpr(ParenExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitUnaryOperator(UnaryOperator stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitOffsetOfExpr(OffsetOfExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitArraySubscriptExpr(ArraySubscriptExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCallExpr(CallExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitMemberExpr(MemberExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCompoundLiteralExpr(CompoundLiteralExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCastExpr(CastExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitImplicitCastExpr(ImplicitCastExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitExplicitCastExpr(ExplicitCastExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCStyleCastExpr(CStyleCastExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitBinaryOperator(BinaryOperator stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCompoundAssignOperator(CompoundAssignOperator stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitAbstractConditionalOperator(AbstractConditionalOperator stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitConditionalOperator(ConditionalOperator stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitBinaryConditionalOperator(BinaryConditionalOperator stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitAddrLabelExpr(AddrLabelExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitStmtExpr(StmtExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitShuffleVectorExpr(ShuffleVectorExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitConvertVectorExpr(ConvertVectorExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitChooseExpr(ChooseExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitGNUNullExpr(GNUNullExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitVAArgExpr(VAArgExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitInitListExpr(InitListExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitDesignatedInitExpr(DesignatedInitExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitNoInitExpr(NoInitExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitDesignatedInitUpdateExpr(DesignatedInitUpdateExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitArrayInitLoopExpr(ArrayInitLoopExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitArrayInitIndexExpr(ArrayInitIndexExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitImplicitValueInitExpr(ImplicitValueInitExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitParenListExpr(ParenListExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitGenericSelectionExpr(GenericSelectionExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitExtVectorElementExpr(ExtVectorElementExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitBlockExpr(BlockExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitAsTypeExpr(AsTypeExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitPseudoObjectExpr(PseudoObjectExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitAtomicExpr(AtomicExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitTypoExpr(TypoExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXMemberCallExpr(CXXMemberCallExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCUDAKernelCallExpr(CUDAKernelCallExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXNamedCastExpr(CXXNamedCastExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXStaticCastExpr(CXXStaticCastExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXDynamicCastExpr(CXXDynamicCastExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXReinterpretCastExpr(CXXReinterpretCastExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXConstCastExpr(CXXConstCastExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitUserDefinedLiteral(UserDefinedLiteral stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXNullPtrLiteralExpr(CXXNullPtrLiteralExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXStdInitializerListExpr(CXXStdInitializerListExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXTypeidExpr(CXXTypeidExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitMSPropertyRefExpr(MSPropertyRefExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitMSPropertySubscriptExpr(MSPropertySubscriptExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXUuidofExpr(CXXUuidofExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXThisExpr(CXXThisExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXThrowExpr(CXXThrowExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXDefaultArgExpr(CXXDefaultArgExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXDefaultInitExpr(CXXDefaultInitExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXConstructExpr(CXXConstructExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXInheritedCtorInitExpr(CXXInheritedCtorInitExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitLambdaExpr(LambdaExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXNewExpr(CXXNewExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXDeleteExpr(CXXDeleteExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitTypeTraitExpr(TypeTraitExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitArrayTypeTraitExpr(ArrayTypeTraitExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitExpressionTraitExpr(ExpressionTraitExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitOverloadExpr(OverloadExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitUnresolvedLookupExpr(UnresolvedLookupExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitExprWithCleanups(ExprWithCleanups stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitUnresolvedMemberExpr(UnresolvedMemberExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXNoexceptExpr(CXXNoexceptExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitPackExpansionExpr(PackExpansionExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitSizeOfPackExpr(SizeOfPackExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitSubstNonTypeTemplateParmPackExpr(SubstNonTypeTemplateParmPackExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitFunctionParmPackExpr(FunctionParmPackExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCXXFoldExpr(CXXFoldExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCoroutineSuspendExpr(CoroutineSuspendExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCoawaitExpr(CoawaitExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitDependentCoawaitExpr(DependentCoawaitExpr stmt)
        {
            throw new NotImplementedException();
        }

        public virtual bool VisitCoyieldExpr(CoyieldExpr stmt)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

    public static class Helpers
    {
        public static Regex RegexTag = new Regex(@"^(<|</)[a-zA-Z][\w\-]*?>?$");
        public static Regex RegexCommentCommandLeftover = new Regex(@"^\S*");
        public static readonly string InternalStruct = Generator.GeneratedIdentifier("Internal");
        public static readonly string InstanceField = Generator.GeneratedIdentifier("instance");
        public static readonly string InstanceIdentifier = Generator.GeneratedIdentifier("Instance");
        public static readonly string PrimaryBaseOffsetIdentifier = Generator.GeneratedIdentifier("PrimaryBaseOffset");
        public static readonly string ReturnIdentifier = Generator.GeneratedIdentifier("ret");
        public static readonly string DummyIdentifier = Generator.GeneratedIdentifier("dummy");
        public static readonly string TargetIdentifier = Generator.GeneratedIdentifier("target");
        public static readonly string SlotIdentifier = Generator.GeneratedIdentifier("slot");
        public static readonly string PtrIdentifier = Generator.GeneratedIdentifier("ptr");

        public static readonly string OwnsNativeInstanceIdentifier = Generator.GeneratedIdentifier("ownsNativeInstance");

        public static readonly string CreateInstanceIdentifier = Generator.GeneratedIdentifier("CreateInstance");
        public static readonly string GetOrCreateInstanceIdentifier = Generator.GeneratedIdentifier("GetOrCreateInstance");
        public static readonly string RecordNativeToManagedMappingIdentifier = Generator.GeneratedIdentifier("RecordNativeToManagedMapping");
        public static readonly string TryGetNativeToManagedMappingIdentifier = Generator.GeneratedIdentifier("TryGetNativeToManagedMapping");

        public static string GetSuffixForInternal(Class @class)
        {
            var specialization = @class as ClassTemplateSpecialization ??
                @class.Namespace as ClassTemplateSpecialization;

            if (specialization == null)
                return string.Empty;

            Class template = specialization.TemplatedDecl.TemplatedClass;
            if (@class != specialization)
                template = template.Classes.FirstOrDefault(c => c.Name == @class.Name);

            if (template.HasDependentValueFieldInLayout())
            {
                if (specialization.Arguments.All(
                    a => a.Type.Type?.IsAddress() == true))
                    return "_Ptr";
                return GetSuffixFor(specialization);
            }
            return string.Empty;
        }

        public static string GetSuffixFor(Declaration decl)
        {
            var suffixBuilder = new StringBuilder(decl.USR);
            for (int i = 0; i < suffixBuilder.Length; i++)
                if (!char.IsLetterOrDigit(suffixBuilder[i]))
                    suffixBuilder[i] = '_';
            const int maxCSharpIdentifierLength = 480;
            if (suffixBuilder.Length > maxCSharpIdentifierLength)
                return suffixBuilder.Remove(maxCSharpIdentifierLength,
                    suffixBuilder.Length - maxCSharpIdentifierLength).ToString();
            return suffixBuilder.ToString();
        }

        public static string GetAccess(AccessSpecifier accessSpecifier)
        {
            switch (accessSpecifier)
            {
                case AccessSpecifier.Private:
                case AccessSpecifier.Internal:
                    return "internal ";
                case AccessSpecifier.Protected:
                    return "protected ";
                default:
                    return "public ";
            }
        }
    }
}