#!/usr/bin/perl
use strict;
use warnings;
use constant {
  EXIT_OK => 0,
  EXIT_ERROR => 1
};

# no warnings 'utf8';
use Carp;
use IPC::Cmd qw( can_run run );
use Log::Log4perl qw(:no_extra_logdie_message);
use Log::Log4perl::Level;
use POSIX qw(strftime);
use Biber;
use Biber::Output::BBL;
use Biber::Utils;
use File::Spec;

#Don't remove the next line!
#<wrapper_snippet>---------------------------------

use Getopt::Long qw/:config no_ignore_case/;
my $opts = {};
GetOptions(
           $opts,
           'bibencoding|e=s',
           'bblencoding|E=s',
           'bblsafechars',
           'bblsafecharsset=s',
           'collate|C',
           'collate_options|c=s',
           'configfile|g=s',
           'convert_control',
           'decodecharsset=s',
           'debug|D',
           'fastsort|f',
           'help|h|?',
           'mincrossrefs|m=s',
           'logfile=s',
           'noconf',
           'nolog',
           'nostdmacros',
           'onlylog',
           'outfile|O=s',
           'output_directory=s',
           'quiet|q',
           'sortcase=s',
           'sortlocale|l=s',
           'sortupper=s',
           'trace|T',
           'u',                   # alias for bibencoding=UTF-8
           'U',                   # alias for bblencoding=UTF-8
           'validate_control',
           'validate_structure|V',
           'version|v',
           'wraplines|w'
          );

#--------------------------------------------------

our $VERSION = $Biber::VERSION;
our $BETA_VERSION = $Biber::BETA_VERSION;

if (exists $opts->{'help'}) {
  print usage();
  exit EXIT_OK;
}

if (exists $opts->{'version'}) {
  print version();
  exit EXIT_OK;
}

# Catch this situation early
unless (@ARGV) {
  print STDERR usage();
  exit EXIT_ERROR;
}

#--------------------------------------------------

# Sanitise collate option if fastsort is specified
if ($opts->{fastsort}) {
  delete $opts->{collate};
}

# Resolve shortcut aliases for UTF-8ness
if ($opts->{u}) {
  $opts->{bibencoding} = 'UTF-8';
  delete $opts->{u};
}
if ($opts->{U}) {
  $opts->{bblencoding} = 'UTF-8';
  delete $opts->{U};
}

# Sanitise user-specified log name
$opts->{logfile} =~ s/\.blg\z//xms if $opts->{logfile};

# Create Biber object, passing command-line options
my $biber = Biber->new(%$opts);

# Set log file name
my $biberlog;
if (my $log = Biber::Config->getoption('logfile')) { # user specified logfile name
  $biberlog = $log . '.blg';
}
elsif (not @ARGV) {
  $biberlog = 'biber.blg'; # default if no .bcf file specified
}
else { # set log to jobname.blg
  my $bcf = $ARGV[0];
  $bcf =~ s/\.bcf\z//xms;
  $biberlog = $bcf . '.blg';
}

# prepend output directory for log, if specified
if (my $outdir = Biber::Config->getoption('output_directory')) {
  $biberlog = File::Spec->catfile($outdir, $biberlog);
}

# Setting the logging level of Log::Log4perl
my $LOGLEVEL;
if (Biber::Config->getoption('trace')) {
  $LOGLEVEL = 'TRACE'
} elsif (Biber::Config->getoption('debug')) {
  $LOGLEVEL = 'DEBUG'
} elsif (Biber::Config->getoption('quiet')) {
  $LOGLEVEL = 'ERROR'
} else {
  $LOGLEVEL = 'INFO'
}

my $LOGLEVEL_F;
my $LOG_MAIN;
if (Biber::Config->getoption('nolog')) {
  $LOG_MAIN = 'Screen';
  $LOGLEVEL_F = 'OFF'
} else {
  $LOG_MAIN = 'Logfile, Screen';
  $LOGLEVEL_F = $LOGLEVEL
}

