with Io;
with Log;
with Io_Exceptions;
package body Tape_Utils is

    Tape : Device;
    function Obtain (Mode : Io_Mode;
                     Format : String;
                     Volume : String := "";
                     To_Operator : String := "Thank you";
                     Record_Format : Tape_Tools.Record_Format :=
                        Tape_Tools.Fixed_Length;
                     Record_Length : Natural := 80;
                     Block_Length : Natural := 2000;
                     Response : Profile.Response_Profile := Profile.Get)
                    return Device is
        function Mode_Name return String is
        begin
            case Mode is
                when To_Tape =>
                    return "writing";
                when From_Tape =>
                    return "reading";
            end case;
        end Mode_Name;

    begin

        Tape := Tape_Tools.Initialize (Format);
        Log.Put_Line ("Connecting tape for " & Mode_Name,
                      Profile.Note_Msg, Response);

        case Mode is
            when To_Tape =>
                Tape_Tools.Format (Tape,
                                   Kind => Record_Format,
                                   Record_Length => Record_Length,
                                   Block_Length => Block_Length);
                Tape_Tools.Connect_For_Output
                   (Tape, Vol_Id => Volume, To_Operator => To_Operator);
            when From_Tape =>
                Tape_Tools.Connect_For_Input
                   (Tape, Vol_Id => Volume, To_Operator => To_Operator);
        end case;

        Log.Put_Line ("Tape connected", Profile.Positive_Msg, Response);
        return Tape;
    exception
        when others =>
            Log.Put_Line ("Tape not connected", Profile.Negative_Msg, Response);
            raise Tape_Error;

    end Obtain;

    procedure Release (Tape : Device;
                       Response : Profile.Response_Profile := Profile.Get) is
    begin
        begin
            Tape_Tools.Disconnect (Tape);
        exception
            -- compensate for bug in vax/vms format handling of disconnect
            when Io_Exceptions.Status_Error =>
                if Tape_Tools."/=" (Tape_Tools.Status (Tape),
                                    Tape_Tools.Other_Error) then
                    raise;
                end if;
        end;

        Log.Put_Line ("Tape disconnected", Profile.Positive_Msg, Response);
    exception
        when others =>
            Log.Put_Line ("Tape not disconnected",
                          Profile.Negative_Msg, Response);
            raise Tape_Error;
    end Release;

    procedure Write (Tape : in out Device;
                     Host_Name, Tape_Name : String;
                     Record_Format : Tape_Tools.Record_Format :=
                        Tape_Tools.Fixed_Length;
                     Record_Length : Natural := 80;
                     Response : Profile.Response_Profile := Profile.Get) is
        use Tape_Tools;
        File : Io.File_Type;
        Buffer_Length : constant Natural := 512;
        Buffer : String (1 .. Buffer_Length);
        Length : Natural := 0;
        Line_Number : Natural := 0;
    begin
        begin
            Io.Open (File => File, Name => Host_Name, Mode => Io.In_File);
        exception
            when others =>
                Log.Put_Line ("Unable to open file => " & Host_Name,
                              Profile.Negative_Msg, Response);
                return;
        end;
        Tape_Utils.Create (Tape, Tape_Name);

        while not Io.End_Of_File (File) loop
            Line_Number := Line_Number + 1;
            Io.Get_Line (File, Item => Buffer, Last => Length);
            if Record_Format = Tape_Tools.Fixed_Length then
                if Length > Record_Length then
                    Log.Put_Line ("Line Number" & Natural'Image (Line_Number) &
                                  " is too long!",
                                  Profile.Negative_Msg, Response);
                    Log.Put_Line ("This line will be broken into two lines",
                                  Profile.Negative_Msg);
                    Put_Line (Tape, Buffer (1 .. Record_Length));
                    Buffer (1 .. Length - Record_Length) :=
                       Buffer (Record_Length + 1 .. Length);
                    Buffer (Length - Record_Length + 1 .. Record_Length) :=
                       (others => ' ');
                    Put_Line (Tape, Buffer (1 .. Record_Length));
                end if;


                if Length < Record_Length then
                    Buffer (Length + 1 .. Record_Length) := (others => ' ');
                    Put_Line (Tape, Buffer (1 .. Record_Length));
                end if;
            else
                Put_Line (Tape, Buffer (1 .. Length));
            end if;

        end loop;
        Io.Close (File);
        Tape_Tools.Close (Tape);
        Log.Put_Line ("Wrote " & '"' & Host_Name & '"' &
                      " to " & '"' & Tape_Name & '"',
                      Profile.Positive_Msg, Response);
    exception
        when others =>
            Log.Put_Line ("Failed to write " & '"' & Host_Name &
                          '"' & " to " & '"' & Tape_Name & '"',
                          Profile.Negative_Msg, Response);
            raise Tape_Error;
    end Write;

    procedure Read (Tape : in out Device;
                    Host_Name : String := "";
                    Response : Profile.Response_Profile := Profile.Get) is
    begin
        Tape_Tools.Open (Tape);

        declare
            File : Io.File_Type;
            File_Id : constant String := Tape_Tools.File_Id (Tape);
            Buf_Size : constant Natural := Tape_Tools.Record_Length (Tape);
            Buffer : String (1 .. Buf_Size);
            Length : Natural;

            function Local_File_Name return String is
            begin
                if Host_Name = "" then
                    return File_Id;
                else
                    return Host_Name;
                end if;
            end Local_File_Name;
        begin
            Io.Create (File, Name => Local_File_Name);

            while not Tape_Tools.End_Of_File (Tape) loop
                Get_Line (Tape, Buffer, Length);
                Io.Put_Line (File, Buffer (1 .. Length));
            end loop;

            Io.Close (File);
            Log.Put_Line ("Read " & '"' & Local_File_Name & '"',
                          Profile.Positive_Msg, Response);
        end;

        Tape_Tools.Close (Tape);
    exception
        when others =>
            Log.Put_Line ("Failed to read file",
                          Profile.Negative_Msg, Response);
            raise Tape_Error;
    end Read;

    procedure Create (Tape : in out Device; Tape_Name : String) is
    begin
        Tape_Tools.Create (Tape, Id => Tape_Name);
    end Create;

    procedure Open (Tape : in out Device; Tape_Name : out String) is
    begin
        Tape_Tools.Open (Tape);

        declare
            File_Id : constant String := Tape_Tools.File_Id (Tape);
            Last : constant Positive := Tape_Name'First + File_Id'Length - 1;
        begin
            Tape_Name (Tape_Name'First .. Last) := File_Id;
        end;
    end Open;

    procedure Put_Line (Tape : Device; Line : String) is
    begin
        -- This is to compensate for a bug in the tape controller
        -- where odd-length records are not handled properly.
        if (Line'Length mod 2) = 0 then
            Tape_Tools.Put_Line (Tape, Line);
        else
            Tape_Tools.Put_Line (Tape, Line & ' ');
        end if;
    end Put_Line;
end Tape_Utils;