/* **********************************************************************************
 *
 * Copyright (c) Microsoft Corporation. All rights reserved.
 *
 * This source code is subject to terms and conditions of the Shared Source License
 * for IronPython. A copy of the license can be found in the License.html file
 * at the root of this distribution. If you can not locate the Shared Source License
 * for IronPython, please send an email to ironpy@microsoft.com.
 * By using this source code in any fashion, you are agreeing to be bound by
 * the terms of the Shared Source License for IronPython.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * **********************************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;

namespace IronPython.Runtime {
    public interface ICallable {
        object Call(params object[] args);
    }

    public interface ITryCallable : ICallable {
        bool TryCall(object[] args, out object ret);
    }

    public interface IFancyCallable {
        object Call(object[] args, string[] names);
    }

    public interface ICallableWithCallerContext {
        object Call(ICallerContext context, object[] args);
    }

    public interface IContextAwareMember {
        bool IsVisible(ICallerContext context);
    }

    public interface ICallerContext {
        PythonModule Module { get; }
        object Locals { get; }
        object Globals { get; }
        object GetStaticData(int index);
        bool TrueDivision { get; set; }
        CallerContextFlags ContextFlags { get; set; }
        IronPython.Compiler.CompilerContext CreateCompilerContext();
    }

    public interface IFrameEnvironment : ICallerContext {
        object GetGlobal(string name);
        void SetGlobal(string name, object value);
        void DelGlobal(string name);
    }

    public interface IDynamicObject {
        DynamicType GetDynamicType();
    }

    public interface ISuperDynamicObject : IDynamicObject {
        /// <returns>Returns a dictionary that can be indexed by SymbolId. This should only be used for lookup.
        /// If you need to iterate over the keys, use GetObjectKeyedDict(). See FieldIdDict for more information</returns>
        IDictionary<SymbolId, object> GetDict();
        void SetDict(IDictionary<SymbolId, object> dict);
        IDictionary<object, object> GetObjectKeyedDict();

        void SetDynamicType(UserType newType);  //??? maybe PythonType
    }

    /// <summary>
    /// This interface objects to specify how to look up attributes (for code like "obj.attr").
    /// If an object does not implement this interface, its DynamicType is then asked to find the attribute.
    /// See Ops.GetAttrNames() for how this works.
    /// </summary>
    public interface ICustomAttributes {
        bool TryGetAttr(ICallerContext context, SymbolId name, out object value);
        void SetAttr(ICallerContext context, SymbolId name, object value);
        void DeleteAttr(ICallerContext context, SymbolId name);

        /// <returns>The returned List contains all the attributes of the instance. ie. all the keys in the 
        /// dictionary of the object. Note that it can contain objects that are not strings. Such keys can be
        /// added using syntax like:
        ///     obj.__dict__[100] = someOtherObject
        /// </returns>
        List GetAttrNames(ICallerContext context);  
        IDictionary<object, object> GetAttrDict(ICallerContext context);
    }

    public interface ICodeFormattable {
        string ToCodeString();
    }

    public interface IDescriptor {
        [PythonName("__get__")]
        object GetAttribute(object instance, object owner);
    }

    /// <summary>
    /// Implements the IDataDescriptor interface.  Descriptors provide an opportunity
    /// for attribute access to be intercepted on a per-attribute basis.  Data descriptors
    /// provide this functionlity for writable attributes.
    /// 
    /// Internally we also support "static" descriptors which act at a class level instead of 
    /// just an instance level - these are ReflectedMethods and ReflectedPropertys that are static.  
    /// This is the reason for the bool return value, and is a feature not available to pure-Python code.
    /// If the descriptor returns true then the set has been handled, otherwise it has not.
    /// </summary>
    public interface IDataDescriptor : IDescriptor {
        [PythonName("__set__")]
        bool SetAttribute(object instance, object value);
        [PythonName("__delete__")]
        bool DeleteAttribute(object instance);
    }

    public interface IPythonContainer {
        [PythonName("__len__")]
        int GetLength();
        [PythonName("__contains__")]
        bool ContainsValue(object value);
        //???object __iter__(); //??? this vs. IEnumerable
    }


    public interface ISequence : IPythonContainer {
        [PythonName("__add__")]
        object AddSequence(object other);  //??? require that other be ISequence
        [PythonName("__mul__")]
        object MultiplySequence(int count);

        object this[int index] {
            get;
        }
        object this[Slice slice] {
            get;
        }
        //object __getitem__(int index);
        //object __getitem__(Slice slice);
        //??? Do we need backwards compat support for __*slice__ methods
    }

    public interface IMutableSequence : ISequence {
        new object this[int index] {
            set;
        }
        new object this[Slice slice] {
            set;
        }

        [PythonName("__delitem__")]
        void DeleteItem(int index);
        [PythonName("__delitem__")]
        void DeleteItem(Slice slice);
    }

    public interface IMapping : IPythonContainer {
        [PythonName("get")]
        object GetValue(object key);
        [PythonName("get")]
        object GetValue(object key, object defaultValue);

        bool TryGetValue(object key, out object value);

        object this[object key] {
            get;
            set;
        }
        [PythonName("__delitem__")]
        void DeleteItem(object key);
    }

    public interface IPythonComparable {
        object CompareTo(object other);
        object GreaterThan(object other);
        object LessThan(object other);
        object GreaterThanOrEqual(object other);
        object LessThanOrEqual(object other);
    }

    // __hash__ in Python does not work for mutable objects, and __eq__ implements deep comparion for 
    // many of the built-in types like "list". However, object.GetHashCode needs to work for all objects
    // for interoperability with non-Python code as System.Collections.Hashtable uses it.
    // (Note that this works because the default implementation of object.Equals implements 
    // shallow comparison). 
    // IPythonEquality allows different implementation for the two versions of hashing/equality.
    // By default, Python hooks up GetHashCode and __hash__ for Python types. However,
    // a mutable Python type can explicitly throw a TypeError from __hash__/PythonGetHashCode
    // (by calling Ops.TypeErrorForUnhashableType) but it should return some hash value from object.GetHashCode. 
    // This allows hashing behavior consistent with the environment the type is used in (ie. Python vs. non-Python).
    public interface IPythonEquality {
        object PythonGetHashCode();
        object PythonEquals(object other);
    }

    public interface IWeakReferenceable {
        WeakRefTracker GetWeakRef();
        void SetWeakRef(WeakRefTracker value);
    }

    public interface IProxyObject {
        object Target { get; }
    }

    public interface ICustomExceptionConversion {
        object ToPythonException();
    }
}