my $LOGLEVEL_S;
if (Biber::Config->getoption('onlylog')) {
  $LOGLEVEL_S = 'OFF'
} else {
  # Max screen loglevel is INFO
  if (Biber::Config->getoption('quiet')) {
    $LOGLEVEL_S = 'ERROR';
  }
  else {
    $LOGLEVEL_S = 'INFO';
  }
}

# configuration "file" for Log::Log4perl
my $l4pconf = qq(
    log4perl.category.main                             = $LOGLEVEL, $LOG_MAIN
    log4perl.category.screen                           = $LOGLEVEL_S, Screen

    log4perl.appender.Screen                           = Log::Log4perl::Appender::Screen
    log4perl.appender.Screen.utf8                      = 1
    log4perl.appender.Screen.Threshold                 = $LOGLEVEL_S
    log4perl.appender.Screen.stderr                    = 0
    log4perl.appender.Screen.layout                    = Log::Log4perl::Layout::SimpleLayout
);

# Only want a logfile appender if --nolog isn't set
if ($LOGLEVEL_F ne 'OFF') {
  $l4pconf .= qq(
    log4perl.category.logfile                          = $LOGLEVEL_F, Logfile
    log4perl.appender.Logfile                          = Log::Log4perl::Appender::File
    log4perl.appender.Logfile.utf8                     = 1
    log4perl.appender.Logfile.Threshold                = $LOGLEVEL_F
    log4perl.appender.Logfile.filename                 = $biberlog
    log4perl.appender.Logfile.mode                     = clobber
    log4perl.appender.Logfile.layout                   = Log::Log4perl::Layout::PatternLayout
    log4perl.appender.Logfile.layout.ConversionPattern = [%r] %F{1}:%L> %p - %m%n
);
}

Log::Log4perl::init(\$l4pconf) ;

# get the logger object
my $logger  = Log::Log4perl::get_logger('main') ;
my $screen  = Log::Log4perl::get_logger('screen');
my $logfile = Log::Log4perl::get_logger('logfile');

my $vn = $VERSION;
$vn .= ' (beta)' if $BETA_VERSION;

$logger->info("This is biber $vn") unless Biber::Config->getoption('nolog');
$logger->info("Logfile is '$biberlog'") unless Biber::Config->getoption('nolog');

my $ctrlfile;
my $bblfile;

my $time_string = strftime "%a %b %e, %Y, %H:%M:%S", localtime;

$logfile->info("=== Biber version $vn");
$logfile->info("=== $time_string");

if (Biber::Config->getoption('debug')) {
  $screen->info("DEBUG mode: all messages are logged to '$biberlog'")
}

$ctrlfile = $ARGV[0] or croak usage();
$ctrlfile .= '.bcf' unless $ctrlfile =~ m/\.bcf$/;

if (Biber::Config->getoption('outfile')) {
  $bblfile = Biber::Config->getoption('outfile')
}
else {
  $bblfile = $ctrlfile;
  $bblfile =~ s/bcf$/bbl/;
}

# Set the .bbl path to the output dir, if specified
if (my $outdir = Biber::Config->getoption('output_directory')) {
  my (undef, undef, $file) = File::Spec->splitpath($bblfile);
  $bblfile = File::Spec->catfile($outdir, $file)
}

# parse the .bcf control file
$biber->parse_ctrlfile($ctrlfile);

# Postprocess biber options now that they are all read from the various places
Biber::Config->postprocess_biber_opts;

# Check to see if the .bcf set debug=1. If so, increase logging level
# We couldn't set this on logger init as the .bcf hadn't been read then
if (Biber::Config->getoption('debug')) {
  $logger->level($DEBUG);
}

if (Biber::Config->getoption('trace')) {
  $logger->trace("\n###########################################################\n",
    "############# Dump of initial config object: ##############\n",
    Data::Dump::pp($Biber::Config::CONFIG), "\n",
    "############# Dump of initial biber object: ###############\n",
    $biber->_stringdump,
    "\n###########################################################")
}

