#!/bin/bash -p
###############################################################################
# BRLTTY - A background process providing access to the console screen (when in
#          text mode) for a blind person using a refreshable braille display.
#
# Copyright (C) 1995-2016 by The BRLTTY Developers.
#
# BRLTTY comes with ABSOLUTELY NO WARRANTY.
#
# This is free software, placed under the terms of the
# GNU General Public License, as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any
# later version. Please see the file LICENSE-GPL for details.
#
# Web Page: http://brltty.com/
#
# This software is maintained by Dave Mielke <dave@mielke.cc>.
###############################################################################

set -e
umask 022
export MAKEFLAGS=

copyProperties() {
   local from="${1}"
   local to="${2}"
   shift 2

   for suffix in ${*}
   do
      setVariable "${to}${suffix}" "$(getVariable "${from}${suffix}")"
   done
}

installFile() {
   local source="${1}"
   local target="${2}"

   [ "${target%/}" = "${target}" ] || target="${target}${source##*/}"

   if [ -e "${source}" ]
   then
      local path="${installRoot}/${target}"
      mkdir -p "${path%/*}"
      cp "${source}" "${path}"
   else
      logMessage warning "file not installed: ${target}"
   fi
}

installFiles() {
   while [ "${#}" -gt 0 ]
   do
      "installFiles_${1}"
      shift 1
   done
}

installFiles_libusb0() {
   if [ -n "${libusb0Root}" ]
   then
      cd "${libusb0Root}/bin"
      installFile "x86/libusb0.sys" "bin/libusb0.sys"
      installFile "x86/libusb0_x86.dll" "bin/libusb0.dll"
      installFile "amd64/libusb0.sys" "bin/libusb0_x64.sys"
      installFile "amd64/libusb0.dll" "bin/libusb0_x64.dll"
   else
      logMessage warning "${libusb0Name} not installable"
   fi
}

installFiles_libusb1() {
   if [ -n "${libusb1Root}" ]
   then
      cd "${libusb1Root}"
      installFile "MinGW32/dll/libusb-1.0.dll" "bin/"
   else
      logMessage warning "${libusb1Name} not installable"
   fi
}

installFiles_winusb() {
   if [ -n "${winusbRoot}" ]
   then
      cd "${winusbRoot}"
      installFile "x86/winusbcoinstaller2.dll" "bin/x86/"
      installFile "amd64/winusbcoinstaller2.dll" "bin/amd64/"
      installFile "x86/WdfCoInstaller01009.dll" "bin/x86/"
      installFile "amd64/WdfCoInstaller01009.dll" "bin/amd64/"
   else
      logMessage warning "${winusbName} not installable"
   fi
}

. "`dirname "${0}"`/../prologue.sh"
. "${programDirectory}/mingw.sh"

defaultUsbPackage="libusb"
defaultTemporaryDirectory="${TMPDIR:-/tmp}/brltty-${programName}"

addProgramOption u string.package usbPackage "which USB package to use" "${defaultUsbPackage}"
addProgramOption s flag invokeShell "invoke interactive shell to inspect/modify result"
addProgramOption t string.directory temporaryDirectory "the temporary directory to use for the build" "${defaultTemporaryDirectory}"
addProgramOption k flag keepBuild "keep (do not remove) the temporary directory"
addWindowsPackageOption M msvc
addWindowsPackageOption A ahk
addWindowsPackageOption N nsis
addWindowsPackageOption U libusb0
addWindowsPackageOption X libusb1
addWindowsPackageOption W winusb
addWindowsPackageOption P python
addWindowsPackageOption I icu
addProgramOption C string.directory cygwinRoot "the root directory of a Cygwin installation"
addProgramParameter source sourceRoot "the top-level directory of the source tree"
addProgramParameter revision buildRevision "the revision of the build"
parseProgramArguments "${@}"

sourceRoot="$(cd "${sourceRoot}" && pwd -W)"
[ -f "${sourceRoot}/configure" ] || semanticError "not a source tree: ${sourceRoot}"

revisionIdentifier="$("${sourceRoot}/getrevid" "${sourceRoot}")"
logMessage task "revision identifier: ${revisionIdentifier}"

[ -z "${cygwinRoot}" ] || {
   if [ -d "${cygwinRoot}/cygdrive" ]
   then
      export PATH="${PATH}:${cygwinRoot}/bin"
      export nodosfilewarning=1
   else
      logMessage warning "not a Cygwin root directory: ${cygwinRoot}"
   fi
}

