-- This program introduces basic features of Ada:
--    Standard types, i/o, operators, strong typing, control structures
with ada.text_io; use ada.text_io;   -- IO of characters and strings
with ada.integer_text_io; use ada.integer_text_io;  -- IO of integers

-- Case insensitive:
with ada.float_text_io; use ADA.FLOAT_TEXT_IO;  -- IO of floats
    
procedure intro_all is
    -- All of these types are automatically available from package Standard
    -- SIMPLE NUMERIC TYPES - Unitialized
    i, j, k, int_sum, int_avg: Integer;
    f, g, flt_sum, flt_avg: Float;

    -- NUMERIC SUBTYPES - Initialized
    m, n: Natural := 1;   -- 0 .. Integer'last.  Same operations as integer.
    p, q: Positive := 2;   -- 1 .. Integer'last.  Same operations as integer.

    -- CONSTANT (of a specified type)
    c: constant Integer := 3; 

    -- NAMED NUMBERS - no type!
    pi: constant := 3.14159_26535_89793_23846_26433_83279_50288_41971_69399_37510;
    two_pi: constant := 2 * pi;

    one_third: constant := 1 / 3;    -- Really 1/3!
    one: constant := 3 * one_third;  -- Really one!

    -- TYPE BOOLEAN: true or false
    less: Boolean;

    -- ENUMERATED Type
    type Color is (red, blue, green);
    my_car_color: Color := red;

    type Days is (Sun, Mon, Tues, Wed, Thu, Fri, Sat);
    today, tomorrow: Days := Fri;

    package Days_IO is new Ada.Text_IO.Enumeration_IO(Days);
    use Days_IO;

    -- Type for a single 8-bit CHARACTER
    a_char: Character := 's';

    -- FIXED LENGTH STRINGS: A String is an array of Characters
    -- Include lengths in name for later reference
    s3: String := "bat";   -- initialized, size 3
    s5: String(1 .. 5);    -- not initialized
    s6: String(1 .. 6);
    s8: String(1 .. 8);
    s10: String(1 .. 10);
    s12: String(1 .. 12);
    -- UNBOUNDED LENGTH STRINGS: see package Ada.Strings.Unbounded
