#! /bin/bash

#set -x

. faustpath
. faustoptflags
. usage.sh

# Check for some common locations of the VST SDK files. This falls back to
# /usr/local/src/vstsdk if none of these are found. In that case, or if make
# picks the wrong location, you can also set the SDK variable explicitly.

#SDK=/usr/local/src/vstsdk

# Note that the paths to be searched are listed in reverse order here, so that
# the preferred path comes *last*.
sdkpaths="/usr/src/VST* /usr/src/vst* /usr/include/VST* /usr/include/vst* /opt/local/src/VST* /opt/local/src/vst* /opt/local/include/VST* /opt/local/include/vst* /usr/local/src/VST* /usr/local/src/vst* /usr/local/include/VST* /usr/local/include/vst* $VSTSDK"
# This should hopefully work on *BSD, Linux and Mac OSX.
[ -z "$SDK" ] && SDK=$(echo $sdkpaths | xargs ls -f -d 2>/dev/null | tail -n 1)
[ -z "$SDK" ] && SDK=/usr/local/src/vstsdk

# SDKSRC should point to the SDK source files (vstplugmain.cpp et al).
# Usually these are either directly under $SDK or in the
# public.sdk/source/vst2.x subdirectory.
[ -z "$SDKSRC" -a -f "$SDK/vstplugmain.cpp" ] && SDKSRC="$SDK"
[ -z "$SDKSRC" -a -f "$SDK/public.sdk/source/vst2.x/vstplugmain.cpp" ] && SDKSRC="$SDK/public.sdk/source/vst2.x"
[ -z "$SDKSRC" ] && SDKSRC="$SDK/public.sdk/source/vst2.x"

# Default qmake setup (for GUI compilation). This requires Qt4 or Qt5 (Qt5 is
# preferred). We try to locate the qmake executable in some common locations
# here. If this doesn't work out then you can also set the QMAKE environment
# variable explicitly, or use one of the -qt4 and -qt5 options below.
[ -z "$QMAKE" ] && QMAKE=$(which qmake-qt5 || which /opt/local/libexec/qt5/bin/qmake || which qmake-qt4 || which /opt/local/libexec/qt4/bin/qmake || echo qmake)

# Where the Faust includes live. We assume that this is under the prefix of
# whatever Faust binary 'which' locates. You can also specify this explicitly
# by setting the FAUSTINC environment variable accordingly.
# FAUSTINC is now set by faustpath
#[ -z "$FAUSTINC" ] && FAUSTINC=$(which faust 2>/dev/null | sed -e 's?/bin/faust?/include/faust?')

# Where our own Faust library files are. This may be under a different prefix
# or not installed anywhere. We try 'ls' on faustvstqt.h in some common
# locations here, and fall back to the current directory if the file isn't
# found, so that you can run the script from the faust-vst source
# directory. You can also specify this explicitly by setting the FAUSTARCH
# environment variable accordingly.

# The part below is obsolete
# architecture location is now defined by faustpath and stored in a variable named FAUSTARCH
#[ -z "$FAUSTLIB" ] && FAUSTLIB=$(dirname "$((ls -f /usr/share/faust/faustvstqt.h /usr/local/share/faust/faustvstqt.h /opt/local/share/faust/faustvstqt.h "$PWD/faustvstqt.h" 2>/dev/null)|tail -1)")
#[ -z "$FAUSTLIB" ] && FAUSTLIB="$PWD"

# defaults (these can be changed with the options listed below)
FAUST_META=1
FAUST_MIDICC=1
FAUST_MTS=1
FAUST_UI=0
VOICE_CTRLS=1
NVOICES=-1

KEEP="no"
STYLE=""

PROCARCH="-fPIC"
dllext=".so"
CXXFLAGS="-Ofast -std=c++11 -march=native"

# Darwin-specific
#ARCH="-arch i386 -arch x86_64"
if [[ $(uname) == Darwin || $CROSSTARGET == Darwin ]]; then
    #CXXFLAGS="-Ofast -std=c++11 -march=native $ARCH -I/opt/local/include"
    CXXFLAGS="-Ofast -std=c++11 -mmacosx-version-min=10.9 $ARCH -I/opt/local/include"
    dllext=".vst"
fi