[ -n "${usbPackage}" ] || usbPackage="${defaultUsbPackage}"

case "${usbPackage}"
in
   libusb) usbWindowsPackages="libusb0";;
   libusb-1.0) usbWindowsPackages="libusb1 winusb";;
   *) syntaxError "unrecognized USB package: ${usbPackage}"
esac

libusb0Filter="\
    select "USB:", and install ${libusb0Name}'s filter by doing one of the
    following:
    + run Install ${libusb0Name} Filter from the Start Menu
    + answer yes to the setup prompt for installing the ${libusb0Name} filter
    + install it by hand, from ${libusb0Download}"

libusb1Filter="\
    you will need to use ${libusb0Name} instead because
    ${libusb1Name} does not support this requirement."

set -- ${usbWindowsPackages}
copyProperties "${1}" usb Name Location Download Version Pattern Filter

[ -z "${msvcRoot}" ] || {
   if verifyWindowsPackage msvc
   then
      export PATH="${msvcRoot}:${PATH}"
   fi
}

[ -z "${pythonRoot}" ] || {
   if verifyWindowsPackage python
   then
      export PATH="${pythonRoot}:${pythonRoot}/Scripts:${PATH}"
   fi
}

verifyWindowsPackages ahk nsis icu ${usbWindowsPackages} || :
verifyWindowsCommands lib python cython || :
verifyMingwPackages pthreads curses || :
verifyMingwCommands zip unix2dos linuxdoc doxygen groff || :

if [ -z "${temporaryDirectory}" ]
then
   temporaryDirectory="${defaultTemporaryDirectory}"
   rm -f -r "${temporaryDirectory}"
elif [ -e "${temporaryDirectory}" ]
then
   semanticError "directory already exists: ${temporaryDirectory}"
fi

mkdir -p "${temporaryDirectory}"
cd "${temporaryDirectory}"
temporaryDirectory="$(pwd -W)"

logMessage task "copying source tree"
newSourceRoot="${temporaryDirectory}/source"
cp -a "${sourceRoot}" "${newSourceRoot}"
cd "${newSourceRoot}"
sourceRoot="${newSourceRoot}"

logMessage task "applying patches"
for patch in affinity
do
   patch -p0 <"Windows/${patch}.patch"
done

buildRoot="${temporaryDirectory}/build"
mkdir -p "${buildRoot}"
cd "${buildRoot}"

logMessage task "configuring build"
configureOptions="--enable-relocatable-install --with-usb-package="${usbPackage}" --disable-i18n --without-params-package --without-mntpt-package --without-mntfs-package --without-kbd-package --without-bell-package --without-leds-package --without-libbraille --disable-speech-support --disable-java-bindings --disable-ocaml-bindings --disable-tcl-bindings"
"../source/configure" --quiet --prefix=/ ${configureOptions}

. ./config.sh
buildVersion="${BRLTTY_VERSION}-${buildRevision}"
buildName="${BRLTTY_TARNAME}-win-${buildVersion}-${usbPackage}"

logMessage task "building programs"
make -s
make -s -C Drivers/BrlAPI/WindowEyes we-dynamic-library-windows

logMessage task "building documents"
make -s -C Documents

logMessage task "installing files"
installRoot="${temporaryDirectory}/install/${buildName}"
make -s install \
   JAVA_JAR_DIR=/lib JAVA_JNI_DIR=/lib \
   PYTHON_PREFIX="Python" \
   INSTALL_ROOT="${installRoot}"

logMessage task "updating files"
documentDirectory="doc"

configureLines=""
eval set -- $(stringWrapped "${configureOptions}" 70)
for line
do
   [ -z "${configureLines}" ] || configureLines="${configureLines}"$'\n'
   configureLines="${configureLines}    ${line}"
done

readmeHeader="\
This is an unofficial build of ${BRLTTY_NAME} ${BRLTTY_VERSION} which also includes a few fixes
for Windows.

You should probably read doc/BRLTTY.txt and doc/Windows.txt for information
on BRLTTY.

Here are some notes on how to get started:

- BRLTTY only gives access to text consoles. For the rest of the Windows
  environment, you need to also install and run NVDA.
- Either use the BRLTTY configurator (brlttycnf.exe) or manually uncomment the
  appropriate lines in etc/brltty.conf.