begin
    -- NUMERIC, STRING, and CHARACTER INPUT:
    --   values from Standard Input are returned in the parameter!
    --   numeric skips white space, looking for a number
    --   character and string skip newline but not spaces
    put("Enter an int, a nat, a string, a float, a string, a float and a character: ");
    get(i);             -- next integer, skips white space
    get(j);             -- next integer, skips white space
    get(s5);            -- 5 characters, skips end of line
    get(f);             -- next float, skips white space
    get(s6);            -- 6 characters, skips end of line
    get(g);             -- next float, skips white space
    get(a_char);        -- 1 character, skips end of line

    -- Strings are arrays of characters:
    s3(2) := 'i';  -- s3 is now "bit".  Index starts at 1.

    -- FIXED LENGTH STRING OPERATIONS: String lengths must match
    s8 := s3 & s5;
    s10 := s3 & s6 & a_char;
    s12 := s3 & s6 & a_char & "!!";
    put_line(s8);
    put_line(s10);
    put_line(s12);

    -- ARITHMETIC OPERATIONS
    k := i + j;
    k := i - j;
    k := i * j;
    k := i / j;        -- truncates
    k := 2 ** 10;      -- 1024

    k := 13 mod 10;    --    3  -- Mod and rem identical for positives
    k := 13 rem 10;    --    3
    k := (-2) mod 12;  --   10  Useful for clock arithmetic
    k := (-2) rem 12;  --   -2  Like Java % operator

    put("AREA: "); put(pi * f ** 2); new_line;

    -- STRONG TYPING: ASSIGNMENT, EXPRESSIONS, PUT
    -- Cannot mix types!  These are all okay.
    flt_sum := f + g;
    int_sum := i + j;

    int_avg := int_sum / 2;

    flt_avg := flt_sum / 2.0;
    flt_avg := float(int_sum) / 2.0;   -- TYPE CONVERSION
    flt_avg := float(int_sum / 2);

    -- TYPE CHECKING FOR SUBTYPES
    get(n);
    i := n;   -- Can mix parent types and subtypes.  Where can errors occur?
    get(i);
    n := i;
    p := i;
    p := n;
    n := p;
    
    -- OUTPUT INTEGERS and FLOATS - No put_line for numeric
    put(i); new_line;         -- Default width: 11
    put(i, 4); new_line;      -- Minimum width 4
    -- KEYWORD PARAMETERS
    put(item => i, width => 4); new_line;

    put(f, 4, 5, 0); new_line;       -- Put is OVERLOADED
    put(item => f, fore => 4, aft => 5, exp => 0); new_line;

    -- 'IMG AND TYPE'IMAGE CONVERT TO A STRING, LIKE to_string
    put_line(i'img);  -- 'img is not standard: provided by GNAT
    put_line(f'img);

    put_line(integer'image(i));
    put_line(float'image(f));

    -- ENUMERATED TYPE
    if my_car_color = red then
       put_line("It's red!");
    else
       put_line("It's not red!");
    end if;

    if today in sat | sun then
       put("Weekend!");
    end if;

    if today not in mon .. fri then  -- Another way
       put("Weekend!");
    end if;

    tomorrow := (if today = sat then sun else Days'Succ(today));

    for d in Days loop
       Days_IO.put(d);
       put(Days'pos(d));
       Days_IO.put(Days'val(Days'pos(d)));
       new_line;
    end loop;

    for c in Color loop
       if c = my_car_color then
          put(c'img);
       end if;
    end loop;
    -- Can also instantiate Ada.Text_IO.Enumeration_IO for get and put for colors


    -- BOOLEAN
    less := i < j;  -- assign a boolean variable
    put_line(less'img);

    -- CONTROL STRUCTURES, BOOLEAN OPERATIONS: AND, OR, NOT, XOR
    if less then 
        put_line(i'img & " < " & j'img);
    elsif NOT (i = j) OR f > g then 
        put(i);
        put(" /= ");
        put_line(j'img & "OR f > g");
    -- IN operator and a range
    -- () required when mixing and, or
    elsif i in 1 .. 10 and (j not in 1 .. 10 xor i * j /= 25) then  
        put_line("Interesting");
    elsif f in 1.1 .. 2.1 then -- Ranges work for floats, too
        put_line("Great, floats too");
    else 
        put_line(i'img & " > " & j'img);
    end if;

    -- SHORT CIRCUIT EVALUATION: AND THEN, OR ELSE
    if j /= 0 AND THEN i > 5 then  -- Stop if NOT j/=0
        put(i / j);  new_line;
    elsif i = 0 OR ELSE j = 0 then -- Stop if i=0
        put_line("One or the other is 0");
    end if;

    -- CASE MUST ACCOUNT FOR ALL VALUES.  
    -- All branches must be disjoint
    case i is
        when 1 =>
            put_line("It's one");
        when 5 | 3 =>
            put_line("It's three or five");
        when 20 .. 29  =>
            put_line("It's in the 20's");
        when 7 | 30 .. 39 | 9  =>
            put_line("It's 7 or 9 or in the 30's");
        when others =>
            put_line("It's neither odd <= 10,");
            put_line("or in 20's or 30's");
    end case;

    int_sum := 0;
    for k in i .. j loop    -- RANGE ALLOWED TO  BE EMPTY!
        int_sum := int_sum + k; 
    end loop;

    for k in reverse 1 .. 10 loop 
        put(k);  -- Prints 10 to 1
    end loop;

    flt_sum := 0.0;
    while i < 2 * j loop 
        flt_sum := flt_sum + f; 
        i := i + 1; 
    end loop;
    put(f, 2, 2, 0);
    new_line;

    loop   -- loop and a half
        get(f); 
        exit when f < 0.0; 
        put(f, 2, 2, 0);
    end loop;

    new_line(2);       -- two newlines
    put_line("DONE");
end intro_all;