#!/bin/bash
#
# Tvheadend configure script
#
# Copyright (c) 2012 Adam Sutton <dev@adamsutton.me.uk>
#

# ###########################################################################
# Setup
# ###########################################################################

ROOTDIR=$(cd "$(dirname "$0")"; pwd)
test -z "$PKG_CONFIG" && PKG_CONFIG=pkg-config

#
# Options
#

OPTIONS=(
  "pie:yes"
  "ccdebug:no"
  "cwc:yes"
  "capmt:yes"
  "constcw:yes"
  "linuxdvb:yes"
  "satip_server:yes"
  "satip_client:yes"
  "hdhomerun_client:no"
  "hdhomerun_static:yes"
  "iptv:yes"
  "tsfile:yes"
  "dvbscan:yes"
  "timeshift:yes"
  "trace:yes"
  "imagecache:auto"
  "avahi:auto"
  "zlib:auto"
  "libav:auto"
  "ffmpeg_static:yes"
  "libx264:yes"
  "libx264_static:yes"
  "libx265:yes"
  "libx265_static:yes"
  "libvpx:yes"
  "libvpx_static:yes"
  "libtheora:yes"
  "libtheora_static:yes"
  "libvorbis:yes"
  "libvorbis_static:yes"
  "libfdkaac:yes"
  "libfdkaac_static:yes"
  "nvenc:auto"
  "qsv:no"
  "libmfx_static:yes"
  "inotify:auto"
  "epoll:auto"
  "uriparser:auto"
  "ccache:auto"
  "tvhcsa:auto"
  "bundle:no"
  "pngquant:no"
  "dvbcsa:no"
  "dvben50221:auto"
  "kqueue:no"
  "dbus_1:auto"
  "android:no"
  "tsdebug:no"
  "gtimer_check:no"
  "slow_memoryinfo:no"
  "libsystemd_daemon:no"
  "pcloud_cache:yes"
)

#
# Begin
#

. "$ROOTDIR/support/configure.inc"

case "$ROOTDIR" in
  *\ * ) die "The source directory contains a space. It is not supported."
esac

