with Context;
with Pipe;
with Calendar;
with Directory;
with String_Utilities;
package body Connections is
    package Dir renames Directory;
    package Naming renames Directory.Naming;

    function Form_Pipe_Name (From_User : String;
                             From_Session : String;
                             To_User : String;
                             To_Session : String;
                             Kind : Mode) return String is

    begin
        case Kind is
            when Call | Listen_Call =>
                return Context & "." & From_User & "_calling_" & To_User;
            when Answer | Listen_Answer =>
                return Context & "." & From_User & "_answering_" & To_User;
        end case;
    end Form_Pipe_Name;

    function Establish (From_User : String;
                        From_Session : String;
                        To_User : String;
                        To_Session : String;
                        Kind : Mode) return Handle is

        The_Handle : Handle;

        This_Version : Dir.Version;
        Status : Naming.Name_Status;

        Pipe_Name : constant String :=
           Form_Pipe_Name (From_User, From_Session, To_User, To_Session, Kind);
    begin

        The_Handle.Kind := Kind;

        Naming.Resolve (Pipe_Name, This_Version, Status);

        case Kind is
            when Call | Answer =>
                case Status is
                    when Naming.Successful | Naming.Ambiguous =>
                        raise Already_Exists;
                    when Naming.Undefined =>
                        Pipe.Create (The_Handle.The_Pipe,
                                     Pipe.Exclusive, Pipe_Name);
                        Pipe.Close (The_Handle.The_Pipe);
                        Pipe.Open (The_Handle.The_Pipe,
                                   Pipe.Shared_Write, Pipe_Name);
                    when others =>
                        raise Not_Established;
                end case;
            when Listen_Call | Listen_Answer =>
                case Status is
                    when Naming.Successful =>
                        Pipe.Open (The_Handle.The_Pipe,
                                   Pipe.Shared_Read, This_Version);

                    when Naming.Ambiguous =>
                        raise Already_Exists;
                    when Naming.Undefined =>
                        raise Not_Available;
                    when others =>
                        raise Not_Established;
                end case;

        end case;

        return The_Handle;

    end Establish;

    procedure Break (From : in out Handle) is
    begin
        case From.Kind is
            when Call | Answer =>
                Pipe.Delete (From.The_Pipe);
            when Listen_Call | Listen_Answer =>
                Pipe.Close (From.The_Pipe);
        end case;
    exception
        when Pipe.Use_Error =>
            raise Connection_Still_In_Use;
        when Pipe.Status_Error =>
            raise Connection_Already_Released;
    end Break;

    function Convert (Connection : Handle) return Pipe.Handle is
    begin
        return Connection.The_Pipe;
    end Convert;

    function Is_Available (From_User : String;
                           From_Session : String;
                           To_User : String;
                           To_Session : String;
                           Kind : Mode) return String is

        Status : Naming.Name_Status;

        Pipe_Name : constant String :=
           Form_Pipe_Name (From_User, From_Session, To_User, To_Session, Kind);
        Possible_Callers : Naming.Iterator;
        E_Status : Dir.Error_Status;
        Obj : Dir.Object;
    begin
        Naming.Resolve (Possible_Callers, Pipe_Name, Status);

        if not Naming.Done (Possible_Callers) then
            Naming.Get_Object (Possible_Callers, Obj, E_Status);

            declare
                Name : constant String := Naming.Get_Simple_Name (Obj);
            begin
                case Kind is
                    when Call | Listen_Call =>
                        return Name (Name'First ..
                                        String_Utilities.Locate
                                           ("_calling_", Name, True) - 1);
                    when Answer | Listen_Answer =>
                        return Name (Name'First ..
                                        String_Utilities.Locate
                                           ("_answering_", Name, True) - 1);
                end case;
            end;
        else
            return "";
        end if;
    end Is_Available;
end Connections;