#############################################################################
##
#W  gapbibl.g             GAP 4 package `browse'                Thomas Breuer
##
#Y  Copyright (C)  2006,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
##


#############################################################################
##
#F  StringBibAsBib( <bibtex_record> )
##
##  It would be good if the &GAPDoc; function `PrintBibAsBib' would be
##  replaced by a function that returns a string instead of printing the
##  data.
##
StringBibAsBib:= function( bibtex_record )
    local str, a;

    str:= "";
    a:= OutputTextString( str, true );
    PrintTo1( a, function() PrintBibAsBib( bibtex_record ); end );
    CloseStream( a );
    return str;
end;


#############################################################################
##
#F  BrowseData.ExplodedString( <string>, <substring> )
##
##  This is similar to the library function `SplitString'
##  but more general in the sense that <string> is split at all occurrences
##  of a certain substring <substring> not at single characters.
##
##  (In the current application, this function would be unnecessary if the
##  &GAPDoc; function `NormalizedNameAndKey' would return also the list of
##  involved names.)
##
BrowseData.ExplodedString:= function( string, substring )
    local result, pos;

    result:= [];
    while true do
      pos:= PositionSublist( string, substring );
      if pos = fail then
        if not IsEmpty( string ) then
          Add( result, string );
        fi;
        return result;
      fi;
      if pos <> 0 then
        Add( result, string{ [ 1 .. pos - 1 ] } );
      fi;
      string:= string{ [ pos + Length( substring ) .. Length( string ) ] };
    od;
end;


#############################################################################
##
#F  BrowseData.SimplifiedString( <string> )
##
##  Normalize whitespace, replace &LaTeX; notorious characters,
##  insert whitespace behind dots.
##
BrowseData.SimplifiedString:= function( string )
    local patterns, pair, pos;

    patterns:= [ [ "{", "" ],
                 [ "}", "" ],
                 [ "~", " " ],
                 [ "\\", "" ],
               ];

    string:= NormalizedWhitespace( string );
    for pair in patterns do
      string:= ReplacedString( string, pair[1], pair[2] );
    od;
    pos:= Position( string, '.' );
    while pos <> fail do
      if pos < Length( string ) and string[ pos+1 ] <> " " then
        string:= Concatenation( string{ [ 1 .. pos ] }, " ",
                                string{ [ pos+1 .. Length( string ) ] } );
      fi;
      pos:= Position( string, '.', pos );
    od;

    return string;
end;