# ###########################################################################
# Parse arguments
# ###########################################################################
opt=
val=
for opt do
  val=${opt#*=}
  opt=${opt%=*}
  opt=${opt#*--}
  case "$opt" in
    help)
      show_help
      ;;
    *dir|prefix)
      eval "$opt=$val"
      ;;
    gzip)
      eval "$(toupper ${opt}CMD)=\"$val\""
      ;;
    cc|cflags|ldflags|arch|cpu|platform|python|bzip2|nowerror)
      eval "$(toupper $opt)=\"$val\""
      ;;
    enable-*)
      opt=${opt#*-}
      enable $opt 1
      ;;
    disable-*)
      opt=${opt#*-}
      disable $opt 1
      ;;
  esac
done

# ###########################################################################
# Checks
# ###########################################################################

echo "Checking support/features"

#
# Compiler
#

# Use ccache
if enabled_or_auto ccache; then
  which ccache &> /dev/null
  if [ $? -eq 0 ]; then
    echo "$CC" | grep -q ccache || CC="ccache $CC"
    enable ccache
  elif enabled ccache; then
    die "ccache not found, try --disable-ccache"
  fi
fi

# Valiate compiler
check_cc || die 'No C compiler found'
# We only want libunwind on non-linux plaforms where we have had bad
# stack traces from existing implementation.
if [ ${PLATFORM} = "freebsd" ]; then
    check_cc_header libunwind
    # If we don't have libunwind then fallback to execinfo.
    if ! enabled libunwind
    then
        check_cc_header execinfo
    fi

else
    check_cc_header execinfo
fi
check_cc_option mmx
check_cc_option sse2
check_cc_optionW unused-result

if check_cc '
#if !defined(__clang__)
#error this is not clang
#endif
'; then
  COMPILER=clang
else
  COMPILER=gcc
fi

check_cc_snippet strlcat '#include <string.h>
int test(int argc, char **argv) {
  char dst[10];
  strlcat("test", dst, sizeof(dst));
  return 0;
}'

check_cc_snippet strlcpy '#include <string.h>
int test(int argc, char **argv) {
  char dst[10];
  strlcpy("test", dst, sizeof(dst));
  return 0;
}'

check_cc_snippet fdatasync '#include <unistd.h>
int test(int argc, char **argv) {
  fdatasync(0);
  return 0;
}'

check_cc_snippet getloadavg '#include <stdlib.h>
void test() { getloadavg(NULL,0); }'

check_cc_snippet atomic32 '#include <stdint.h>
int test(int *ptr){
return __sync_fetch_and_add(ptr, 1);
}'

check_cc_snippet atomic64 '#include <stdint.h>
uint64_t test(uint64_t *ptr){
return __sync_fetch_and_add(ptr, 1);
}'

check_cc_snippet atomic_time_t '#include <stdint.h>
#include <time.h>
uint64_t test(time_t *ptr){
return __sync_fetch_and_add(ptr, 1);
}'

check_cc_snippet bitops64 '#include <stdint.h>
int test(void){
  int l = sizeof(long);
  return l == 8 ? 0 : 1;
}'

check_cc_snippet lockowner '
#include <sys/syscall.h>
#include <unistd.h>
#include <pthread.h>
#define TEST test
int ok = 1;
void *lockowner ( void *p)
{
  pthread_mutex_t lock;
  pthread_mutex_init(&lock, NULL);
  pthread_mutex_lock(&lock);
  if (lock.__data.__owner == syscall(SYS_gettid))
    ok = 0;
  return NULL;
}
int test ( void )
{
  pthread_t tid;
  pthread_create(&tid, NULL, lockowner, NULL);
  pthread_join(tid, NULL);
  return ok;
}' -lpthread

check_cc_snippet qsort_r '
#define __USE_GNU
#include <stdlib.h>
#define TEST test
int test(void)
{
  qsort_r(NULL, 0, 0, NULL, NULL);
  return 0;
}
'

check_cc_snippet stime '
#include <time.h>
#define TEST test
int test(void)
{
  time_t t = 1;
  stime(&t);
  return 0;
}
'

check_cc_snippet gmtoff '
#include <time.h>
#define TEST test
int test(void)
{
  struct tm x;
  x.tm_gmtoff = 0;
  return 0;
}
' -DHAS_GMTOFF

check_cc_snippet recvmmsg '
#define _GNU_SOURCE
#include <stdlib.h>
#include <sys/socket.h>
#define TEST test
int test(void)
{
  recvmmsg(0, NULL, 0, 0, NULL);
  return 0;
}
'

check_cc_snippet sendmmsg '
#define _GNU_SOURCE
#include <stdlib.h>
#include <sys/socket.h>
#define TEST test
int test(void)
{
  sendmmsg(0, NULL, 0, 0);
  return 0;
}
'

# a check for the external iconv library
# for build-in libc iconv routines this check should fail
# note that iconv routines are mandatory
check_cc_snippet libiconv '
#include <iconv.h>
int test(void)
{
  iconv_t ic = iconv_open("ASCII", "ASCII");
  return 0;
}
' -liconv

if enabled libiconv; then
  printf "    ^ using external iconv library\n"
else
  printf "    ^ using build-in glibc iconv routines\n"
fi

if enabled_or_auto dvben50221; then
  check_cc_snippet libdvben50221 '
  #include <libdvben50221/en50221_session.h>
  #define TEST test
  int test(void)
  {
    struct en50221_transport_layer *tl = en50221_tl_create(5, 32);
    return 0;
  }
  ' '-ldvben50221 -ldvbapi -lucsi'
  if enabled libdvben50221; then
    enable dvben50221
  else
    disable dvben50221
  fi
fi

check_cc_snippet ifnames '
#include <net/if.h>
int test(void)
{
  struct if_nameindex *ifnames = if_nameindex();
  if_freenameindex(ifnames);
  return 0;
}
'

#
# Python
#
check_py || echo 'WARN: no python binary found'
check_py_import gzip

#
# Binaries
#
check_bin ${PKG_CONFIG} bin_pkg_config || die 'no pkg-config binaries found'
check_bin ${XGETTEXT} || die 'no gettext binaries found'
check_bin ${MSGMERGE} || die 'no gettext binaries found'
check_bin ${GZIPCMD} || die 'no gzip binary found'
check_bin ${BZIP2} || echo 'WARN: no bzip2 binary found'

#
# SSL
#
if [ ${PLATFORM} = "freebsd" ]; then
  # FreeBSD has libssl in base
  enable ssl
elif check_pkg openssl || check_pkg libssl; then
  enable ssl
elif check_cc_header 'openssl/ssl' ssl; then
  enable ssl
else
  die "SSL development support not found"
fi

#
# OS X
#
if [ ${PLATFORM} = "darwin" ]; then
  disable linuxdvb
  disable avahi
  enable bonjour
fi

#
# DVB API
#
check_cc_header 'linux/dvb/version' linuxdvbapi
if enabled_or_auto linuxdvb; then
  if enabled linuxdvbapi; then
    enable linuxdvb
  elif enabled linuxdvb; then
    die "Linux DVB API not found (use --disable-linuxdvb)"
  fi
fi

#
# HDHomeRun - libhdhomerun
#
if enabled hdhomerun_static; then

  enable hdhomerun_client

else

  if enabled_or_auto hdhomerun_client; then
    if check_cc_header 'libhdhomerun/hdhomerun' libhdhomerun; then
      enable  hdhomerun_client
      LDFLAGS="$LDFLAGS -lhdhomerun"
    fi
  fi

fi

if enabled libunwind; then
    LDFLAGS="$LDFLAGS -lunwind"
fi
#
# Gzip
#
if enabled_or_auto zlib; then
  if check_pkg zlib; then
    enable zlib
  elif enabled zlib; then
    die "Zlib development support not found (use --disable-zlib)"
  fi
fi

#
# SAT>IP server
#
if enabled_or_auto satip_server; then
  enable upnp
fi

#
# SAT>IP client
#
if enabled_or_auto satip_client; then
  enable upnp
fi

#
# uriparser
#
if enabled_or_auto uriparser; then
  if check_pkg liburiparser; then
    enable uriparser
  elif enabled uriparser; then
    die "liburiparser development support not found (use --disable-uriparser)"
  fi
fi

#
# Bundling
#
if enabled bundle; then
  if enabled zlib && ! enabled py_gzip; then
    die "Python gzip module not found (use --disable-zlib or --disable-bundle)"
  fi
fi

#
# Avahi
#
if enabled_or_auto avahi; then
  if check_pkg avahi-client; then
    enable avahi
  elif enabled avahi; then
    die "Avahi development support not found (use --disable-avahi)"
  fi
fi

#
# ffmpeg
#
if enabled ffmpeg_static; then

  enable libav
  has_libav=true

  # libx264
  if enabled libx264; then
    if disabled libx264_static; then
      check_pkg x264 ">=0.142" || die "x264 package not found"
    fi
  else
    disable libx264_static
  fi

  # libx265
  if enabled libx265; then
    if disabled libx265_static; then
      check_pkg x265 ">=1.5" || die "x265 package not found"
    else
      check_bin cmake || die "cmake not found"
      check_cc_lib stdc++ stdcpp || die "libstdc++ not found"
    fi
  else
    disable libx265_static
  fi

  # libvpx
  if enabled libvpx; then
    if disabled libvpx_static; then
      check_pkg vpx ">=1.3.0" || die "vpx package not found"
    fi
  else
    disable libvpx_static
  fi

  # libtheora
  if enabled libtheora; then
    if disabled libtheora_static; then
      check_pkg theoraenc ">=1.1.1" || die "theoraenc package not found"
      check_pkg theoradec ">=1.1.1" || die "theoradec package not found"
      check_pkg theora    ">=1.1.1" || die "theora package not found"
    else
      enable libogg_static
    fi
  else
    disable libtheora_static
  fi

  # libvorbis
  if enabled libvorbis; then
    if disabled libvorbis_static; then
      check_pkg vorbisfile ">=1.3.4" || die "vorbisfile package not found"
      check_pkg vorbisenc  ">=1.3.4" || die "vorbisenc package not found"
      check_pkg vorbis     ">=1.3.4" || die "vorbis package not found"
    else
      enable libogg_static
    fi
  else
    disable libvorbis_static
  fi

  # libfdk-aac
  if enabled libfdkaac; then
    if disabled libfdkaac_static; then
      check_pkg fdk-aac ">=0.1.3" || die "fdk-aac package not found"
    fi
  else
    disable libfdkaac_static
  fi

  # nvenc
  # latest ffmpeg has own code
  if enabled_or_auto nvenc; then
    #check_cc_header nvEncodeAPI || \
    #  die "NVENC library (https://developer.nvidia.com/nvidia-video-codec-sdk) not found"
    enable nvenc
  else
    disable nvenc
  fi

  # qsv
  if enabled qsv; then
    if disabled libmfx_static; then
      check_pkg libmfx ">=1.16" || die "libmfx package not found"
    else
      check_bin autoreconf       || die "autoreconf not found"
      check_bin libtool          || die "libtool not found"
      check_bin libtoolize       || die "libtoolize not found"
      check_cc_lib stdc++ stdcpp || die "libstdc++ not found"
    fi
    enable vaapi
  else
    disable libmfx_static
  fi

  # vaapi
  if enabled vaapi; then
    check_pkg libva ">=0.38.0"     || \
      die "vaapi (Video Acceleration (VA) API for Linux) not found"
    check_pkg libva-x11 ">=0.38.0" || die "libva-x11 not found"
    check_pkg libva-drm ">=0.38.0" || die "libva-drm not found"
  fi

else

  if enabled_or_auto libav; then
    has_libav=true
    has_resample=false

    check_pkg libavfilter   ">=6.31.100"  || has_libav=false
    check_pkg libswresample ">=2.0.101"   && has_resample=true
    check_pkg libavresample ">=3.0.0"     && has_resample=true
    check_pkg libswscale    ">=4.0.100"   || has_libav=false
    check_pkg libavformat   ">=57.25.100" || has_libav=false
    check_pkg libavcodec    ">=57.24.102" || has_libav=false
    check_pkg libavutil     ">=55.17.103" || has_libav=false

    if $has_libav -a $has_resample; then
      enable libav
    else
      echo "WARNING: none or old ffmpeg libraries were detected"
      echo "         * use --disable-libav or --enable-ffmpeg_static"
      if enabled libav; then
        die "ffmpeg development support not found"
      fi
    fi

  fi

fi

#
# Inotify
#
if enabled_or_auto inotify; then
  if check_cc_header "sys/inotify" inotify_h; then
    enable inotify
    check_cc_snippet inotify_init1 '
    #include <sys/inotify.h>
    int test(void)
    {
      inotify_init1(IN_CLOEXEC);
      return 0;
    }
    '
  elif enabled inotify; then
    die "Inotify support not found (use --disable-inotify)"
  fi
fi

#
# libdvbcsa, tvhcsa
#
if enabled cwc || enabled capmt || enabled constcw; then
  enable tvhcsa
  if enabled dvbcsa; then
    (check_cc_header "dvbcsa/dvbcsa" dvbcsa_h &&\
     check_cc_lib    dvbcsa dvbcsa_l) ||\
    die "Failed to find dvbcsa support (use --disable-dvbcsa)"
    LDFLAGS="$LDFLAGS -ldvbcsa"
  fi
fi

#
# libdvben50221
#
if enabled linuxdvb; then
  if enabled libdvben50221; then
    LDFLAGS="$LDFLAGS -ldvben50221 -ldvbapi -lucsi"
    enable linuxdvb_ca
  fi
fi

#
# Icon caching
#
if enabled_or_auto imagecache; then
  enable imagecache
fi

#
# DVB scan
#
if enabled dvbscan; then
  printf "${TAB}" "fetching dvb-scan files ..."
  "${ROOTDIR}/support/getmuxlist"
  if [ $? -ne 0 ]; then
    echo "fail"
    check_bin git || echo "FATAL: git binary not found (install the git package)"
    die "Failed to fetch dvb-scan data (use --disable-dvbscan)"
  fi
  echo "ok"
fi

#
# epoll
#
if [ ${PLATFORM} = "linux" ]; then
  enable epoll
  check_cc_snippet epoll_create1 '
  #include <sys/epoll.h>
  int test(void)
  {
    epoll_create1(EPOLL_CLOEXEC);
    return 0;
  }
  '
fi

#
# kqueue
#
if [ ${PLATFORM} = "freebsd" ] || [ ${PLATFORM} = "darwin" ]; then
  enable kqueue
fi

#
# MPEGTS support
#
disable mpegts
disable mpegts_dvb
if enabled linuxdvb || enabled iptv || enabled tsfile || enabled satip_client || \
   enabled hdhomerun_client || enabled satip_server;
then
  enable mpegts
fi
if enabled linuxdvb || enabled satip_client || enabled hdhomerun_client || enabled satip_server; then
  enable mpegts_dvb
fi

#
# DBus
#
if enabled_or_auto dbus_1; then
  if check_pkg dbus-1; then
    enable dbus_1
  elif enabled dbus-1; then
    die "DBus-1 development support not found (use --disable-dbus-1)"
  fi
fi

#
# TSDebug
#
if enabled_or_auto tsdebug; then
  enable mpegts_dvb
fi

#
# systemd
#
if enabled_or_auto libsystemd_daemon; then
  if check_pkg libsystemd-daemon || check_pkg libsystemd; then
    enable libsystemd_daemon
  elif enabled libsystemd_daemon; then
    die "libsystemd-daemon development support not found (use --disable-systemd_daemon)"
  fi
fi

# ###########################################################################
# Write config
# ###########################################################################

# Write config
write_config
cat >> "${CONFIG_H}" <<EOF
#define TVHEADEND_DATADIR "$(eval echo ${datadir})/tvheadend"
EOF

# Output config
print_config
echo "Final Binary:"
echo "  $BUILDDIR/tvheadend"
echo ""
echo "Tvheadend Data Directory:"
echo "  $(eval echo ${datadir}/tvheadend)"
echo ""
