with Terminal_Interface, Text_Io, Run_Time_Statistics, Corrector;

package body Process_Package is

    No_More_Words : exception renames Document_Handler.No_More_Words;
    Abandon : exception;
    Invalid_User_Dictionary_Request : exception;

    function "=" (Left, Right : Get_User_Info.Mode_Type) return Boolean
        renames Get_User_Info."=";

    package Rts renames Run_Time_Statistics;



    --****************************************************************
    --****************************************************************


    --------------------------------------------------------------------------
    -- Abstract   : This operation will sumerize the run time statics
    --            : collected during the operation of the tool.
    --            : The statistics collected are :
    --            :          number of words corrected
    --            :          number of suspect words
    --            :          number of restore request which failed
    --            :          number of words which were replaced with
    --            :              of a different length.
    --------------------------------------------------------------------------
    -- Parameters : FILE is the suspect word list provided by the user.
    --            : If the corrector were activated then the file is invalid.
    --            : The statistics will be reported to the screen.
    --------------------------------------------------------------------------
    -- Algorithm  :
    --            :
    --------------------------------------------------------------------------

    procedure Wrapup (User_Info : Get_User_Info.User_Info_Type) is

    begin
        if User_Info.Mode = Get_User_Info.Disabled then
            Text_Io.Put_Line ("There were " &
                              Natural'Image (Run_Time_Statistics.Count
                                                (Run_Time_Statistics.
                                                 Suspect_Words)) &
                              " suspect words detected.");
        else

            Terminal_Interface.New_Page;

            Terminal_Interface.New_Line (2);

            Terminal_Interface.Put_Line
               ("There were " & Natural'Image
                                   (Run_Time_Statistics.Count
                                       (Run_Time_Statistics.Corrected_Words)) &
                " words corrected.");

            Terminal_Interface.New_Line (2);

            Terminal_Interface.Put_Line
               ("There were " & Natural'Image
                                   (Run_Time_Statistics.Count
                                       (Run_Time_Statistics.Suspect_Words)) &
                " suspect words detected.");

            Terminal_Interface.New_Line (2);

            Terminal_Interface.Put_Line
               ("There were " & Natural'Image
                                   (Run_Time_Statistics.Count
                                       (Run_Time_Statistics.Failed_Restores)) &
                " request(s) to restore words which were unsuccessful");

            if Run_Time_Statistics.Count
                  (Run_Time_Statistics.Words_Changing_Length) > 0 then

                Terminal_Interface.New_Line (2);

                Terminal_Interface.Put_Line
                   ("There were " & Natural'Image (Run_Time_Statistics.Count
                                                      (Run_Time_Statistics.
                                                       Words_Changing_Length)) &
                    " words which changed length.");

                Terminal_Interface.Put_Line ("Reformat.");

            end if;
        end if;
    end Wrapup;

    --****************************************************************
    --****************************************************************


    --------------------------------------------------------------------------
    -- Abstract   : This operation will initialize the processing
    --            : information contained in the USER_INFO record.
    --            : All dictionaries will be ENABLED or DISABLED as
    --            : necessary.  All options will be identified and
    --            : the appropriate steps taken to to insure proper
    --            : processing.
    --------------------------------------------------------------------------
    -- Parameters : USER_INFO : GET_USER_INFO.USER_INFO_TYPE
    --            : This parameter is a record containg all of the
    --            : processing supplied by the user.
    --            : FILE the file of the suspect word list.
    --------------------------------------------------------------------------
    -- Algorithm  :
    --            :
    --------------------------------------------------------------------------
    procedure Initialize (User_Info : in out Get_User_Info.User_Info_Type;
                          File : in out Text_Io.File_Type) is

        Dictionary_Ptr : Dictionary_Manager.Dictionary_Ptr;
        Response : String (Get_User_Info.Name_Range);
        Length : Natural;

    begin
        begin

            if Text_Io.Is_Open (File) then
                Text_Io.Close (File);
            end if;

            Text_Io.Create (File, Text_Io.Out_File,
                            User_Info.Word_List.Name
                               (User_Info.Word_List.Name'First ..
                                   User_Info.Word_List.Length));

        exception
            when others =>
                Text_Io.Put_Line (Text_Io.Standard_Output,
                                  "Error in filename <<" &
                                     User_Info.Word_List.Name
                                        (User_Info.Word_List.Name'First ..
                                            User_Info.Word_List.Length) & ">>");

                Text_Io.Put_Line (Text_Io.Standard_Output,
                                  "The file is invalid.  " &
                                     "This operation is terminated.");

                raise Abandon;
        end;


        begin

            Document_Handler.Open_Source
               (User_Info.Document.Name (User_Info.Document.Name'First ..
                                            User_Info.Document.Length));
        exception
            when others =>
                if Text_Io.Is_Open (File) then
                    Text_Io.Put_Line
                       (File, "Document <<" &
                                 User_Info.Document.Name
                                    (User_Info.Document.Name'First ..
                                        User_Info.Document.Length) &
                                 ">> is invalid.");

                    Text_Io.Put_Line (File, "This operation is terminated.");

                    Text_Io.Close (File);

                    if User_Info.Mode = Get_User_Info.Enabled then
                        Terminal_Interface.Put_Line
                           ("Document <<" & User_Info.Document.Name
                                               (User_Info.Document.Name'First ..
                                                   User_Info.Document.Length) &
                            ">> is invalid.");
                        Terminal_Interface.Put_Line
                           ("This operation is terminated.");
                        Terminal_Interface.Put_Line
                           ("Hit return key to acknowledge.");
                        Terminal_Interface.Get_Line (Response, Length);
                    end if;

                    raise Abandon;



                else
                    Text_Io.Put_Line
                       (Text_Io.Standard_Output,

                        "Document <<" & User_Info.Document.Name
                                           (User_Info.Document.Name'First ..
                                               User_Info.Document.Length) &
                           ">> is invalid.");

                    Text_Io.Put_Line (Text_Io.Standard_Output,
                                      "This operations is terminated.");

                    if User_Info.Mode = Get_User_Info.Enabled then

                        Terminal_Interface.Put_Line
                           ("Hit any key to acknowledge");

                        Terminal_Interface.Get_Line (Response, Length);
                    end if;
                    raise Abandon;
                end if;
        end;

        if User_Info.Mode = Get_User_Info.Enabled then
            begin

                Document_Handler.Open_Destination
                   (User_Info.Corrected_Doc.Name
                       (User_Info.Corrected_Doc.Name'First ..
                           User_Info.Corrected_Doc.Length));
            exception
                when others =>
                    Document_Handler.Close_Source_And_Destination;

                    if Text_Io.Is_Open (File) then
                        Text_Io.Close (File);
                    end if;

                    Terminal_Interface.Put_Line
                       ("Document <<" &
                        User_Info.Corrected_Doc.Name
                           (User_Info.Corrected_Doc.Name'First ..
                               User_Info.Corrected_Doc.Length) &
                        ">> is invalid.");

                    Terminal_Interface.Put_Line
                       ("This operation is terminated.");

                    Terminal_Interface.Put_Line ("Hit any key to acknowledge");

                    Terminal_Interface.Get_Line (Response, Length);

                    raise Abandon;
            end;

        end if;

        if User_Info.Master then

            begin

                Dictionary_Manager.Enable_Dictionary
                   (Dictionary_Manager.Get_Master_Dictionary);
            exception
                when Dictionary_Manager.Dictionary_Error =>

                    begin

                        Dictionary_Manager.Create_Dictionary
                           (Dictionary_Manager.Master, Dictionary_Ptr,
                            Machine_Dependencies.Master_Dictionary);

                    exception
                        when Storage_Error =>
                            Text_Io.Put_Line (Text_Io.Standard_Output,
                                              "Error in master creation");

                            Text_Io.Put_Line (Text_Io.Standard_Output,
                                              "Hit any key to acknowledge");
                            Text_Io.Get_Line (Response, Length);
                        when others =>
                            null;
                    end;

                when others =>
                    Text_Io.Put_Line (Text_Io.Standard_Output,
                                      "unknown exception on Master Enable");
                    Text_Io.Put_Line (Text_Io.Standard_Output,
                                      "Hit any key to acknowledge");
                    Text_Io.Get_Line (Response, Length);
            end;


        else
            begin

                Dictionary_Manager.Disable
                   (Dictionary_Manager.Get_Master_Dictionary);
            exception
                when others =>
                    null;
            end;
        end if;

        if User_Info.Acronym then

            begin

                Dictionary_Manager.Enable_Dictionary
                   (Dictionary_Manager.Get_Acronym_Dictionary);

            exception
                when Dictionary_Manager.Dictionary_Error =>
                    begin

                        Dictionary_Manager.Create_Dictionary
                           (Dictionary_Manager.Acronym, Dictionary_Ptr,
                            Machine_Dependencies.Acronym_Dictionary);

                    exception
                        when Storage_Error =>

                            Text_Io.Put_Line (Text_Io.Standard_Output,
                                              "Error in acronym creation");
                            Text_Io.Put_Line (Text_Io.Standard_Output,
                                              "Hit any key to acknowledge");
                            Text_Io.Get_Line (Response, Length);

                        when others =>
                            null;

                    end;
                when others =>
                    Text_Io.Put_Line (Text_Io.Standard_Output,
                                      "Unknown error on Acronym Enable");
            end;
        else
            begin

                Dictionary_Manager.Disable
                   (Dictionary_Manager.Get_Acronym_Dictionary);
            exception
                when others =>
                    null;

            end;
        end if;

        ------------------------------------------------------------------
        --    process the user dictionaries
        ------------------------------------------------------------------

        for I in 1 .. Get_User_Info.Number_Of_User_Dictionaries loop

            if User_Info.User_Dict.Names (I).Mode = Get_User_Info.Enabled then

                begin
                    Dictionary_Manager.Enable_Dictionary
                       (User_Info.User_Dict.Names (I).Ptr);
                exception
                    when Dictionary_Manager.Dictionary_Error =>
                        begin
                            Dictionary_Manager.Create_Dictionary
                               (Dictionary_Manager.User,
                                User_Info.User_Dict.Names (I).Ptr,
                                User_Info.User_Dict.Names (I).Name
                                   (User_Info.User_Dict.Names (I).Name'First ..
                                       User_Info.User_Dict.Names (I).Length));

                        exception
                            when Storage_Error =>
                                Text_Io.Put_Line
                                   (Text_Io.Standard_Output,
                                    "Cannot create user dictionary." &
                                       "  Not enough storage.");

                                Text_Io.Put_Line (Text_Io.Standard_Output,
                                                  "Hit any key to acknowledge");
                                Text_Io.Get_Line (Response, Length);
                            when others =>
                                User_Info.User_Dict.Names (I).Mode :=
                                   Get_User_Info.Disabled;
                        end;
                    when others =>
                        raise Invalid_User_Dictionary_Request;
                end;

            else
                begin

                    Dictionary_Manager.Disable
                       (User_Info.User_Dict.Names (I).Ptr);
                exception
                    when Dictionary_Manager.Dictionary_Error =>
                        null;
                end;

            end if;
        end loop;

    end Initialize;


    --****************************************************************
    --****************************************************************


    ----------------------------------------------------------
    -- Abstract   : This operation will change the case of the first letter
    --            : of a word.
    --------------------------------------------------------------------------
    -- Parameters : WORD : TOKEN_DEFINITION.TOKEN_TYPE;
    --            : This parameter is a record containg a word (string) and
    --            : the length of a word.
    --------------------------------------------------------------------------
    -- Algorithm  : If the first character is in the uppercase range change
    --            : it to lower case and return the word.  Otherwise change
    --            : it to uppercase.  All of this only applies to word whose
    --            : first character is in the alpha range.
    --------------------------------------------------------------------------

    procedure Change_Case (Word : in out Token_Definition.Token_Type) is

        subtype Upper_Case_Range is
           Integer range Character'Pos ('A') .. Character'Pos ('Z');

        Case_Constant : constant Natural :=
           Character'Pos ('a') - Character'Pos ('A');

    begin
        if Character'Pos (Word.Word (Word.Word'First)) in
           Upper_Case_Range'First .. Upper_Case_Range'Last or else
           Character'Pos (Word.Word (Word.Word'First)) - Case_Constant in
              Upper_Case_Range'First .. Upper_Case_Range'Last then

            if Character'Pos (Word.Word (Word.Word'First)) in
               Upper_Case_Range'First .. Upper_Case_Range'Last then
                Word.Word (Word.Word'First) :=
                   Character'Val (Character'Pos (Word.Word (Word.Word'First)) +
                                  Case_Constant);
            else
                Word.Word (Word.Word'First) :=
                   Character'Val (Character'Pos (Word.Word (Word.Word'First)) -
                                  Case_Constant);
            end if;
        end if;
    end Change_Case;


    --****************************************************************
    --****************************************************************


    --------------------------------------------------------------------------
    -- Abstract   : This operation will check variant forms of a word for
    --            : presence in a dictionary.  If the word ends in a
    --            : period or an apostrophe then strip it off.  Search the
    --            : dictionaries for the changed word.  If found then
    --            : all is well.  If not found then change the case
    --            : and search again.  If still not found then report
    --            : that the word is most likely spelled incorrectly.
    --------------------------------------------------------------------------
    -- Parameters : WORD : TOKEN_DEFINITION.TOKEN_TYPE;
    --            : is a record containg the fields WORD : which
    --            : a string and LENGTH which indicates the number
    --            : of characters in the WORD.
    --------------------------------------------------------------------------
    -- Algorithm  :
    --
    --------------------------------------------------------------------------

    function Found_Other_Form
                (Word : Token_Definition.Token_Type) return Boolean is

        Token : Token_Definition.Token_Type := Word;
        Period : constant Character := '.';
        Apostrophe : constant Character := ''';
        Dictionary_Ptr : Dictionary_Manager.Dictionary_Ptr;
        Found : Boolean := False;

    begin

        if Token.Word (Token.Length) = Period or else
           Token.Word (Token.Length) = Apostrophe then

            Token.Word (Token.Word'First .. Token.Length - 1) :=
               Token.Word (Token.Word'First .. Token.Length - 1);

            Token.Length := Token.Length - 1;

            if Token.Length >= 1 then
                Dictionary_Manager.Token_Is_Found
                   (Dictionary_Ptr, Token, Found);
                if Found then
                    null;
                else
                    Change_Case (Token);
                    Dictionary_Manager.Token_Is_Found
                       (Dictionary_Ptr, Token, Found);
                end if;
            end if;
        else
            Change_Case (Token);
            Dictionary_Manager.Token_Is_Found (Dictionary_Ptr, Token, Found);
        end if;
        return Found;
    end Found_Other_Form;



    --****************************************************************
    --****************************************************************


    --------------------------------------------------------------------------
    -- Abstract   : This operation will check a document for misspelled
    --            : words.
    --            : If any are found it will cause them to be list to a file
    --            : designated by the user.
    --------------------------------------------------------------------------
    -- Parameters : USER_INFO : GET_USER_INFO.USER_INFO_TYPE;
    --            : This record contains the processing information supplied
    --            : by the user.
    --------------------------------------------------------------------------
    -- Algorithm  :
    --
    --------------------------------------------------------------------------


    procedure Batch_Process (User_Info : in out Get_User_Info.User_Info_Type) is

        In_Bounds : Boolean;
        Found : Boolean;
        Word : Token_Definition.Token_Type;
        Dictionary_Ptr : Dictionary_Manager.Dictionary_Ptr;
        File : Text_Io.File_Type;


    begin

        Initialize (User_Info, File);

        Rts.Initialize_Counters;

        Misspelled_Word_List.Initialize;

        loop
            begin
                Document_Handler.Get_Word (Word, In_Bounds);

                if In_Bounds and then Word.Length > 1 then

                    Dictionary_Manager.Token_Is_Found
                       (Dictionary_Ptr, Word, Found);

                    if Found then
                        null;
                    elsif Found_Other_Form (Word) then
                        null;
                    else
                        Rts.Increment_Counter (Rts.Suspect_Words);
                        Misspelled_Word_List.Add_Token (Word);
                    end if;
                elsif Word.Length < 2 and
                      (Word.Word (Word.Length) = Upper_Case_A or
                       Word.Word (Word.Length) = Upper_Case_I or
                       Word.Word (Word.Length) = Lower_Case_A or
                       Word.Word (Word.Length) = Lower_Case_I) then
                    null;
                else
                    Rts.Increment_Counter (Rts.Suspect_Words);
                    Misspelled_Word_List.Add_Token (Word);
                end if;
            exception
                when Dictionary_Manager.Bad_Word =>
                    Rts.Increment_Counter (Rts.Suspect_Words);
                    Misspelled_Word_List.Add_Token (Word);
            end;
        end loop;
    exception
        when No_More_Words =>
            Document_Handler.Close_Source_And_Destination;
            Wrapup (User_Info);
            Misspelled_Word_List.Print (File);
            Text_Io.Close (File);

            for I in 1 .. Get_User_Info.Number_Of_User_Dictionaries loop
                begin
                    Dictionary_Manager.Delete_Dictionary
                       (User_Info.User_Dict.Names (I).Ptr);
                exception
                    when Dictionary_Manager.Dictionary_Error =>
                        null;
                end;
            end loop;

        when Abandon =>
            if Text_Io.Is_Open (File) then
                Text_Io.Close (File);
            else
                null;
            end if;
        when others =>
            if Text_Io.Is_Open (File) then
                Text_Io.Put_Line (File, "Unrecoverable error in processing");
                Text_Io.Put_Line (File,
                                  "Check your processing options and retry.");
                Text_Io.Close (File);
            else
                Text_Io.Put_Line (Text_Io.Standard_Output,
                                  "Unrecoverable error in processing.");
                Text_Io.Put_Line (Text_Io.Standard_Output,
                                  "Check your processing options and retry.");
            end if;

    end Batch_Process;


    --****************************************************************
    --****************************************************************

    --------------------------------------------------------------------------
    -- Abstract   : This operation will check a document for misspelled
    --            : words.  If any are found it will cause the context of
    --            : the word to be displayed to the user.  It will then be
    --            : up to the user to determine what the next step will be.
    --            : The user will be given a number choices from which to
    --            : choose.
    --------------------------------------------------------------------------
    -- Parameters : USER_INFO : GET_USER_INFO.USER_INFO_TYPE;
    --            : This record contains the processing information supplied
    --            : by the user.
    --------------------------------------------------------------------------
    -- Algorithm  :
    --
    --------------------------------------------------------------------------


    procedure Interactive_Process (User_Info : in out
                                      Get_User_Info.User_Info_Type) is

        In_Bounds : Boolean;
        Found : Boolean;
        Completed : Boolean;
        Word : Token_Definition.Token_Type;
        Dictionary_Ptr : Dictionary_Manager.Dictionary_Ptr;
        File : Text_Io.File_Type;
        Response : String (1 .. Token_Definition.Token_Length);
        Length : Natural;


    begin
        Initialize (User_Info, File);

        Rts.Initialize_Counters;

        Corrector.Initialize;

        Misspelled_Word_List.Initialize;

        loop
            begin
                Document_Handler.Get_Word (Word, In_Bounds);

                if In_Bounds and then Word.Length > 1 then

                    Dictionary_Manager.Token_Is_Found
                       (Dictionary_Ptr, Word, Found);

                    if Found then
                        null;
                    elsif Found_Other_Form (Word) then
                        null;
                    else

                        if Corrector.Was_Corrected (Word) then
                            null;
                        else
                            Misspelled_Word_List.Add_Token (Word);
                            Rts.Increment_Counter (Rts.Suspect_Words);
                            Corrector.Correct (User_Info, Word);

                        end if;
                    end if;
                elsif Word.Length < 2 and
                      (Word.Word (Word.Length) = Upper_Case_A or
                       Word.Word (Word.Length) = Upper_Case_I or
                       Word.Word (Word.Length) = Lower_Case_A or
                       Word.Word (Word.Length) = Lower_Case_I) then
                    null;
                else

                    if Corrector.Was_Corrected (Word) then
                        null;
                    else
                        Rts.Increment_Counter (Rts.Suspect_Words);
                        Misspelled_Word_List.Add_Token (Word);
                        Corrector.Correct (User_Info, Word);
                    end if;

                end if;
            exception
                when Dictionary_Manager.Bad_Word =>
                    Rts.Increment_Counter (Rts.Suspect_Words);
                    Misspelled_Word_List.Add_Token (Word);
                    Corrector.Bad_Word_Flag := True;
                    Corrector.Correct (User_Info, Word);
            end;
        end loop;
    exception
        when No_More_Words =>
            Document_Handler.Close_Source_And_Destination;

            for I in 1 .. Get_User_Info.Number_Of_User_Dictionaries loop
                begin
                    if Dictionary_Manager.Alter
                          (User_Info.User_Dict.Names (I).Ptr) then
                        Dictionary_Manager.List_Dictionary
                           (User_Info.User_Dict.Names (I).Ptr,
                            User_Info.User_Dict.Names (I).Name
                               (User_Info.User_Dict.Names (I).Name'First ..
                                   User_Info.User_Dict.Names (I).Length));
                    end if;

                    Dictionary_Manager.Delete_Dictionary
                       (User_Info.User_Dict.Names (I).Ptr);
                exception
                    when others =>
                        null;
                end;
            end loop;

            Misspelled_Word_List.Print (File);
            Text_Io.Close (File);
            Wrapup (User_Info);
            Terminal_Interface.Put_Line ("Hit return key to continue");
            Terminal_Interface.Get_Line (Response, Length);

        when Abandon =>
            if Text_Io.Is_Open (File) then
                Text_Io.Close (File);
            end if;

        when Corrector.Abandon_Operation =>
            Document_Handler.Close_Source_And_Destination;

            if Text_Io.Is_Open (File) then
                Text_Io.Close (File);
            end if;

            for I in 1 .. Get_User_Info.Number_Of_User_Dictionaries loop
                begin
                    Dictionary_Manager.Delete_Dictionary
                       (User_Info.User_Dict.Names (I).Ptr);
                exception
                    when others =>
                        null;

                end;
            end loop;

        when Invalid_User_Dictionary_Request =>
            Text_Io.Put_Line (Text_Io.Standard_Output,
                              "Invalid user dictionary request");
            Text_Io.Put_Line (Text_Io.Standard_Output,
                              "Hit return key to acknowledge");
            Text_Io.Get_Line (Response, Length);
        when others =>
            Text_Io.Put_Line (Text_Io.Standard_Output,
                              "Unrecoverable error in processing");

            Text_Io.Put_Line (Text_Io.Standard_Output,
                              "Check your processing options and retry.");

            Text_Io.Put_Line (Text_Io.Standard_Output,
                              "Hit return key to acknowledge");
            Text_Io.Get_Line (Response, Length);

            begin
                Document_Handler.Close_Source_And_Destination;
            exception
                when others =>
                    null;
            end;

    end Interactive_Process;

    --****************************************************************
    --****************************************************************

end Process_Package;