# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.

use strict;
use ExtUtils::MakeMaker;
use Config;
use File::Spec::Functions qw(catfile catdir);
use Cwd;

my $cwd = getcwd;
my $libmd = catdir $cwd, 'libmd';

my $arch = $Config{archname};
my $is_win32 = ($arch =~ /MSWin32/i && $Config{cc} eq 'cl');
my $is_387 = ($arch =~ /^i\d+-linux/i);
my $is_sol = ($arch =~ /sun4-solaris|sparc/i);
my $is_dar = ($arch =~ /darwin/i);
my $is_cyg = ($arch =~ /cygwin/i || ($arch =~ /MSWin32/i && !$is_win32));
my $is_vms = ($arch =~ /vms/i);
my $dmake  = ($Config{make} eq 'dmake');

######################################
# for dmake, fool WriteMakefile into thinking there's a libmd lib
if ($dmake) {
    my $lib = 'libmd' . $Config{lib_ext};
    my $dummy = catfile $libmd, $lib;
    open(DUMMY, ">$dummy") or die "Cannot create $dummy: $!";
    close DUMMY;
}
######################################

my @clean = qw(libmd/setprec.c libmd/mconf.h libmd/sqrt.c);

my %opts = (
	    NAME => 'Math::Cephes',
            MYEXTLIB => "$libmd/libmd\$(LIB_EXT)",
	    VERSION_FROM => 'lib/Math/Cephes.pm',
            OBJECT => 'Cephes_wrap.o arrays.o',
            INC => "-I$libmd",
	    EXE_FILES => [ 'pmath' ],
	    dist   => {
			 SUFFIX   => 'gz',
			 COMPRESS => 'gzip -9f',
			},
	    clean => { FILES => "@clean"},
	   );

my $eu_version = $ExtUtils::MakeMaker::VERSION;
if ($eu_version >= 5.43) {
   $opts{ABSTRACT} = 'Perl interface to the math cephes library';
   $opts{AUTHOR} = 'Randy Kobes <r.kobes@uwinnipeg.ca>';
   $opts{CAPI} = 'TRUE' if $arch =~ /-object\b/i;
}
if ($eu_version > 6.11) {
   $opts{NO_META} = 1;
}

my $mconf = catfile $libmd, 'mconf.h';
my %defs = (HAVE_LONG_DOUBLE => 'd_longdbl',
            SIZEOF_INT => 'intsize',
            RETSIGTYPE => 'd_voidsig',
            HAVE_MALLOC_H => 'i_malloc',
            HAVE_STRING_H => 'i_string',
            VOLATILE => 'd_volatile',
           );
my $vals = {RETSIGTYPE => {define => 'void', undef => 'int'},
            VOLATILE => {define => 'volatile', undef => ''},
           };