# dispatch command arguments
for ((i=1;i<$#+1;i++)); do
    p=${!i}
    if [ $p = "-help" ] || [ $p = "-h" ]; then
        usage faust2faustvst "[options ...] <file.dsp>"
        require VST SDK
        echo  "Compiles Faust programs to VST plugins"
        option
        option -gui "build the plugin GUI."
        option -keep "retain the build directory."
        option -nometa "ignore metadata (author information etc.) from the Faust source"
        option -nomidicc "plugin doesn't process MIDI control data."
        option -notuning "disable the tuning control (instruments only)."
        option -novoicectrls "no extra polyphony/tuning controls on GUI (instruments only)"
        option "-nvoices N" "number of synth voices (instruments only; arg must be an integer)"
        option "-qt4, -qt5" "select the GUI toolkit (requires Qt4/5; implies -gui)."
        option "-style S" "  select the stylesheet (arg must be Default, Blue, Grey or Salmon)."
        cat <<EOF
Environment variables:
  FAUSTINC: specify the location of the Faust include directory
    Default: $FAUSTINC
  FAUSTARCH: specify the location of the Faust VST library files
    Default: $FAUSTARCH
  QMAKE: specify the location of the qmake binary
    Default: $QMAKE
  SDK: specify the location of the VST SDK
    Default: $SDK
  SDKSRC: specify the location of the VST SDK sources
    Default: $SDKSRC
EOF
    exit 0
    elif [ $p = "-omp" ]; then
    : ignore
    elif [ $p = "-icc" ]; then
        CXX=icpc
        CXXFLAGS="-O3 -std=c++11 -xHost -ftz -fno-alias -fp-model fast=2"
    elif [ $p = "-osc" ]; then
        # not implemented for now
        OSCDEFS="DEFINES += OSCCTRL"
        OSCLIBS="-lOSCFaust"
    elif [ $p = "-httpd" ]; then
        # not implemented for now
        HTTPDEFS="DEFINES += HTTPCTRL"
        HTTPLIBS="-lHTTPDFaust"
        HTTPLIBS1=`pkg-config --cflags --libs libmicrohttpd`
    elif [ $p = "-qrcode" ]; then # requires -httpd
        QRDEFS="DEFINES += QRCODECTRL"
    elif [ $p = "-nometa" ]; then
        FAUST_META=0
    elif [ $p = "-nomidicc" ]; then
        FAUST_MIDICC=0
    elif [ $p = "-notuning" ]; then
        FAUST_MTS=0
    elif [ $p = "-novoicectrls" ]; then
        VOICE_CTRLS=0
    elif [ $p = "-gui" ]; then
        FAUST_UI=1
        plugin_gui=yes
    elif [ $p = "-qt4" ]; then
        FAUST_UI=1
        plugin_gui=yes
        QMAKE=$(which qmake-qt4 || which /opt/local/libexec/qt4/bin/qmake || echo qmake-qt4)
    elif [ $p = "-qt5" ]; then
        FAUST_UI=1
        plugin_gui=yes
        QMAKE=$(which qmake-qt5 || which /opt/local/libexec/qt5/bin/qmake || echo qmake-qt5)
    elif [ $p = "-nvoices" ]; then
        (( i++ ))
        NVOICES=${!i}
    elif [ $p = "-arch32" ]; then
        PROCARCH="-m32 -L/usr/lib32"
    elif [ $p = "-arch64" ]; then
        PROCARCH="-m64 -fPIC"
    elif [ $p = "-keep" ]; then
        KEEP="yes"
    elif [ $p = "-style" ]; then
        (( i++ ))
        STYLE=${!i}
    elif [ ${p:0:1} = "-" ]; then
        OPTIONS="$OPTIONS $p"
    elif [[ -f "$p" ]] && [ ${p: -4} == ".dsp" ]; then
        FILES="$FILES $p"
    else
        OPTIONS="$OPTIONS $p"
    fi
done

FILES=( $FILES )
if [ ${#FILES[@]} = 0 ]; then
    echo "$0: no filename specified" >&2
    exit 1
elif [ ${#FILES[@]} -gt 1 ]; then
    echo "$0: multiple filenames specified" >&2
    exit 1
fi

# Check to see whether the required include and library files are where we
# expect them, and bail out with an error message otherwise.
if [ ! -f "$FAUSTINC/faust/gui/QTUI.h" ]; then echo "$0: faust include files not found" >&2; exit 1; fi
if [ ! -f "$FAUSTARCH/faustvstqt.h" ]; then echo "$0: faust-vst library files not found" >&2; exit 1; fi

# Determine the Qt version so that we can pick the needed compilation options.
QTVERSION=$($QMAKE -v 2>/dev/null | tail -1 | sed 's/.*Qt version \([0-9]\).*/\1/')

QTEXTRA=
if [ $QTVERSION = 5 ]; then
    QTEXTRA=x11extras
fi

arch=faustvst.cpp
dspname=${FILES[0]}
SRCDIR=$(dirname "$dspname")
ABSDIR=$(cd $SRCDIR && pwd)
CURDIR=$(pwd)

clsname=`basename "$dspname" .dsp`
cppname="$clsname.cpp"
soname="$clsname$dllext"
tmpdir=`mktemp -d /tmp/faust2faustvst.XXXXXX`

RESOURCES=
STYLE_CXXFLAGS=
if [ -n "$STYLE" ]; then
    RESOURCES="RESOURCES+=$FAUSTINC/faust/gui/Styles/$STYLE.qrc"
    STYLE_CXXFLAGS="QMAKE_CXXFLAGS+=-DSTYLE=\"$STYLE\""
fi

# now set in faustoptflags
#CXX=g++
CPPFLAGS="-DFAUST_META=$FAUST_META -DFAUST_MIDICC=$FAUST_MIDICC -DFAUST_MTS=$FAUST_MTS -DFAUST_UI=$FAUST_UI -DVOICE_CTRLS=$VOICE_CTRLS -I$SDK -I$SDKSRC -D__cdecl="
if [ $NVOICES -ge 0 ]; then
    CPPFLAGS="$CPPFLAGS -DNVOICES=$NVOICES"
fi

# Extra SDK modules needed to build a working plugin.
main=vstplugmain.cpp
afx=audioeffect.cpp
afxx=audioeffectx.cpp
sdksrc="$SDKSRC/$main $SDKSRC/$afx $SDKSRC/$afxx"

# Uncomment this to have Faust substitute the proper class name into the C++
# code. NOTE: This requires that the basename of the dsp file is a valid C
# identifier, which isn't guaranteed, so we disable this by default.
#OPTIONS="$OPTIONS -cn \"$clsname\""

# Create the temp directory.
mkdir -p $tmpdir
#trap "echo $0: compile error, intermediate files left in $tmpdir >&2" EXIT
# Compile the Faust module.
faust -i -a "$FAUSTARCH/$arch" $OPTIONS "$dspname" -o "$tmpdir/$cppname" || exit 1
if [ -n "$plugin_gui" ]; then
# We have to use qmake here.
# XXXTODO: OSX support
(
    cd "$tmpdir"
    $QMAKE -project -t lib -o ${clsname}.pro "CONFIG += gui plugin no_plugin_name_prefix warn_off" "QT += widgets printsupport network $QTEXTRA" "INCLUDEPATH+=$ABSDIR" "INCLUDEPATH+=$CURDIR" "INCLUDEPATH+=$FAUSTARCH" "INCLUDEPATH+=$FAUSTINC" "QMAKE_CXXFLAGS=-std=c++11 $CPPFLAGS" $STYLE_CXXFLAGS "LIBS+=$ARCHLIB $OSCLIBS $HTTPLIBS $HTTPLIBS1" "SOURCES+=$SDKSRC/$main $SDKSRC/$afx $SDKSRC/$afxx" "HEADERS+=$FAUSTARCH/faustvstqt.h $FAUSTINC/faust/gui/QTUI.h" $RESOURCES "$OSCDEFS" "$HTTPDEFS" "$QRDEFS"
    $QMAKE *.pro
    make
) > /dev/null || exit 1
else
if [[ $(uname) == Darwin || $CROSSTARGET == Darwin ]]; then
    mkdir -p $tmpdir/$soname/Contents/MacOS
    printf '%s' 'BNDL????' > $tmpdir/$soname/Contents/PkgInfo
    sed -e "s?@name@?$clsname?g;s?@version@?1.0.0?g" > $tmpdir/$soname/Contents/Info.plist <<EOF
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleExecutable</key>
    <string>@name@</string>
    <key>CFBundleIdentifier</key>
    <string>@name@</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>@name@</string>
    <key>CFBundlePackageType</key>
    <string>BNDL</string>
    <key>CFBundleShortVersionString</key>
    <string>@version@</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleVersion</key>
    <string>@version@</string>
    <key>CSResourcesFileMapped</key>
    <true/>
</dict>
</plist>
EOF
    if [ $(uname -p) == arm ]; then
        $CXX -arch arm64 -arch x86_64 -bundle $CXXFLAGS $FAUSTTOOLSFLAGS $PROCARCH -I"$ABSDIR" $CPPFLAGS $sdksrc "$tmpdir/$cppname" -o "$tmpdir/$soname/Contents/MacOS/$clsname" || exit 1
    else
        $CXX -bundle $CXXFLAGS $FAUSTTOOLSFLAGS $PROCARCH -I"$ABSDIR" $CPPFLAGS $sdksrc "$tmpdir/$cppname" -o "$tmpdir/$soname/Contents/MacOS/$clsname" || exit 1
    fi
    if which codesign > /dev/null; then
        codesign --sign - --deep --force "$tmpdir/$soname/"
    fi
else
    $CXX -shared $CXXFLAGS $FAUSTTOOLSFLAGS $PROCARCH -I"$ABSDIR" $CPPFLAGS $sdksrc "$tmpdir/$cppname" -o "$tmpdir/$soname" || exit 1
fi
fi
#trap - EXIT

# copy down the plugin (bundle on OS X)
rm -rf "$SRCDIR/$soname"
cp -r "$tmpdir/$soname" "$SRCDIR"
if [[ $KEEP == yes ]]; then
    # keep the build directory
    rm -rf "$SRCDIR/$clsname"
    mv $tmpdir "$SRCDIR/$clsname"
else
    # Clean up.
    rm -rf $tmpdir
fi
# Print the name of the generated plugin.
echo "$SRCDIR/$soname;"
