// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Microsoft.CSharp.RuntimeBinder.Syntax;

namespace Microsoft.CSharp.RuntimeBinder.Semantics
{
    internal abstract class CType
    {
        private protected CType(TypeKind kind)
        {
            TypeKind = kind;
        }

        public bool IsWindowsRuntimeType => (AssociatedSystemType.Attributes & TypeAttributes.WindowsRuntime) != 0;

        [ExcludeFromCodeCoverage] // Should only be called through override.
        public virtual Type AssociatedSystemType => throw Error.InternalCompilerError();

        public TypeKind TypeKind { get; }

        public virtual CType BaseOrParameterOrElementType => null;

        ////////////////////////////////////////////////////////////////////////////////
        // Given a symbol, determine its fundamental type. This is the type that
        // indicate how the item is stored and what instructions are used to reference
        // if. The fundamental types are:
        // one of the integral/float types (includes enums with that underlying type)
        // reference type
        // struct/value type
        public virtual FUNDTYPE FundamentalType => FUNDTYPE.FT_NONE;

        public virtual ConstValKind ConstValKind => ConstValKind.Int;

        ////////////////////////////////////////////////////////////////////////////////
        // Strips off ArrayType, ParameterModifierType, PointerType, PinnedType and optionally NullableType
        // and returns the result.
        public CType GetNakedType(bool fStripNub)
        {
            for (CType type = this; ;)
            {
                switch (type.TypeKind)
                {
                    default:
                        return type;

                    case TypeKind.TK_NullableType:
                        if (!fStripNub)
                        {
                            goto default;
                        }

                        goto case TypeKind.TK_ArrayType;

                    case TypeKind.TK_ArrayType:
                    case TypeKind.TK_ParameterModifierType:
                    case TypeKind.TK_PointerType:
                        type = type.BaseOrParameterOrElementType;
                        break;
                }
            }
        }

        public virtual CType StripNubs() => this;

        public virtual CType StripNubs(out bool wasNullable)
        {
            wasNullable = false;
            return this;
        }

        public virtual bool IsDelegateType => false;

        ////////////////////////////////////////////////////////////////////////////////
        // A few types are considered "simple" types for purposes of conversions and so
        // on. They are the fundamental types the compiler knows about for operators and
        // conversions.
        public virtual bool IsSimpleType => false;

        public virtual bool IsSimpleOrEnum => false;

        public virtual bool IsSimpleOrEnumOrString => false;

        ////////////////////////////////////////////////////////////////////////////////
        // A few types are considered "numeric" types. They are the fundamental number
        // types the compiler knows about for operators and conversions.
        public virtual bool IsNumericType => false;

        public virtual bool IsStructOrEnum => false;

        public virtual bool IsStructType => false;

        public virtual bool IsEnumType => false;

        public virtual bool IsInterfaceType => false;

        public virtual bool IsClassType => false;

        [ExcludeFromCodeCoverage] // Should only be called through override.
        public virtual AggregateType UnderlyingEnumType => throw Error.InternalCompilerError();

        // Pointer types (or arrays of them) are the only unsafe types.
        // Note that generics may not be instantiated with pointer types
        public virtual bool IsUnsafe() => false;

        public virtual bool IsPredefType(PredefinedType pt) => false;

        public virtual bool IsPredefined => false;

        [ExcludeFromCodeCoverage] // Should only be called through override.
        public virtual PredefinedType PredefinedType => throw Error.InternalCompilerError();

        public virtual bool IsStaticClass => false;

        // These check for AGGTYPESYMs, TYVARSYMs and others as appropriate.
        public virtual bool IsValueType => false;

        public virtual bool IsNonNullableValueType => false;

        public virtual bool IsReferenceType => false;

        [ExcludeFromCodeCoverage] // Should only be called through override.
        public virtual AggregateType GetAts()
        {
            Debug.Fail("Bad type for AsAggregateType");
            return null;
        }
    }
}