- For Python support (e.g. for NVDA), run Brlapi-${BRLAPI_RELEASE}.win32.exe.
- For sighted users, use the xw braille driver to get a virtual braille box.
"

readmeFooter="\

If you are having problems, please run run-debug.bat and send us the debug.log
and brltty.conf files.

Documentation can be found in the doc/ subdirectory.

==============================
Technical notes on this build:

- Source Revision: ${revisionIdentifier}
- BRLTTY Version: ${BRLTTY_VERSION}
- BrlAPI Version: ${BRLAPI_RELEASE}
- Some MinGW-specific path and file name changes have been made.
- The configure options were:
${configureLines}
- To make life easier for Windows users, the BrlAPI server was modified to:
  * not use authentication by default (BRLAPI_DEFAUTH set to none)
  * only use local sockets (:0+127.0.0.1:0 changed to :0)
- *${usbPattern}* files come from ${usbName} ${usbVersion}, which can be found at:
  ${usbDownload}
- Python bindings are provided by: Brlapi-${BRLAPI_RELEASE}.win32.exe
- C bindings are provided in: include/, and lib/
  A .lib file is provided for linking in (for example) Visual C. Then you can
  just ship bin/brlapi${BRLAPI_RELEASE%.*}.dll alongside your .exe application.
"

cd "${buildRoot}"
installFile "Documents/brltty.conf" "etc/brltty.conf"
installFile "Drivers/BrlAPI/WindowEyes/webrloem109.dll" "WindowEyes/"
installFile "Bindings/Python/dist/Brlapi-${BRLAPI_RELEASE}.win32.exe" "Python/"

cd "${sourceRoot}"
installFile "LICENSE-GPL" "LICENSE-GPL.txt"
installFile "LICENSE-LGPL" "LICENSE-LGPL.txt"
installFile "README" "${documentDirectory}/BRLTTY.txt"
installFile "Drivers/Braille/XWindow/UBraille.ttf" "Fonts/"
installFile "Drivers/BrlAPI/WindowEyes/README" "${documentDirectory}/WindowEyes.txt"
installFile "Autostart/Windows/brltty-${usbPackage}.inf" "bin/"

for document in ChangeLog HISTORY TODO
do
   installFile "Documents/${document}" "${documentDirectory}/${document}.txt"
done

for document in Windows BrailleDots TextTables AttributesTables ContractionTables KeyTables
do
   installFile "Documents/README.${document}" "${documentDirectory}/${document}.txt"
done

for root in "${sourceRoot}" "${buildRoot}"
do
   cd "${root}/Documents"

   for manual in Manual-BRLTTY Manual-BrlAPI BrlAPIref
   do
      [ -d "${manual}" ] || continue

      for file in $(find "${manual}" -type f -print)
      do
         name="${file##*/}"
         extension="${name##*.}"

         case "${extension}"
         in
            txt | html | htm | doc | pdf)
               installFile "${file}" "${documentDirectory}/${file}"
               ;;

            *);;
         esac
      done
   done
done

cd "${sourceRoot}/Drivers"
for document in $(find Braille Speech -type f -name "README*" -print)
do
   installFile "${document}" "${documentDirectory}/Drivers/${document}.txt"
done

cd "${programDirectory}"
installFile "msvcr90.dll" "bin/"
installFile "brltty.cat" "bin/"
installFile "brltty_x64.cat" "bin/"
installFile "install.bat" "/"
installFile "uninstall.bat" "/"
installFile "run-debug.bat" "/"

installFiles ${usbWindowsPackages}

cd /mingw/bin
for file in libgcc_s_dw2-1.dll libiconv-2.dll libpdcurses*.dll
do
   installFile "${file}" "bin/"
done

cd "${installRoot}"
rm -f "bin/brltty-config"
rm -f "etc/brlapi.key"

echo "${revisionIdentifier}" >"REVISION.txt"

for source in $(find "share/man" -type f -name "*.[1-9]" -print)
do
   [ -z "${groffPath}" ] || {
      target="${source%.*}.txt"

      "${groffPath}" -T ascii -mandoc 2>/dev/null <"${source}" |
      sed -e 's/'$'\033''\[[0-9]*m//g' >"${target}"

      [ -s "${target}" ] || rm -f "${target}"
   }

   rm -f "${source}"
done

cat >"README.txt" <<END-OF-README
${readmeHeader}
- To register BRLTTY as a service so that it will get started automatically at
  boot, run install.bat. To unregister it, run uninstall.bat.
