-- This program is based on the word-frequency problem described here:
--  <http://shootout.alioth.debian.org/benchmark.php?test=wordfreq&lang=all&sort=fullcpu>
--
-- The input file is:
--  <http://shootout.alioth.debian.org/download/wordfreq-input.txt>
--
-- The output should look like:
--  <http://shootout.alioth.debian.org/download/wordfreq-output.txt>

with Ada.Text_IO;              use Ada.Text_IO;
with Ada.Characters.Handling;  use Ada.Characters.Handling;
with Ada.Integer_Text_IO;      use Ada.Integer_Text_IO;
with Ada.Command_Line;         use Ada.Command_Line;
with Ada.Containers;           use Ada.Containers;
with Wordfreq_Types;

procedure Wordfreq is

   Line : String (1 .. 256);
   Last : Natural;

   I, J : Positive;
   Word : String (1 .. Line'Length);

   Data_Vector : Wordfreq_Types.Data_Vectors.Vector;

   File : File_Type;

begin

   -- Either use a pipe to execute this:
   --   cat file.txt | wordfreq
   -- (from a win shell use "type" instead of "cat");
   -- or, you can just name the input file on the command line.

   if Argument_Count > 0 then
      Open (File, In_File, Name => Argument (1));
      Set_Input (File);
   end if;

   <<Scan_Line>> null;

   if End_Of_File then
      goto Sort_Words;
   end if;

   Get_Line (Line, Last);
   pragma Assert (Last < Line'Last);

   I := Line'First;

   <<Find_Word>> null;

   loop
      if I > Last then
         goto Scan_Line;
      end if;

      exit when Is_Letter (Line (I));

      I := I + 1;
   end loop;

   J := 1;

   <<Scan_Word>> null;

   Word (J) := To_Lower (Line (I));
   I := I + 1;

   if I > Last then
      Wordfreq_Types.Insert (Data_Vector, Word (1 .. J));
      goto Scan_Line;
   end if;

   if Is_Letter (Line (I)) then
      J := J + 1;
      goto Scan_Word;
   end if;

   Wordfreq_Types.Insert (Data_Vector, Word (1 .. J));
   I := I + 1;

   goto Find_Word;

   <<Sort_Words>> null;

   Sort_Data:
   declare
      function "<" (L, R : Wordfreq_Types.Data_Type) return Boolean is
      begin
         if L.Count = R.Count then
            return L.Word > R.Word;
         else
            return L.Count > R.Count;
         end if;
      end "<";

      package Sorting is
         new Wordfreq_Types.Data_Vectors.Generic_Sorting;
   begin
      Sorting.Sort (Data_Vector);
   end Sort_Data;

   Print_Data:
   declare
      procedure Print (Data : Wordfreq_Types.Data_Type) is
      begin
         Put (Data.Count, Width => 7);
         Put (' ');
         Put (Data.Word);
         New_Line;
      end;
   begin
      for Index in Data_Vector.First_Index .. Data_Vector.Last_Index loop
         Data_Vector.Query_Element (Index, Print'Access);
      end loop;
   end Print_Data;

end Wordfreq;