if ($is_win32 or $is_387 or $is_cyg) {
    $defs{IBMPC} = 1;
}
elsif ($is_sol) {
    $defs{WORDS_BIGENDIAN} = 1;
    $defs{FLOAT_WORDS_BIGENDIAN} = 1;
    $defs{UNK} = 1;
}
elsif ($is_dar) {
    require POSIX;
    my $machine = (POSIX::uname())[4];
    if ($machine eq 'i386') {
        $defs{IBMPC} = 1;
    }
    else {
      $defs{WORDS_BIGENDIAN} = 1;
      $defs{FLOAT_WORDS_BIGENDIAN} = 1;
      $defs{MIEEE} = 1;
    }
}
elsif ($is_vms) {
    $defs{DEC} = 1;
}
else {
    $defs{UNK} = 1;
}
my $match = join '|', keys %defs;
open(MCONF, ">$mconf") or die "Cannot open $mconf: $!";
while (my $line = <DATA>) {
    if ($line =~ /\#define\s+($match)/) {
        my $def = $1;
        print MCONF fix_mconf($def, $defs{$def});
    }
    elsif ($line =~ /^\#define XPD/) {
        my $lds = $Config{longdblsize};
        my $xpd = ($is_387 and $lds and $lds == 12) ? '0,' : '';
        print MCONF qq{\#define XPD $xpd\n};
    }
    else {
        print MCONF $line;
    }
}
close MCONF;


WriteMakefile(%opts);

my $message;

if ($is_sol or $is_dar or $is_win32 or $is_387) {
  $message = <<"END";

A file libmd/mconf.h, which contains machine-dependent
definitions, has been used which is known to work with some 
versions of $arch. If there are problems with 
the tests, some manual editing of this file may be needed.

END
}

else {
    $message = <<'END';

The file libmd/mconf.h, which contains machine-dependent
definitions, may require some manual editing for your platform.

END
}

print $message;

sub fix_mconf {
    my ($what, $key) = @_;
    my $lookup = $Config{$key} ? $Config{$key} :
        ( ($key == 1) ? 'define' : 'undef' );
    my $string;
    my $val = $vals->{$what}->{$lookup};
    if ($lookup eq 'define') {
        $string = defined $val ?
            qq{\#define $what $val\n} : qq{\#define $what 1\n};
    }
    elsif ($lookup eq 'undef') {
        $string = defined $val ?
            qq{\#define $what $val\n} :
                qq{\#ifdef $what\n\#undef $what\n\#endif\n};
    }
    else {
        $string = qq{\#define $what $lookup\n};
    }
    return $string;
}

sub MY::postamble {
    my $postamble = '';

    if ($is_win32 && Win32::IsWin95()) {
        if ($Config{'make'} =~ /dmake/i) {
	        # dmake-specific
            $postamble .= <<"EOT";
\$(MYEXTLIB): $libmd/Makefile
\@[
	cd $libmd
	\$(MAKE) static
	cd ..
]
EOT
	    } 
        elsif ($Config{'make'} =~ /nmake/i) {
	        #
            $postamble .= <<"EOT";
\$(MYEXTLIB): $libmd/Makefile
	cd $libmd
	\$(MAKE) static
	cd ..
EOT
           }
        }
    elsif ($^O ne 'VMS') {
        $postamble .= <<"EOT";

\$(MYEXTLIB): $libmd/Makefile
	cd $libmd && \$(MAKE) static
EOT
        }
    else {
        $postamble .= <<"EOT";

\$(MYEXTLIB) : ${libmd}descrip.mms
	set def $libmd
    $(MMS) static
    set def [-]
EOT
    }

    return $postamble;
}

__DATA__

/*
Cephes Math Library Release 2.3:  June, 1995
Copyright 1984, 1987, 1989, 1995 by Stephen L. Moshier
*/

/* Define if the `long double' type works.  */
#define HAVE_LONG_DOUBLE 1

/* Define as the return type of signal handlers (int or void).  */
#define RETSIGTYPE void

/* Define if you have the ANSI C header files.  */
#define STDC_HEADERS 1

/* Define if your processor stores words with the most significant
   byte first (like Motorola and SPARC, unlike Intel and VAX).  */
/* #define WORDS_BIGENDIAN */

/* Define if floating point words are bigendian.  */
/* #define FLOAT_WORDS_BIGENDIAN */

/* The number of bytes in a int.  */
#define SIZEOF_INT 4

/* Define if you have the <malloc.h> header file.  */
#define HAVE_MALLOC_H 1

/* Define if you have the <string.h> header file.  */
#define HAVE_STRING_H 1

/* Name of package */
#define PACKAGE "cephes"

/* Version number of package */
#define VERSION_CEPHES "2.7"

/* Constant definitions for math error conditions
 */

#define DOMAIN		1	/* argument domain error */
#define SING		2	/* argument singularity */
#define OVERFLOW	3	/* overflow range error */
#define UNDERFLOW	4	/* underflow range error */
#define TLOSS		5	/* total loss of precision */
#define PLOSS		6	/* partial loss of precision */

#define EDOM		33
#define ERANGE		34
/* Complex numeral.  */
typedef struct
	{
	double r;
	double i;
	} cmplx;

#ifdef HAVE_LONG_DOUBLE
/* Long double complex numeral.  */
typedef struct
	{
	long double r;
	long double i;
	} cmplxl;
#endif

/* Type of computer arithmetic */

/* PDP-11, Pro350, VAX:
 */
/* #define DEC 1 */

/* Intel IEEE, low order words come first:
 */
/* #define IBMPC 1 */

/* Motorola IEEE, high order words come first
 * (Sun 680x0 workstation):
 */
/* #define MIEEE 1 */

/* UNKnown arithmetic, invokes coefficients given in
 * normal decimal format.  Beware of range boundary
 * problems (MACHEP, MAXLOG, etc. in const.c) and
 * roundoff problems in pow.c:
 * (Sun SPARCstation)
 */
/* #define UNK 1 */

/* If you define UNK, then be sure to set BIGENDIAN properly. */
#ifdef FLOAT_WORDS_BIGENDIAN
#define BIGENDIAN 1
#else
#define BIGENDIAN 0
#endif

/* Define this `volatile' if your compiler thinks
 * that floating point arithmetic obeys the associative
 * and distributive laws.  It will defeat some optimizations
 * (but probably not enough of them).
 *
 */
/* #define VOLATILE volatile */

/* For 12-byte long doubles on an i386, pad a 16-bit short 0
 * to the end of real constants initialized by integer arrays.
 *
 * #define XPD 0,
 *
 * Otherwise, the type is 10 bytes long and XPD should be
 * defined blank (e.g., Microsoft C).
 *
 * #define XPD
 */
#define XPD 0,

/* Define to support tiny denormal numbers, else undefine. */
#define DENORMAL 1

/* Define to ask for infinity support, else undefine. */
#define INFINITIES 1

/* Define to ask for support of numbers that are Not-a-Number,
   else undefine.  This may automatically define INFINITIES in some files. */
#define NANS 1

/* Define to distinguish between -0.0 and +0.0.  */
#define MINUSZERO 1

/* Define 1 for ANSI C atan2() function
   See atan.c and clog.c. */
#define ANSIC 1

/* Get ANSI function prototypes, if you want them. */
#ifdef __STDC__
#define ANSIPROT
/* #include "protos.h" */
int mtherr();
#else
int mtherr();
#endif

/* Variable for error reporting.  See mtherr.c.  */
extern int merror;