# Set the output class. Should be a subclass of Biber::Output::Base
$biber->set_output_obj(Biber::Output::BBL->new());

if (Biber::Config->getoption('trace')) {
  $logger->trace("\n###########################################################\n",
    "############# Dump of post-parse config object: ###########\n",
    Data::Dump::pp($Biber::Config::CONFIG), "\n",
    "############# Dump of post-parse biber object: ############\n",
    $biber->_stringdump,
    "\n###########################################################")
}

# Do all the real work
$biber->prepare;

if (Biber::Config->getoption('trace')) {
  $logger->trace("\n###########################################################\n",
    "############# Dump of final config object: ################\n",
    Data::Dump::pp($Biber::Config::CONFIG), "\n",
    "############# Dump of final biber object: #################\n",
    $biber->_stringdump,
    "\n###########################################################")
}

# Get reference to output object
my $bbloutput = $biber->get_output_obj;
# Set the output target
$bbloutput->set_output_target_file($bblfile);
# Write the output to the target
$bbloutput->output;

###

# display warnings/errors summary, like BibTeX's format
# unless in quiet mode and exit
unless (Biber::Config->getoption('quiet')) {
  if (defined($biber->{errors})) { # if errors, ignore warnings
    if ($biber->{errors} == 1) {
      print "(There was 1 error message)\n";
    }
    elsif ($biber->{errors} > 1) {
      print "(There were ", $biber->{errors}, " error messages)\n";
    }
    exit EXIT_ERROR;
  }
  elsif (defined($biber->{warnings})) {
    if ($biber->{warnings} == 1) {
      print "(There was 1 warning)\n";
    } elsif ($biber->{warnings} > 1) {
      print "(There were ", $biber->{warnings}, " warnings)\n";
    }
    exit EXIT_OK;
  }
}

#======================================================

sub version {
  my $v = "biber version: $VERSION";
  $v .= ' (beta)' if $BETA_VERSION;
  return "$v\n";
}

