#!/usr/bin/perl -w

use strict;
use Cwd qw(abs_path getcwd);
use Date::Format;
use File::Path;
use File::Copy;
use File::Basename;

our $version_major;
our $version_minor;
our $version_sub_minor;
our $asio_name;
our $boost_asio_name;

sub print_usage_and_exit
{
  print("usage: ./release.pl <version>\n");
  print("   or: ./release.pl --package-asio\n");
  print("\n");
  print("examples:\n");
  print("  create new version and build packages for asio and boost.asio:\n");
  print("    ./release.pl 1.2.0\n");
  print("  create packages for asio only:\n");
  print("    ./release.pl --package-asio\n");
  exit(1);
}

sub determine_version($)
{
  my $version_string = shift;
  if ($version_string =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)$/)
  {
    our $version_major = $1;
    our $version_minor = $2;
    our $version_sub_minor = $3;

    our $asio_name = "asio";
    $asio_name .= "-$version_major";
    $asio_name .= ".$version_minor";
    $asio_name .= ".$version_sub_minor";

    our $boost_asio_name = "boost_asio";
    $boost_asio_name .= "_$version_major";
    $boost_asio_name .= "_$version_minor";
    $boost_asio_name .= "_$version_sub_minor";
  }
  else
  {
    print_usage_and_exit();
  }
}

sub determine_version_from_configure()
{
  my $from = "configure.ac";
  open(my $input, "<$from") or die("Can't open $from for reading");
  while (my $line = <$input>)
  {
    chomp($line);
    if ($line =~ /^AC_INIT\(\[asio\],\[(.*)\]\)$/)
    {
      our $asio_name = "asio-$1";
      our $boost_asio_name = "boost_asio_$1";
      last;
    }
  }
}

sub update_configure_ac
{
  # Open the files.
  my $from = "configure.ac";
  my $to = $from . ".tmp";
  open(my $input, "<$from") or die("Can't open $from for reading");
  open(my $output, ">$to") or die("Can't open $to for writing");

  # Copy the content.
  while (my $line = <$input>)
  {
    chomp($line);
    if ($line =~ /^AC_INIT\(\[asio\].*\)$/)
    {
      $line = "AC_INIT([asio],[";
      $line .= "$version_major.$version_minor.$version_sub_minor";
      $line .= "])";
    }
    print($output "$line\n");
  }

  # Close the files and move the temporary output into position.
  close($input);
  close($output);
  move($to, $from);
  unlink($to);
}

sub update_readme
{
  # Open the files.
  my $from = "README";
  my $to = $from . ".tmp";
  open(my $input, "<$from") or die("Can't open $from for reading");
  open(my $output, ">$to") or die("Can't open $to for writing");

  # Copy the content.
  while (my $line = <$input>)
  {
    chomp($line);
    if ($line =~ /^asio version/)
    {
      $line = "asio version ";
      $line .= "$version_major.$version_minor.$version_sub_minor";
    }
    elsif ($line =~ /^Released/)
    {
      my @time = localtime;
      $line = "Released " . strftime("%A, %d %B %Y", @time) . ".";
    }
    print($output "$line\n");
  }

  # Close the files and move the temporary output into position.
  close($input);
  close($output);
  move($to, $from);
  unlink($to);
}