- If your braille device uses a USB connection:
  * If you either cannot, or would prefer not to, install its
    manufacturer's driver, then you can select "USB:", and install
    ${usbName}'s driver by doing one of the following:
    + right-clicking on bin/brltty-${usbPackage}.inf and selecting install
    + answering yes to the setup prompt for installing the ${usbName} driver
  * If you have installed its manufacturer's driver, and if that driver defines
    a virtual COM port, then select that virtual COM port.
  * If you need to install its manufacturer's driver, but
    that driver does not define a virtual COM port, then 
${usbFilter}
- If your braille device uses a serial connection, or if it is connected via a
  serial to USB converter, then select the correct COM port. Make sure to
  select the correct braille driver as well, because serial autodetection may
  brick some devices.
- If your braille device uses a Bluetooth connection, you can either use the
  Windows Bluetooth facilities to create a virtual COM port which you can then
  select, or manually configure the braille-device line in brltty.conf.
${readmeFooter}
END-OF-README

if [ -n "${libPath}" ]
then
   cd "${installRoot}/lib"
   "${libPath}" //nologo /def:brlapi.def /name:brlapi-${BRLAPI_VERSION}.dll /out:brlapi.lib /machine:x86
else
   logMessage warning "import library not creatable"
fi

# for the installer but before text file conversion
cd "${installRoot}/.."
nsisScript="${usbPackage}.nsi"
cp "${programDirectory}/${usbPackage}.nsi" .
cp "${programDirectory}/nsistrings.txt" .

cat >"README.txt" <<END-OF-README
${readmeHeader}
${readmeFooter}
END-OF-README

if [ -n "${ahkRoot}" ]
then
   logMessage task "creating configurator"
   cd "${installRoot}"
   cp "${programDirectory}/brlttycnf.ahk" .
   "${ahkRoot}/Compiler/Ahk2Exe.exe" //in brlttycnf.ahk //out brlttycnf.exe
   rm brlttycnf.ahk
else
   logMessage warning "configurator not creatable"
fi

if [ -n "${unix2dosPath}" ]
then
   logMessage task "converting text files"
   cd "${installRoot}/.."

   find . -print |
      while read path
      do
         handle="${path#.}"
         [ -n "${handle}" ] || continue
         handle="${handle#/}"

         name="${path##*/}"
         extension="${name##*.}"

         if [ -f "${path}" ]
         then
            if [ "${extension}" != "${name}" ]
            then
               case "${extension}"
               in
                  bat | cat | conf | h | htm | html | inf | nsi | txt | [tack]t[bi])
                     "${unix2dosPath}" -q -o "${path}"
                     ;;

                  a | def | dll | doc | egg-info | exe | exp | lib | pdf | pyd | sys | ttf);;
                  *) logMessage warning "unexpected file extension: ${handle}";;
               esac
            fi
         elif [ ! -d "${path}" ]
         then
            logMessage warning "unsupported special file: ${handle}"
         fi
      done
else
   logMessage warning "text files not convertable"
fi

if "${invokeShell}"
then
   logMessage task "invoking shell"
   cd "${installRoot}"
   "${SHELL:-/bin/sh}" || :
fi

if [ -n "${zipPath}" ]
then
   logMessage task "creating archive"
   archiveFile="${initialDirectory}/${buildName}.zip"
   rm -f "${archiveFile}"
   cd "${installRoot}/.."
   "${zipPath}" -q -A -r "${archiveFile}" "${buildName}"
else
   logMessage warning "archive not creatable"
fi

if [ -n "${nsisRoot}" ]
then
   logMessage task "creating installer"
   cd "${installRoot}/.."
   mv nsistrings.txt "${buildName}"
   installerFile="${buildName}.exe"

   "${nsisRoot}/makensis" -V2 \
      -DVERSION="${buildVersion}" \
      -DDISTDIR="${buildName}" \
      -DOUTFILE="${installerFile}" \
      "${nsisScript}"

   rm -f "${initialDirectory}/${installerFile}"
   cp "${installerFile}" "${initialDirectory}/${installerFile}"
else
   logMessage warning "installer not creatable"
fi

"${keepBuild}" || {
   logMessage task "cleaning up"
   cd "${initialDirectory}"
   rm -f -r "${temporaryDirectory}"
}

logMessage task "done"
exit 0
