using System;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Security.Cryptography;
using System.Diagnostics;
using System.Reflection.Emit;


namespace Cosmos.Compiler.Assembler
{
    public static class MethodInfoLabelGenerator
    {
      public static uint LabelCount {
        get;
        private set;
      }

        public static string GenerateLabelName( MethodBase aMethod )
        {
            string xResult = DataMember.FilterStringForIncorrectChars( GenerateFullName( aMethod ) );
            xResult = GenerateLabelFromFullName( xResult );
            LabelCount++;
            return xResult;
        }

        public static string GenerateLabelFromFullName( string xResult )
        {
            if( xResult.Length > 245 )
            {
                using( var xHash = MD5.Create() )
                {
                    xResult = xHash.ComputeHash(
                        Encoding.Default.GetBytes( xResult ) ).Aggregate( "_", ( r, x ) => r + x.ToString( "X2" ) );
                }
            }
            return xResult;
        }


        //public static string GenerateLabelName(MethodBase aMethod)
        //{
        //    return GenerateLabelNameInt(aMethod).ToString();
        //}
        //            unsigned long hash(char *name)
        //{
        // unsigned long h = 0, g;

        // while ( *name ) {
        //  h = ( h << 4 ) + *name++;
        //  if ( g = h & 0xF0000000 )
        //  h ^= g >> 24;
        //  h &= ~g;
        // }

        // return h;
        //}



        public static string GetFullName( Type aType )
        {
            if( aType.IsGenericParameter )
            {
              return aType.FullName;
            }
            var xSB = new StringBuilder();
            if( aType.IsArray )
            {
                xSB.Append( GetFullName( aType.GetElementType() ) );
                xSB.Append( "[" );
                int xRank = aType.GetArrayRank();
                while( xRank > 1 )
                {
                    xSB.Append( "," );
                    xRank--;
                }
                xSB.Append( "]" );
                return xSB.ToString();
            }
            if( aType.IsByRef && aType.HasElementType )
            {
                return "&" + GetFullName( aType.GetElementType() );
            }
            if( aType.IsGenericType && !aType.IsGenericTypeDefinition )
            {
                xSB.Append( GetFullName(aType.GetGenericTypeDefinition()));
            }
            else
            {
              xSB.Append(aType.FullName);
            }
            if( aType.IsGenericType )
            {
                xSB.Append( "<" );
                var xArgs = aType.GetGenericArguments();
                for( int i = 0; i < xArgs.Length - 1; i++ )
                {
                    xSB.Append( GetFullName( xArgs[ i ] ) );
                    xSB.Append( ", " );
                }
                xSB.Append( GetFullName( xArgs.Last() ) );
                xSB.Append( ">" );
            }
            //xSB.Append(", ");
            //xSB.Append(aType.Assembly.FullName);
            return xSB.ToString();
        }

        public static string GenerateFullName( MethodBase aMethod )
        {
            if( aMethod == null )
            {
                throw new ArgumentNullException( "aMethod" );
            }
            var xBuilder = new StringBuilder( 256 );
            var xParts = aMethod.ToString().Split( ' ' );
            var xParts2 = xParts.Skip( 1 ).ToArray();
            var xMethodInfo = aMethod as System.Reflection.MethodInfo;
            if( xMethodInfo != null )
            {
                xBuilder.Append( GetFullName( xMethodInfo.ReturnType ) );
            }
            else
            {
                var xCtor = aMethod as ConstructorInfo;
                if( xCtor != null )
                {
                    xBuilder.Append( typeof( void ).FullName );
                }
                else
                {
                    xBuilder.Append( xParts[ 0 ] );
                }
            }
            xBuilder.Append( "  " );
            if (aMethod.DeclaringType != null)
            {
                xBuilder.Append(GetFullName(aMethod.DeclaringType));
            }
            else
            {
                xBuilder.Append("dynamic_method");
            }
            xBuilder.Append( "." );
            xBuilder.Append( aMethod.Name );
            if( aMethod.IsGenericMethod || aMethod.IsGenericMethodDefinition )
            {
                var xGenArgs = aMethod.GetGenericArguments();
                if( xGenArgs.Length > 0 )
                {
                    xBuilder.Append( "<" );
                    for( int i = 0; i < xGenArgs.Length - 1; i++ )
                    {
                        xBuilder.Append( GetFullName( xGenArgs[ i ] ) );
                        xBuilder.Append( ", " );
                    }
                    xBuilder.Append( GetFullName( xGenArgs.Last() ) );
                    xBuilder.Append( ">" );
                }
            }
            xBuilder.Append( "(" );
            var xParams = aMethod.GetParameters();
            for( var i = 0; i < xParams.Length; i++ )
            {
                if( xParams[ i ].Name == "aThis" && i == 0 )
                {
                    continue;
                }
                xBuilder.Append( GetFullName( xParams[ i ].ParameterType ) );
                if( i < ( xParams.Length - 1 ) )
                {
                    xBuilder.Append( ", " );
                }
            }
            xBuilder.Append( ")" );
            return String.Intern( xBuilder.ToString() );
        }

        public static string GetFullName( FieldInfo aField )
        {
            return GetFullName( aField.FieldType ) + " " + GetFullName( aField.DeclaringType ) + "." + aField.Name;
            //var xSB = new StringBuilder(aField.FieldType.FullName.Length + 1 + aField.DeclaringType.FullName.Length + 1 + aField.Name);
            //xSB.Append(aField.FieldType.FullName);
            //xSB.Append(" ");
            //xSB.Append(aField.DeclaringType.FullName);
            //xSB.Append(".");
            //xSB.Append(aField.Name);
            //return String.Intern(xSB.ToString());
        }
    }
}