#############################################################################
##
#F  BrowseBibliography( [<bibfiles>] )
##
##  <#GAPDoc Label="Bibliography_section">
##  <Section Label="sec:gapbibl">
##  <Heading>Overview of the &GAP; Bibliography</Heading>
##
##  The &GAP; documentation contains a bibliography of &GAP; related
##  publications, see&nbsp;<Cite Key="GAPBibliography"/>.
##  &Browse; provides access to this information in &GAP;,
##  using the standard facilities of the function
##  <Ref Func="NCurses.BrowseGeneric"/>, i.&nbsp;e.,
##  one can scroll in the list, search for entries, sort by year,
##  sort and categorize by authors etc.
##  <P/>
##  The &Browse; package contains a (perhaps outdated) version of this
##  bibliography.
##  One can get an updated version as follows.
##  <P/>
##  <C>wget -N http://www.gap-system.org/Doc/Bib/gap-publishednicer.bib</C>
##
##  <ManSection>
##  <Func Name="BrowseBibliography" Arg="[bibfiles]"/>
##
##  <Returns>
##  a list of strings representing &BibTeX; entries.
##  </Returns>
##
##  <Description>
##  This function shows the list of &BibTeX; entries in the files given by
##  the strings in the list <A>bibfiles</A>.
##  If no argument is given then the file <F>bibl/gap-publishednicer.bib</F>
##  in the &Browse; package directory is taken.
##  The return value is a list of strings that contain the &BibTeX; entries
##  that have been <Q>clicked</Q> in visual mode.
##  <P/>
##  The full functionality of the function
##  <Ref Func="NCurses.BrowseGeneric"/> is available.
##  <P/>
##  <Example><![CDATA[
##  gap> # sort and categorize by year, scroll down, expand a category row
##  gap> BrowseData.SetReplay( "scrrscsedddddxdddddQ" );
##  gap> BrowseBibliography();;
##  gap> # sort & categorize by authors, expand all category rows, scroll down
##  gap> BrowseData.SetReplay( "scscXseddddddQ" );
##  gap> BrowseBibliography();;
##  gap> # sort and categorize by journal, search for a journal name, expand
##  gap> BrowseData.SetReplay( Concatenation( "scrrrsc/J. Algebra",
##  >        [ NCurses.keys.ENTER ], "nxdddQ" ) );
##  gap> BrowseBibliography();;
##  gap> BrowseData.SetReplay( false );
##  ]]></Example>
##  <P/>
##  <E>Implementation remarks</E>:
##  The browse table has a static header, no footer and row labels,
##  one row of column labels is given by the descriptions of the table
##  columns (authors, title, year, journal).
##  For categorizing by authors, a special <C>CategoryValues</C> function is
##  provided that distributes the authors to different category rows,
##  such that each entry appears once for each of its authors in the
##  categorized table.
##  When a data row or an entry in this row is selected,
##  <Q>click</Q> adds the corresponding &BibTeX; entry to the result list.
##  The width of the title column is preset, in order to avoid computing all
##  values in advance; note that the contents of this column is formatted
##  as a paragraph, using the function <C>FormatParagraph</C>.
##  <P/>
##  For three columns, the sort parameters are customized as follows:
##  The authors column does not become hidden when the table is categorized
##  according to this column, sorting by year yields a descending order,
##  and the category rows arising from the three columns show the numbers
##  of the data rows that belong to them.
##  <P/>
##  This function requires some of the utilities provided by the
##  &GAP; package <Package>GAPDoc</Package>
##  (see&nbsp;<Cite Key="GAPDoc"/>),
##  such as <C>FormatParagraph</C>, <C>NormalizedWhitespace</C>,
##  <C>NormalizeNameAndKey</C>, <C>ParseBibFiles</C>, and
##  <C>PrintBibAsBib</C>.
##  <P/>
##  The code can be found in the file <F>lib/gapbibl.g</F> of the package.
##  </Description>
##  </ManSection>
##  </Section>
##  <#/GAPDoc>
##
BindGlobal( "BrowseBibliography", function( arg )
    local files, header, titlewidth, leftaligned, entries, file, parse,
          entry, authors, title, year, journal, table;

    # Get the arguments.
    if   Length( arg ) = 0 then
      files:= [ Filename( DirectoriesPackageLibrary( "browse", "bibl" ),
                          "gap-publishednicer.bib" ) ];
      header:= "GAP Bibliography";
    elif Length( arg ) = 1 and IsString( arg[1] ) then
      files:= arg;
      header:= files[1];
    elif Length( arg ) = 1 and IsList( arg[1] )
                           and ForAll( arg[1], IsString ) then
      files:= arg[1];
      header:= files[1];
      if 1 < Length( files ) then
        header:= Concatenation( header, ", ..." );
      fi;
    else
      Error( "usage: BrowseBibliography( [<bibfiles>] )" );
    fi;

    titlewidth:= 40;

    leftaligned:= function( list )
      local max;

      max:= - Maximum( List( list, Length ) );
      return List( list, x -> FormattedString( x, max ) );
    end;

    # Parse the data files.
    entries:= [];
    for file in files do
      parse:= ParseBibFiles( file )[1];
      for entry in parse do
        NormalizeNameAndKey( entry );
      od;
      Append( entries, parse );
    od;

    # Fetch the items that shall be shown.
    authors:= List( entries, x -> rec(
                rows:= leftaligned( BrowseData.ExplodedString(
                         BrowseData.SimplifiedString( x.author ),
                         " and " ) ),
                            align:= "tl" ) );
    title:= List( entries, x -> rec(
              rows:= leftaligned( SplitString( FormatParagraph(
                       BrowseData.SimplifiedString( x.title ),
                                titlewidth, "left" ), "\n" ) ),
                      align:= "tl" ) );
    year:= List( entries, x -> rec( rows:= [ x.year ], align:= "tr" ) );
    journal:= List( entries, function( x )
                if IsBound( x.journal ) then
                  return rec( rows:= [ x.journal ], align:= "tl" );
                else
                  return "";
                fi;
                end );

    # Construct the browse table.
    table:= rec(
      work:= rec(
        align:= "tl",
        header:= [ "", [ NCurses.attrs.UNDERLINE, true, header,
                         NCurses.attrs.NORMAL, true,
                         " (", String( Length( entries ) ), " entries)" ],
                   "" ],
        main:= TransposedMat( [ authors, title, year, journal ] ),
        labelsCol:= [ [ rec( rows:= [ "authors" ], align:= "l" ),
                        rec( rows:= [ "title" ], align:= "l" ),
                        rec( rows:= [ "year" ], align:= "r" ),
                        rec( rows:= [ "journal" ], align:= "l" ),
        ] ],
        sepLabelsCol:= "=",
        sepRow:= "-",
        sepCol:= [ "| ", " | ", " | ", " | ", " |" ],

        # Set fixed width for the paragraph column with the title.
        widthCol:= [ ,,, titlewidth ],

        Click:= rec(
          select_entry:= rec(
            helplines:= [ "add the BibTeX entry to the result list" ],
            action:= function( t )
            if t.dynamic.selectedEntry <> [ 0, 0 ] then
              Add( t.dynamic.Return, StringBibAsBib( entries[
                       t.dynamic.selectedEntry[1] / 2 ] ) );
            fi;
          end ),
          select_row:= ~.work.Click.select_entry,
        ),
        CategoryValues:= function( t, i, j )
          local value;

          value:= t.work.main[ i/2 ][ j/2 ];
          if IsRecord( value ) then
            value:= value.rows;
          fi;
          if NCurses.IsAttributeLine( value ) then
            value:= [ value ];
          fi;
          if j = 2 then
            # Split the value of the authors column into lines.
            return Filtered( List( value, NormalizedWhitespace ),
                             x -> x <> "" );
          else
            value:= NormalizedWhitespace(
                        NCurses.ConcatenationAttributeLines( value ) );
            if value = "" then
              value:= "(empty category)";
            fi;
            return [ value ];
          fi;
        end,
      ),
      dynamic:= rec(
        Return:= [],
      ),
    );

    # Customize the sort parameters for the authors, year, journal columns.
    BrowseData.SetSortParameters( table, "column", 1,
        [ "hide on categorizing", "no",
          "add counter on categorizing", "yes" ] );
    BrowseData.SetSortParameters( table, "column", 3,
        [ "direction", "descending",
          "add counter on categorizing", "yes" ] );
    BrowseData.SetSortParameters( table, "column", 4,
        [ "add counter on categorizing", "yes" ] );

    # Show the table.
    return NCurses.BrowseGeneric( table );
end );


#############################################################################
##
#E