sub update_asio_version_hpp
{
  # Open the files.
  my $from = "include/asio/version.hpp";
  my $to = $from . ".tmp";
  open(my $input, "<$from") or die("Can't open $from for reading");
  open(my $output, ">$to") or die("Can't open $to for writing");

  # Copy the content.
  while (my $line = <$input>)
  {
    chomp($line);
    if ($line =~ /^#define ASIO_VERSION /)
    {
      my $version = $version_major * 100000;
      $version += $version_minor * 100;
      $version += $version_sub_minor + 0;
      $line = "#define ASIO_VERSION " . $version;
      $line .= " // $version_major.$version_minor.$version_sub_minor";
    }
    print($output "$line\n");
  }

  # Close the files and move the temporary output into position.
  close($input);
  close($output);
  move($to, $from);
  unlink($to);
}

sub update_boost_asio_version_hpp
{
  # Open the files.
  my $from = "boost/boost/asio/version.hpp";
  my $to = $from . ".tmp";
  open(my $input, "<$from") or die("Can't open $from for reading");
  open(my $output, ">$to") or die("Can't open $to for writing");

  # Copy the content.
  while (my $line = <$input>)
  {
    chomp($line);
    if ($line =~ /^#define BOOST_ASIO_VERSION /)
    {
      my $version = $version_major * 100000;
      $version += $version_minor * 100;
      $version += $version_sub_minor + 0;
      $line = "#define BOOST_ASIO_VERSION " . $version;
      $line .= " // $version_major.$version_minor.$version_sub_minor";
    }
    print($output "$line\n");
  }

  # Close the files and move the temporary output into position.
  close($input);
  close($output);
  move($to, $from);
  unlink($to);
}

sub build_asio_doc
{
  $ENV{BOOST_ROOT} = abs_path("boost");
  system("rm -rf doc");
  my $bjam = abs_path(glob("boost/b2"));
  chdir("src/doc");
  system("$bjam clean");
  system("rm -rf html");
  system("$bjam");
  chdir("../..");
  mkdir("doc");
  system("cp -vR src/doc/html/* doc");
}

sub make_asio_packages
{
  our $asio_name;
  system("./autogen.sh");
  system("./configure");
  system("make dist");
  system("tar tfz $asio_name.tar.gz | sed -e 's/^[^\\/]*//' | sort -df > asio.manifest");
}

sub build_boost_asio_doc
{
  my $cwd = getcwd();
  my $bjam = abs_path(glob("boost/b2"));
  chdir("boost/doc");
  system("rm -rf html/boost_asio");
  chdir("../libs/asio/doc");
  system("$bjam clean");
  system("$bjam asio");
  chdir($cwd);
}

our $boost_asio_readme = <<"EOF";
Copy the `boost', `doc' and `libs' directories into an existing boost
distribution.

Before using Boost.Asio, the Boost.System library needs to be built. This can
be done by running b2 in the libs/system directory. Consult the Boost Getting
Started page (http://www.boost.org/more/getting_started.html) for more
information on how to build the Boost libraries.
EOF

sub create_boost_asio_content
{
  # Create directory structure.
  system("rm -rf $boost_asio_name");
  mkdir("$boost_asio_name");
  mkdir("$boost_asio_name/doc");
  mkdir("$boost_asio_name/doc/html");
  mkdir("$boost_asio_name/boost");
  mkdir("$boost_asio_name/boost/config");
  mkdir("$boost_asio_name/libs");

  # Copy files.
  system("cp -vLR boost/doc/html/boost_asio.html $boost_asio_name/doc/html");
  system("cp -vLR boost/doc/html/boost_asio $boost_asio_name/doc/html");
  system("cp -vLR boost/boost/asio.hpp $boost_asio_name/boost");
  system("cp -vLR boost/boost/asio $boost_asio_name/boost");
  system("cp -vLR boost/boost/cerrno.hpp $boost_asio_name/boost");
  system("cp -vLR boost/boost/config/warning_disable.hpp $boost_asio_name/boost/config");
  system("cp -vLR boost/boost/system $boost_asio_name/boost");
  system("cp -vLR boost/libs/asio $boost_asio_name/libs");
  system("cp -vLR boost/libs/system $boost_asio_name/libs");

  # Remove modular boost include directories.
  system("rm -rf $boost_asio_name/libs/asio/include");
  system("rm -rf $boost_asio_name/libs/system/include");

  # Add dummy definitions of BOOST_SYMBOL* to boost/system/config.hpp.
  my $from = "$boost_asio_name/boost/system/config.hpp";
  my $to = "$boost_asio_name/boost/system/config.hpp.new";
  open(my $input, "<$from") or die("Can't open $from for reading");
  open(my $output, ">$to") or die("Can't open $to for writing");
  while (my $line = <$input>)
  {
    print($output $line);
    if ($line =~ /<boost\/config\.hpp>/)
    {
      print($output "\n// These #defines added by the separate Boost.Asio package.\n");
      print($output "#if !defined(BOOST_SYMBOL_IMPORT)\n");
      print($output "# if defined(BOOST_HAS_DECLSPEC)\n");
      print($output "#  define BOOST_SYMBOL_IMPORT __declspec(dllimport)\n");
      print($output "# else // defined(BOOST_HAS_DECLSPEC)\n");
      print($output "#  define BOOST_SYMBOL_IMPORT\n");
      print($output "# endif // defined(BOOST_HAS_DECLSPEC)\n");
      print($output "#endif // !defined(BOOST_SYMBOL_IMPORT)\n");
      print($output "#if !defined(BOOST_SYMBOL_EXPORT)\n");
      print($output "# if defined(BOOST_HAS_DECLSPEC)\n");
      print($output "#  define BOOST_SYMBOL_EXPORT __declspec(dllexport)\n");
      print($output "# else // defined(BOOST_HAS_DECLSPEC)\n");
      print($output "#  define BOOST_SYMBOL_EXPORT\n");
      print($output "# endif // defined(BOOST_HAS_DECLSPEC)\n");
      print($output "#endif // !defined(BOOST_SYMBOL_EXPORT)\n");
      print($output "#if !defined(BOOST_SYMBOL_VISIBLE)\n");
      print($output "# define BOOST_SYMBOL_VISIBLE\n");
      print($output "#endif // !defined(BOOST_SYMBOL_VISIBLE)\n\n");
    }
  }
  close($input);
  close($output);
  system("mv $to $from");

  # Create readme.
  $to = "$boost_asio_name/README.txt";
  open($output, ">$to") or die("Can't open $to for writing");
  print($output $boost_asio_readme);
  close($output);

  # Remove SVN and git files.
  system("find $boost_asio_name -name .svn -exec rm -rf {} \\;");
  system("find $boost_asio_name -name .git -exec rm -rf {} \\;");
  system("find $boost_asio_name -name .gitignore -exec rm -rf {} \\;");
  system("find $boost_asio_name -name .gitattributes -exec rm -rf {} \\;");
}

sub make_boost_asio_packages
{
  our $boost_asio_name;
  system("tar --format=pax -chf - $boost_asio_name | gzip -c >$boost_asio_name.tar.gz");
  system("tar --format=pax -chf - $boost_asio_name | bzip2 -9 -c >$boost_asio_name.tar.bz2");
  system("rm -f $boost_asio_name.zip");
  system("zip -rq $boost_asio_name.zip $boost_asio_name");
  system("rm -rf $boost_asio_name");
  system("tar tfz $boost_asio_name.tar.gz | sed -e 's/^[^\\/]*//' | sort -df > boost_asio.manifest");
}

(scalar(@ARGV) == 1) or print_usage_and_exit();
my $new_version = 1;
my $package_asio = 1;
my $package_boost = 1;
if ($ARGV[0] eq "--package-asio")
{
  $new_version = 0;
  $package_boost = 0;
}

if ($new_version)
{
  determine_version($ARGV[0]);
  update_configure_ac();
  update_readme();
  update_asio_version_hpp();
  update_boost_asio_version_hpp();
}
else
{
  determine_version_from_configure();
}

if ($package_asio)
{
  build_asio_doc();
  make_asio_packages();
}

if ($package_boost)
{
  build_boost_asio_doc();
  create_boost_asio_content();
  make_boost_asio_packages();
}
