using System;
using System.Collections.Generic;
using omg.org.CosNaming;
using Ch.Elca.Iiop.Services;
using omg.org.CosNaming.NamingContext_package;
using System.Reflection;

namespace IIOPLib
{
    public class Ref
    {
        public string Name { get; private set; }
        public MarshalByRefObject Obj { get; private set; }
        public Ref(string name, MarshalByRefObject obj)
        {
            Name = name;
            Obj = obj;
        }
    }


    public class Naming
    {
        NameComponent[] _adders = new NameComponent[] { new NameComponent("Adders", "") };
        string _orbInitialHost;
        int _orbInitialPort;

        public Naming(string orbInitialHost, int orbInitialPort)
        {
            _orbInitialHost = orbInitialHost;
            _orbInitialPort = orbInitialPort;
        }

        public void Bind(CorbaInit init, Ref r)
        {
            // create "Adders" context - careful if it already exists 
            // register name under "Adders"
            NamingContext nsCtx = init.GetNameService(_orbInitialHost, _orbInitialPort);

            NamingContext addersCtx = null;
            try
            {
                addersCtx = nsCtx.bind_new_context(_adders);
            }
            catch (TargetInvocationException e)
            {
                // ok - context already exists
                if (e.InnerException is AlreadyBound)
                    addersCtx = (NamingContext)nsCtx.resolve(_adders);
                else
                    throw e;
            }

            NameComponent[] name = { new NameComponent(r.Name, "") };
            addersCtx.rebind(name, r.Obj);
        }



        public void Unbind(CorbaInit init, Ref r)
        {
            NamingContext nsCtx = init.GetNameService(_orbInitialHost, _orbInitialPort);
            NamingContext addersCtx = (NamingContext)nsCtx.resolve(_adders);

            NameComponent[] name = { new NameComponent(r.Name, "") };
            addersCtx.unbind(name);

            // if the adder that was just removed was the last one, remove the context, too
            if (List(init).Count == 0)
            {
                addersCtx.destroy();  // H&V 689 - destroy the context, unbind the name from the parent
                nsCtx.unbind(_adders);
            }
        }


        public List<Ref> List(CorbaInit init)
        {
            List<Ref> refList = new List<Ref>();

            NamingContext nsCtx = init.GetNameService(_orbInitialHost, _orbInitialPort);
            NamingContext addersCtx = null;
            try
            {
                addersCtx = (NamingContext)nsCtx.resolve(_adders);
            }
            catch (TargetInvocationException e)
            {
                if (e.InnerException is NotFound)
                    return refList;  // no context, so no adders
                else
                    throw e;
            }

            Binding[] bindings = null;
            BindingIterator bi = null;
            int chunkSize = 100;
            addersCtx.list(chunkSize, out bindings, out bi);
            AddBindings(bindings, addersCtx, refList);
            if (bi != null)
            {
                while (bi.next_n(chunkSize, out bindings))
                    AddBindings(bindings, addersCtx, refList);
                bi.destroy();
            }

            return refList;
        }

        void AddBindings(Binding[] bindings, NamingContext addersCtx, List<Ref> refList)
        {
            for (int i = 0; i < bindings.Length; i++)
            {
                MarshalByRefObject objRef = addersCtx.resolve(bindings[i].binding_name);
                String name = bindings[i].binding_name[bindings[i].binding_name.Length - 1].id; // last component of the name        	
                refList.Add(new Ref(name, objRef));
            }
        }
    }
}
