------------------------------------------------------------------------------
-- Copyright (c) 2016, Natacha Porté                                        --
--                                                                          --
-- Permission to use, copy, modify, and distribute this software for any    --
-- purpose with or without fee is hereby granted, provided that the above   --
-- copyright notice and this permission notice appear in all copies.        --
--                                                                          --
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES --
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         --
-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  --
-- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   --
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    --
-- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  --
-- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           --
------------------------------------------------------------------------------

------------------------------------------------------------------------------
-- Natools.Smaz_Implementations.Base_64_Tools provides types and primitives --
-- common to both base-64 and base-4096 variants of Smaz.                   --
------------------------------------------------------------------------------

with Ada.Streams;

package Natools.Smaz_Implementations.Base_64_Tools is
   pragma Pure;

   use type Ada.Streams.Stream_Element;


   type Base_64_Digit is range 0 .. 63;

   subtype Single_Byte_Padding is Base_64_Digit range 0 .. 15;
      --  Padding after a single-byte partial block
   subtype Double_Byte_Padding is Base_64_Digit range 0 .. 3;
      --  Padding after a double-byte partial block

   subtype Full_Block is String (1 .. 3);
   subtype Double_Byte is String (1 .. 2);

   subtype Full_Block_Image is Ada.Streams.Stream_Element_Array (1 .. 4);
   subtype Double_Byte_Image is Ada.Streams.Stream_Element_Array (1 .. 3);
   subtype Single_Byte_Image is Ada.Streams.Stream_Element_Array (1 .. 2);

   type Base_64_Array
     is array (Ada.Streams.Stream_Element_Offset range <>) of Base_64_Digit;

   subtype Base_64_Symbol is Ada.Streams.Stream_Element
     with Static_Predicate => Base_64_Symbol in
                                 Character'Pos ('A') .. Character'Pos ('Z')
                               | Character'Pos ('a') .. Character'Pos ('z')
                               | Character'Pos ('0') .. Character'Pos ('9')
                               | Character'Pos ('+')  | Character'Pos ('/');


   function Image (Digit : in Base_64_Digit) return Ada.Streams.Stream_Element
     is (case Digit is
         when 0 .. 25
           => Character'Pos ('A') + Ada.Streams.Stream_Element (Digit) - 0,
         when 26 .. 51
           => Character'Pos ('a') + Ada.Streams.Stream_Element (Digit) - 26,
         when 52 .. 61
           => Character'Pos ('0') + Ada.Streams.Stream_Element (Digit) - 52,
         when 62 => Character'Pos ('+'),
         when 63 => Character'Pos ('/'));
      --  Printable character representation of Digit

   function Value (Symbol : in Base_64_Symbol) return Base_64_Digit
     is (case Symbol is
         when Character'Pos ('A') .. Character'Pos ('Z')
           => Base_64_Digit (Symbol - Character'Pos ('A') + 0),
         when Character'Pos ('a') .. Character'Pos ('z')
           => Base_64_Digit (Symbol - Character'Pos ('a') + 26),
         when Character'Pos ('0') .. Character'Pos ('9')
           => Base_64_Digit (Symbol - Character'Pos ('0') + 52),
         when Character'Pos ('+') => 62,
         when Character'Pos ('/') => 63);
      --  Value represented by a symbol


   function Image_Length (Input_Length : in Natural)
     return Ada.Streams.Stream_Element_Count
     is (Ada.Streams.Stream_Element_Count
           (Input_Length + (Input_Length + 2) / 3));
      --  Paddingless encoded length

   function Value_Length (Input_Length : in Ada.Streams.Stream_Element_Count)
     return Natural
     is (Natural (Input_Length) - (Natural (Input_Length) + 3) / 4);
      --  Original length of an encoded array

   function Symbol_Count (Input : Ada.Streams.Stream_Element_Array)
     return Ada.Streams.Stream_Element_Count;
      --  Return the number of valid symbols in Input


   procedure Encode
     (Input : in String;
      Output : in out Ada.Streams.Stream_Element_Array;
      Offset : in out Ada.Streams.Stream_Element_Offset);
      --  Paddingless raw encoding of Input data

   procedure Decode
     (Input : in Ada.Streams.Stream_Element_Array;
      Offset : in out Ada.Streams.Stream_Element_Offset;
      Output : out String);
      --  Paddingless raw decoding of Input data


   procedure Encode_Block
     (Input : in Full_Block;
      Output : in out Ada.Streams.Stream_Element_Array;
      Offset : in out Ada.Streams.Stream_Element_Offset);
   procedure Encode_Double
     (Input : in Double_Byte;
      Padding : in Double_Byte_Padding;
      Output : in out Ada.Streams.Stream_Element_Array;
      Offset : in out Ada.Streams.Stream_Element_Offset);
   procedure Encode_Single
     (Input : in Character;
      Padding : in Single_Byte_Padding;
      Output : in out Ada.Streams.Stream_Element_Array;
      Offset : in out Ada.Streams.Stream_Element_Offset);
      --  Encode a complete or partial block into Output at Offset

   procedure Decode_Block
     (Input : in Ada.Streams.Stream_Element_Array;
      Offset : in out Ada.Streams.Stream_Element_Offset;
      Output : out Full_Block);
   procedure Decode_Double
     (Input : in Ada.Streams.Stream_Element_Array;
      Offset : in out Ada.Streams.Stream_Element_Offset;
      Output : out Double_Byte;
      Padding : out Double_Byte_Padding);
   procedure Decode_Single
     (Input : in Ada.Streams.Stream_Element_Array;
      Offset : in out Ada.Streams.Stream_Element_Offset;
      Output : out Character;
      Padding : out Single_Byte_Padding);


   procedure Next_Digit
     (Input : in Ada.Streams.Stream_Element_Array;
      Offset : in out Ada.Streams.Stream_Element_Offset;
      Digit : out Base_64_Digit);
      --  Look for the first valid symbol in Input from Offset, and decode it

end Natools.Smaz_Implementations.Base_64_Tools;
