with ada.text_io; use ada.text_io;
with ada.integer_text_io; use ada.integer_text_io;
with ada.exceptions;
use ada.exceptions;

-- When using gnat, requires -gnato option to enable overflow checks
procedure table4 is
    -- Precondition: sPow, fPow in 1 .. 99
    procedure putHeader(sPow, fPow: Integer) is
    begin
        -- First column
        put("          I");

        -- Columns for powers
        for i in sPow .. fPow loop
            if i in 1 .. 9 then 
                put("       I**");
            else
                put("      I**");
            end if;

            put(i, 1);
        end loop;
        new_line;

        -- Add one to fPow to account for column for I
        for i in sPow .. fPow + 1 loop
            put("-----------");
        end loop;
        new_line;
    end putHeader;

    procedure putRows(sInt, fInt, sPow, fPow: Integer) is
        tempInt: Integer;
        overflow: Boolean;
    begin
        for i in sInt .. fInt loop
            put(i);
            for j in sPow .. fPow loop
                overflow := false;
                begin
                    tempInt := i ** j;
                exception
                    when Constraint_Error => 
                        overflow := true;
                end;
                if not overflow then 
                    put(tempInt);
                else
                    put("   ********");
                end if;
            end loop;
            new_line;
        end loop;
    end putRows;

    procedure getOneItem(
        item: out Integer; 
        prompt: String; 
        lowerBound: Integer; 
        errorMsg: String) is
    begin
        loop
            begin
                put(prompt);
                get(item);

                exit when item > lowerBound;
                put_line(errorMsg);
            exception
                when Data_Error =>
                    new_line;
                    put_line("Must enter a valid integer value.");

                    -- Discard anything left on input line
                    skip_line;

                when e: others =>
                    new_line;
                    put_line("Unknown exception occurred.");
                    put_line("Bailing out!");
                    raise Program_Error with exception_name(e);
                    -- exit;
            end;
        end loop;

    end getOneItem;

    procedure getData(sI, fI, sP, fP: out Integer) is
    begin
        getOneItem(sI, "Enter starting integer", 
                    -1, "Integer must be non-negative");

        getOneItem(fI, "Enter ending integer", 
                    sI, "Ending integer must be larger than starting");

        getOneItem(sP, "Enter starting power", 
                    -1, "Power must be non-negative");

        getOneItem(fP, "Enter ending power", 
                    sP, "Ending power must be larger than starting");
    end getData;
        

    startI, finishI: Integer;
    startPower, finishPower: Integer;

begin
    getData(StartI, finishI, startPower, finishPower);

    new_line;

    putHeader(startPower, finishPower);
    
    putRows(startI, finishI, startPower, finishPower);

end table4;