sub usage {
  qq{
Usage:  biber file.bcf
        Creates file.bbl using control file file.bcf (.bcf extension is optional)

        biber -d foo.bib file.bcf
        Creates file.bbl from entries in foo.bib and the data sources
        listed in file.bcf

        biber -d foo.bib,bar.bib -a
        Creates foo.bbl from all entries in foo.bib and bar.bib. No .bcf used

Options:

  --bblencoding|-E [encoding]
      Specify the encoding of the output .bbl file. Default is "UTF-8".
      Normally it's not necessary to set this as it's passed via biblatex
      from the inputenc package setting.
      See "perldoc Encode::Supported" for a list of supported encodings.

  --bblsafechars
      Try to convert UTF-8 chars into LaTeX macros when writing the .bbl
      This can prevent unknown char errors when using PDFLaTeX and inputenc
      as this doesn't understand all of UTF-8. Note, it is better to switch
      to XeTeX or LuaTeX to avoid this situation. Without argument uses a
      generous set of conversions.

  --bblsafecharsset=base|extra|full
      The set of characters included in the conversion routine for
      --bblsafechars. Set to "full" to try harder with a much
      larger set or "base" to use a minimal set. Default is "extra" which is
      between "base" and "full". You may need to load more macro packages
      to deal with the results of "full" (Dings, Greek characters, special
      symbols etc.)

  --bibencoding|-e [encoding]
      Specify the encoding of the bib file(s). Default is "UTF-8"
      Normally it's not necessary to set this as it's passed via the
      .bcf file from biblatex's setting of the same name.
      See "perldoc Encode::Supported" for a list of supported encodings.

  --collate|-C
      Sort with Unicode::Collate instead of the built-in sort function.
      This is the default.

  --collate_options|-c [options]
      Options to pass to the Unicode::Collate object used for sorting
      (default is 'level => "2", table => "latinkeys.txt"').
      See "perldoc Unicode::Collate" for details.

  --configfile|-g <file>
      Use <file> as configuration file for Biber.
      The default is the first file found among
      "biber.conf" in the current directory, "\$HOME/.biber.conf",
      or else the output of "kpsewhich biber.conf".

  --convert_control
      Converts the .bcf control file into html using an XSLT transform. Can
      be useful for debugging. File is named by appending \".html\"
      to .bcf file.

  --decodecharsset=base|extra|full
      The set of characters included in the conversion routine when decoding
      LaTeX macros into UTF-8 (which happens when --bblencoding|-E is set to
      UTF-8) Set to "full" to try harder with a much larger set or "base" to
      use a minimal set. Default is "extra" which is between "base" and "full".
      You may want to try "full" if you want, for example, to sort more
      obscure UTF-8 characters in the .bib

  --debug|-D
      Turn on debugging for biber.

  --fastsort|-f
      Use Perl’s sort instead of Unicode::Collate for sorting. Also uses
      OS locale definitions (which may be broken for some languages ...)

  --help|-h
      Show this help message.

  --logfile <file>
      Use <file>.blg as the name of the logfile

  --mincrossrefs|-m <n>
      Set threshold for crossrefs.

  --noconf
      Don't look for a configfile

  --nolog
      Do not write any logfile.

  --nostdmacros
      Don't automatically define any standard macros like month abbreviations.
      If you also define these yourself, this option can be used to suppress
      macro redefinition warnings.

  --onlylog
      Do not write any message to screen.

  --outfile|-O <file>
      Output to <file> instead of <basename>.bbl
      <file> is relative to --output_directory, if set (absolute
      paths in this case are stripped to filename only). <file> can
      be absolute if --output_directory is not set.

  --output_directory <d>
      Files (.bbl and .log) are output to directory <d> instead
      of the current directory. Input files are also looked for in <d>
      before current directory.

  --quiet|-q
      Log only errors

  --sortcase=true|false
      Case-sensitive sorting (default is true)

  --sortlocale|-l [locale]
      Set the locale to be used for sorting.  With default sorting
      (--collate|-C), the locale is used to add CLDR 
      tailoring to the sort (if available for the locale). With
      --fastsort|-f this sets the OS locale for sorting.

  --sortupper=true|false
      Whether to sort uppercase before lowercase when using
      default sorting (--collate|-C). When
      using --fastsort|-f, your OS collation locale determines
      this and this option is ignored. (default is true)

  -u
      Alias for --bibencoding=UTF-8.

  -U
      Alias for --bblencoding=UTF-8.

  --validate_control
      Schema validate the .bcf biblatex control file.

  --validate_structure|-V
      Validate various aspects of the bib entries

  --version|-v
      Display version number.

  --wraplines|-w
      Wrap lines in the .bbl file.

Bug reports, forum and downloads at:
  https://sourceforge.net/projects/biblatex-biber
\n}
}

__END__

=pod

=encoding utf8

=head1 NAME

C<biber> - A bibtex replacement for users of biblatex

=head1 SYNOPSIS

Please run "biber --help" for usage

=head1 DESCRIPTION

C<biber> provides a replacement of the bibtex processor for users of biblatex.
Besides emulating the functionality of bibtex + biblatex.bst, it also supports ...

(REST TO BE WRITTEN)

=head1 AUTHOR

François Charette, C<< <firmicus at gmx.net> >>

=head1 BUGS

Please report any bugs or feature requests on our sourceforge tracker at
L<https://sourceforge.net/tracker2/?func=browse&group_id=228270>.

=head1 COPYRIGHT & LICENSE

Copyright 2009-2011 François Charette and Philip Kime, all rights reserved.

This module is free software.  You can redistribute it and/or
modify it under the terms of the Artistic License 2.0.

This program is distributed in the hope that it will be useful,
but without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.

=cut

# vim: set tabstop=2 shiftwidth=2 expandtab:
