using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Lucene.Net.Util
{
    /*
     * 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.
     */

    /// <summary> Base class for Attributes that can be added to a
    /// <see cref="Lucene.Net.Util.AttributeSource" />.
    /// <p/>
    /// Attributes are used to add data in a dynamic, yet type-safe way to a source
    /// of usually streamed objects, e. g. a <see cref="Lucene.Net.Analysis.TokenStream" />.
    /// </summary>
#if FEATURE_SERIALIZABLE
    [Serializable]
#endif
    public abstract class Attribute : IAttribute
#if FEATURE_CLONEABLE
        , ICloneable
#endif
    {
        /// <summary> Clears the values in this Attribute and resets it to its
        /// default value. If this implementation implements more than one Attribute interface
        /// it clears all.
        /// </summary>
        public abstract void Clear();

        /// <summary>
        /// This is equivalent to the anonymous class in the java version of ReflectAsString
        /// </summary>
        private class StringBuilderAttributeReflector : IAttributeReflector
        {
            private readonly StringBuilder buffer;
            private readonly bool prependAttClass;

            public StringBuilderAttributeReflector(StringBuilder buffer, bool prependAttClass)
            {
                this.buffer = buffer;
                this.prependAttClass = prependAttClass;
            }

            public void Reflect<T>(string key, object value)
                where T : IAttribute
            {
                Reflect(typeof(T), key, value);
            }

            public void Reflect(Type type, string key, object value)
            {
                if (buffer.Length > 0)
                {
                    buffer.Append(',');
                }
                if (prependAttClass)
                {
                    buffer.Append(type.Name).Append('#');
                }
                buffer.Append(key).Append('=').Append(object.ReferenceEquals(value, null) ? (object)"null" : value);
            }
        }

        public string ReflectAsString(bool prependAttClass)
        {
            StringBuilder buffer = new StringBuilder();

            ReflectWith(new StringBuilderAttributeReflector(buffer, prependAttClass));

            return buffer.ToString();
        }

        public virtual void ReflectWith(IAttributeReflector reflector) // LUCENENET NOTE: This method was abstract in Lucene
        {
            Type clazz = this.GetType();
            LinkedList<WeakReference> interfaces = AttributeSource.GetAttributeInterfaces(clazz);

            if (interfaces.Count != 1)
            {
                throw new NotSupportedException(clazz.Name + " implements more than one Attribute interface, the default ReflectWith() implementation cannot handle this.");
            }

            Type interf = (System.Type)interfaces.First().Target;

            /*object target = interfaces.First.Value;

            if (target == null)
                return;

            Type interf = target.GetType();// as Type;*/

            //problem: the interfaces list has weak references that could have expired already

            FieldInfo[] fields = clazz.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
            try
            {
                for (int i = 0; i < fields.Length; i++)
                {
                    FieldInfo f = fields[i];
                    if (f.IsStatic) continue;
                    reflector.Reflect(interf, f.Name, f.GetValue(this));
                }
            }
            catch (MemberAccessException e)
            {
                throw new Exception(e.ToString(), e);
            }
        }

        /// <summary> The default implementation of this method accesses all declared
        /// fields of this object and prints the values in the following syntax:
        ///
        /// <code>
        /// public String toString() {
        /// return "start=" + startOffset + ",end=" + endOffset;
        /// }
        /// </code>
        ///
        /// This method may be overridden by subclasses.
        /// </summary>
        public override string ToString() // LUCENENET NOTE: This method didn't exist in Lucene
        {
            StringBuilder buffer = new StringBuilder();
            Type clazz = this.GetType();
            FieldInfo[] fields = clazz.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static);
            for (int i = 0; i < fields.Length; i++)
            {
                FieldInfo f = fields[i];
                if (f.IsStatic)
                    continue;
                //f.setAccessible(true);   // {{Aroush-2.9}} java.lang.reflect.AccessibleObject.setAccessible
                object value_Renamed = f.GetValue(this);
                if (buffer.Length > 0)
                {
                    buffer.Append(',');
                }
                if (value_Renamed == null)
                {
                    buffer.Append(f.Name + "=null");
                }
                else
                {
                    buffer.Append(f.Name + "=" + value_Renamed);
                }
            }

            return buffer.ToString();
        }

        /// <summary> Copies the values from this Attribute into the passed-in
        /// target attribute. The target implementation must support all the
        /// Attributes this implementation supports.
        /// </summary>
        public abstract void CopyTo(IAttribute target);

        /// <summary> Shallow clone. Subclasses must override this if they
        /// need to clone any members deeply,
        /// </summary>
        public virtual object Clone()
        {
            return base.MemberwiseClone();
        }
    }
}