with Bounded_String;
with Definitions;
with Display_Tools;
with Fonts;
with Io;
with New_Keys;
with Single_Selection_Electric_Menus;
with System_Utilities;
with Training_Names;
with Window_Io;

package body Step_Tools is

    Maximum_Idle_Time : constant Duration := 7200.0; -- 2 hours

    package Sio renames Io;

    package Module renames Definitions.Module;
    package Step renames Definitions.Step;
    package Lines renames Definitions.Lines;

    type Step_Array is array (Positive range <>) of Lines.List;

    function "=" (K1, K2 : New_Keys.Key_Type) return Boolean
        renames Window_Io.Raw."=";

    function Is_Select_Key (Key : New_Keys.Key_Type) return Boolean is
    begin
        if Key = New_Keys.Select_Script then
            return True;
        else
            return False;
        end if;
    end Is_Select_Key;

    package Menu is new Single_Selection_Electric_Menus
                           (Definitions.Line,
                            Bounded_String.Image, Is_Select_Key);

    function Get_Steps (Title : Definitions.Line) return Step_Array is

        Module_Iter : Module.Iterator;
        Step_Iter : Step.Iterator;
        This_Module : Step.List;
    begin
        Module.Init (Module_Iter, Definitions.All_Modules);

        while not Module.Done (Module_Iter) loop
            if Bounded_String.Image (Module.Value (Module_Iter).Name) =
               Bounded_String.Image (Title) then
                This_Module := Module.Value (Module_Iter).Module;
                Step.Init (Step_Iter, This_Module);

                declare
                    All_Steps : Step_Array (1 .. Step.Length (This_Module));
                    Current_Step : Positive := 1;
                begin
                    while not Step.Done (Step_Iter) loop
                        All_Steps (Current_Step) := Step.Value (Step_Iter);
                        Current_Step := Current_Step + 1;
                        Step.Next (Step_Iter);
                    end loop;

                    return All_Steps;
                end;
            end if;

            Module.Next (Module_Iter);

        end loop;

    end Get_Steps;

    function Build_Elided
                (Full_Line : String; Full_Elision : Boolean) return String is
        Count : Natural := 0;
    begin
        if Full_Elision then
            for C in Full_Line'Range loop
                if Full_Line (C) = ' ' then
                    Count := Count + 1;
                end if;  
                if Count = 5 then
                    return Full_Line (Full_Line'First .. C) & "...";
                end if;
            end loop;
            return Full_Line & "...";
        else
            return Full_Line & "...";
        end if;
    end Build_Elided;

    procedure Display_Lines (The_Lines : Lines.List;
                             Ellided : Boolean := True;
                             Full_Elision : Boolean := False) is
        Line_Iter : Lines.Iterator;
    begin
        Lines.Init (Line_Iter, The_Lines);

        while not Lines.Done (Line_Iter) loop
            if Ellided then
                Window_Io.Insert
                   (Display_Tools.Output_Window,
                    Build_Elided (Bounded_String.Image
                                     (Lines.Value (Line_Iter)), Full_Elision),
                    Image => Fonts.Normal,
                    Kind => Window_Io.Protected);
                Window_Io.New_Line (Display_Tools.Output_Window, 1);
                exit;
            else
                Window_Io.Insert (Display_Tools.Output_Window,
                                  Bounded_String.Image
                                     (Lines.Value (Line_Iter)),
                                  Image => Fonts.Bold,
                                  Kind => Window_Io.Protected);
                Window_Io.New_Line (Display_Tools.Output_Window, 1);
            end if;

            Lines.Next (Line_Iter);
        end loop;

        Window_Io.New_Line (Display_Tools.Output_Window, 1);
    end Display_Lines;

    task body Display_Manager is
        Module_Title : Definitions.Line;
        Def : Menu.Menu_Definition;
        Module_Iter : Module.Iterator;
        Quit_Line : Definitions.Line;
        Quit : constant String := "Quit";
    begin
        Bounded_String.Copy (Quit_Line, Quit);

        select
            accept Start (Course_Name : String; Release_Name : String) do

                Definitions.Initialize
                   (Training_Names.Script_Text_File_Pathname (Course_Name));

                Module.Init (Module_Iter, Definitions.All_Modules);

                while not Module.Done (Module_Iter) loop
                    Menu.Add (Module.Value (Module_Iter).Name, Def);
                    Module.Next (Module_Iter);
                end loop;

                Menu.Add (Quit_Line, Def);

--                Display_Tools.Open_Output;
                Display_Tools.Erase;
                Window_Io.Position_Cursor (Display_Tools.Output_Window, 1, 10);
                Window_Io.Insert (Display_Tools.Output_Window,
                                  "Available Scripts:");

                begin
                    Module_Title := Menu.Get_User_Selection
                                       (Display_Tools.Output_Window, Def, 15, 1,
                                        Max_Wait => Maximum_Idle_Time);
                exception
                    when Menu.Time_Out =>
                        raise Time_Out;
                end;
                Display_Tools.Label_Window
                   (Bounded_String.Image (Module_Title));

--                Display_Tools.Close_Output;

                if Bounded_String.Image (Module_Title) = Quit then
                    raise Terminate_Script;
                end if;
            end Start;
        or
            accept Stop do
                Module_Title := Quit_Line;
            end Stop;
        end select;

        if Bounded_String.Image (Module_Title) /= Quit then

            Outer:
                loop
--                    Display_Tools.Open_Output;
                    Display_Tools.Erase;

                    declare
                        All_Steps : constant Step_Array :=
                           Get_Steps (Module_Title);
                        Current_Step : Natural := 1;
                        Redraw : Boolean := False;
                    begin
--                        Display_Tools.Close_Output;

                        Inner:
                            loop
--                                Display_Tools.Open_Output;
                                Display_Tools.Erase;
                                if Current_Step = All_Steps'First then
                                    Display_Lines (All_Steps (Current_Step),
                                                   Ellided => False);
                                else
                                    Display_Lines (All_Steps
                                                      (Current_Step - 1));
                                    Display_Lines (All_Steps (Current_Step),
                                                   Ellided => False);
                                    if Current_Step /= All_Steps'Last then
                                        Display_Lines (All_Steps
                                                          (Current_Step + 1),
                                                       Full_Elision => True);
                                    end if;
                                end if;
                                --                                Display_Tools.Close_Output;

                                loop
                                    select
                                        accept Select_Module do
--                                            Display_Tools.Open_Output;

                                            Display_Tools.Erase;

                                            Window_Io.Position_Cursor
                                               (Display_Tools.Output_Window,
                                                1, 10);
                                            Window_Io.Insert
                                               (Display_Tools.Output_Window,
                                                "Available Scripts:");

                                            begin
                Module_Title := Menu.Get_User_Selection
                                   (Display_Tools.Output_Window, Def, 15, 1,
                                    Max_Wait => Maximum_Idle_Time);
                                            exception
                when Menu.Time_Out =>
                    raise Time_Out;
                                            end;
                                            Display_Tools.Label_Window
                                               (Bounded_String.Image
                                                   (Module_Title));
--                                            Display_Tools.Close_Output;

                                            if Bounded_String.Image
                                                  (Module_Title) = Quit then
                raise Terminate_Script;
                                            end if;

                                            Current_Step := 1;
                                        end Select_Module;

                                        if Bounded_String.Image (Module_Title) =
                                           Quit then
                                            exit Outer;
                                        else
                                            exit Inner;
                                        end if;
                                    or
                                        accept Stop;

                                        exit Outer;
                                    or
                                        accept Step (Number_Of_Steps :
                                                        Integer := 1) do
                                            if Current_Step + Number_Of_Steps <
                                               1 then
                Window_Io.Bell (Display_Tools.Output_Window);
                Sio.Put_Line (Sio.Standard_Error,
                              "Cannot move before the first step");

                                            elsif Current_Step +
                                                  Number_Of_Steps >
                                                  All_Steps'Last then
                Window_Io.Bell (Display_Tools.Output_Window);
                Sio.Put_Line (Sio.Standard_Error,
                              "Cannot move after the last step");
                                            else
                Current_Step := Current_Step + Number_Of_Steps;
                Redraw := True;
                                            end if;
                                        end Step;
                                    or
                                        accept Locate_Step
                                                  (Step_Number : Positive) do
                                            if Step_Number > All_Steps'Last then
                Window_Io.Bell (Display_Tools.Output_Window);
                Sio.Put_Line (Sio.Standard_Error,
                              "Cannot move after the last step");
                                            else
                Current_Step := Step_Number;
                Redraw := True;
                                            end if;
                                        end Locate_Step;
                                    end select;

                                    if Redraw then
                                        Redraw := False;
                                        exit;
                                    end if;
                                end loop;
                            end loop Inner;
                    end;
                end loop Outer;
        end if;
    exception
        when Training_Names.Bad_Script_Text_File_Name =>
            Sio.Put_Line (Sio.Standard_Error,
                          "Could not find the script text file for a " &
                             System_Utilities.Terminal_Type & " terminal.");
            Step_Tools.Display_Manager.Stop;

    end Display_Manager;
end Step_Tools;
