#! /usr/bin/env bash

# f24353d02d90f97c72b6977f3a8a05006ad06120

ourScript="$(which "$0")"
if ! head -3 "${ourScript}" 2>/dev/null | grep f24353d02d90f97c72b6977f3a8a05006ad06120 >/dev/null; then
	echo "error: Unable to find ourselves" >&2

	exit 1
fi

cd "$(dirname "${ourScript}")" || exit 1

patchDir="$(pwd)/patches"

if [ -z "${NACL_SDK_ROOT}" ]; then
	echo "error: Please set NACL_SDK_ROOT to the path of the current NaCl SDK target" >&2

	exit 1
fi

if [ ! -d "${NACL_SDK_ROOT}/toolchain" ]; then
	echo "error: Invalid NACL_SDK_ROOT, not found: ${NACL_SDK_ROOT}/toolchain" >&2

	exit 1
fi

# Setup cross-compiler toolchain
## Set path to include the tools
PATH="${PATH}:${NACL_SDK_ROOT}/toolchain/linux_pnacl/bin"
export PATH

## Set variables needed by projects
HOST_CC="$(which "${CC:-cc}")"
BUILD_CC="${HOST_CC}"
CC_FOR_BUILD="${HOST_CC}"
AR=pnacl-ar
AS=pnacl-as
LD=pnacl-ld
CC=pnacl-clang
CXX=pnacl-clang++
RANLIB=pnacl-ranlib
STRIP=pnacl-strip
OBJCOPY=pnacl-objcopy
export HOST_CC BUILD_CC CC_FOR_BUILD AR AS LD CC CXX RANLIB STRIP OBJCOPY

## Set some CFLAGS that the compiler fails to internally set
CFLAGS="-I${NACL_SDK_ROOT}/include"
CXXFLAGS="${CFLAGS}"
CPPFLAGS="${CFLAGS}"
export CFLAGS CXXFLAGS CPPFLAGS

# Function to download files from the interwebs and verify them
function download() {
	local url file hash
	local hashMethod
	local chkHash

	url="$1"
	file="$2"
	hash="$3"

	if [ -f "${file}" ]; then
		return 0
	fi

	mkdir -p "$(dirname "${file}")"

	hashMethod='sha256'

	rm -f "${file}.new"
	wget --header "X-Cache-URL: ${url}" -O "${file}.new" "http://hashcache.rkeene.org/${hashMethod}/${hash}" || \
		wget -O "${file}.new" "${url}" || \
		return 1

	chkHash="$(openssl "${hashMethod}" "${file}.new" | sed 's@.*= *@@')"

	if [ "${chkHash}" != "${hash}" ]; then
		echo "error: Checksum mismatch: Got: ${chkHash}; Expected: ${hash}" >&2

		return 1
	fi

	mv "${file}.new" "${file}"

	return 0
}

# Extract an archive into a directory, stripping the top-level directory
# if that is all that it contains
function extract() {
	local file directory

	file="$1"
	directory="$2"

	if [ ! -f "${file}" ]; then
		echo "error: Unable to extract \"${file}\"" >&2

		return 1
	fi

	rm -rf "${directory}"

	mkdir -p "${directory}" || return 1
	(
		cd "${directory}" || exit 1

		case "${file}" in
			*.tar.bz2|*.bz2)
				bzip2 -dc | tar -xf - || exit 1
				;;
			*.tar.gz|*.tgz)
				gzip -dc | tar -xf - || exit 1
				;;
			*.tar.xz|*.txz)
				xz -dc | tar -xf - || exit 1
				;;
			*.zip)
				cat > x.zip || exit 1
				unzip x.zip || exit 1
				rm -f x.zip
				;;
			*)
				echo "error: Don't know what to do with \"${file}\"" >&2

				exit 1
				;;
		esac

		if [ -d "$(echo *)" ]; then
			mv */* . >/dev/null 2>/dev/null
		fi
	) < "${file}" || return 1

	return 0
}

# User-overridable "make"
function make() {
	"${MAKE:-$(which make)}" "$@"
}

# Build "zlib"
function buildZlib() {
	local version url pkg sha256 configure_extra
	local archive workdir

	pkg='zlib'
	version='1.2.8'
	url="http://zlib.net/zlib-${version}.tar.gz"
	sha256='36658cb768a54c1d4dec43c3116c27ed893e88b02ecfcb44f2166f9c0b7f2a0d'

	archive="archive/${pkg}-${version}.tar.gz"
	workdir="workdir-${RANDOM}${RANDOM}${RANDOM}${RANDOM}.build"

	download "${url}" "${archive}" "${sha256}" || return 1
	extract "${archive}" "${workdir}" || return 1

	(
		cd "${workdir}" || exit 1

		./configure --prefix=/ --libdir=/lib --static || exit 1
		make || exit 1
		make DESTDIR="${instdir}" install || exit 1
	) || return 1

	rm -rf "${workdir}"

	return 0
}

# Build "boost"
function buildBoost() {
	local version url pkg sha256 configure_extra
	local archive workdir

	pkg='boost'
	version='1.57.0'
	url="http://downloads.sourceforge.net/project/boost/boost/${version}/boost_`echo ${version} | sed 's_\._\__g'`.tar.bz2"
	sha256='910c8c022a33ccec7f088bd65d4f14b466588dda94ba2124e78b8c57db264967'

	archive="archive/${pkg}-${version}.tar.bz2"
	workdir="workdir-${RANDOM}${RANDOM}${RANDOM}${RANDOM}.build"

	download "${url}" "${archive}" "${sha256}" || return 1
	extract "${archive}" "${workdir}" || return 1

	(
		cd "${workdir}" || exit 1

		./bootstrap.sh --prefix="${instdir}" --with-python=false --show-libraries

		./bootstrap.sh --prefix="${instdir}" --with-python=false --without-libraries='atomic,chrono,container,context,coroutine,date_time,exception,filesystem,graph,graph_parallel,iostreams,locale,log,math,mpi,program_options,python,random,regex,serialization,signals,system,test,thread,timer,wave' || exit 1

		echo "using gcc : pnacl : ${CXX} ;" >> project-config.jam

		./b2 --debug-configuration toolset=gcc-pnacl target-os=linux link=static runtime-link=static || exit 1

		./bjam install | grep -v '^common.copy '

		exit 0
	) || return 1

	rm -rf "${workdir}"

	return 0
}

# Build the libpcsc we need
function buildPCSC() {
	local version url pkg sha256 configure_extra
	local archive workdir

	buildBoost || return 1

	pkg='smart-card-client-c-example-app-sources'
	version='0.0.0'
	sha256='68970c6dc4b55d24a453bcfe5768af2b877f553a92a390be7f935db6223d4bc5'

	archive="archive/${pkg}.zip"
	workdir="workdir-${RANDOM}${RANDOM}${RANDOM}${RANDOM}.build"

	extract "${archive}" "${workdir}" || return 1

	(
		cd "${workdir}" || exit 1

		# Copy out PC/SC headers for later use
		mkdir -p "${instdir}/include/PCSC" || exit 1
		cp third_party/pcsc-lite/src/src/PCSC/*.h "${instdir}/include/PCSC" || exit 1

		# Copy out extra headers
		cat << \_EOF_ > "${instdir}/include/PCSC/pcsc-nacl.h"
#ifndef PCSC_NACL_H
#define PCSC_NACL_H 1
#ifdef __cplusplus
#include <ppapi/cpp/core.h>
#include <ppapi/cpp/instance.h>

void pcscNaClInit(pp::Instance *instance, pp::Core *core);
bool pcscNaClHandleMessage(const pp::Var &message);

#endif
#endif
_EOF_

		# Copy out JavaScript files for later use
		mkdir "${instdir}/js" || exit 1
		cp common-utils/*.js "${instdir}/js" || exit 1
		cp third_party/pcsc-lite/client-side/*.js "${instdir}/js" || exit 1

		# Build libpcsc
		## Assemble all the files into a single tree
		for file in logging.h scard_structs_serialization.h dom_requests_manager.h thread_safe_string_pool.h \
		    pp_var_utils.cc pp_var_utils.h scard_structs_serialization.cc dom_requests_manager.cc logging.cc; do
			find . -type f -name "${file}" -exec cp '{}' third_party/pcsc-lite/client-side/ ';'
		done

		## Create Makefile
		cat << \_EOF_ > third_party/pcsc-lite/client-side/Makefile || exit 1
CFLAGS   += -Wall -std=gnu++11
CXXFLAGS += -Wall -std=gnu++11
CPPFLAGS += -I../src/src/PCSC

OBJS = pcsc_nacl.o pcsc_nacl_global.o scard_structs_serialization.o pp_var_utils.o logging.o dom_requests_manager.o pcsc_nacl_init.o

all: libpcsc.a

libpcsc.a: $(OBJS)
	rm -f libpcsc.a
	$(AR) rc libpcsc.a $(OBJS)
	-$(RANLIB) libpcsc.a

pcsc_nacl_init.o: pcsc_nacl_init.cc pcsc_nacl_global.h dom_requests_manager.h pcsc_nacl.h

pcsc_nacl.o: pcsc_nacl.cc pcsc_nacl.h logging.h pp_var_utils.h scard_structs_serialization.h
pcsc_nacl.h: dom_requests_manager.h thread_safe_string_pool.h

pcsc_nacl_global.o: pcsc_nacl_global.cc pcsc_nacl_global.h logging.h
pcsc_nacl_global.h: pcsc_nacl.h

pp_var_utils.o: pp_var_utils.cc pp_var_utils.h logging.h
pp_var_utils.h: logging.h

scard_structs_serialization.o: scard_structs_serialization.cc scard_structs_serialization.h pp_var_utils.h
scard_structs_serialization.h: pp_var_utils.h

dom_requests_manager.o: dom_requests_manager.cc dom_requests_manager.h logging.h pp_var_utils.h

logging.o: logging.cc logging.h

clean:
	rm -f $(OBJS)
	rm -f libpcsc.a

distclean: clean

.PHONY: all clean distclean
_EOF_

		## Create initialization procedure, since it lacks one
		cat << \_EOF_ > third_party/pcsc-lite/client-side/pcsc_nacl_init.cc
#include <ppapi/cpp/core.h>
#include <ppapi/cpp/instance.h>

#include <unistd.h>

#include "pcsc_nacl_global.h"
#include "dom_requests_manager.h"
#include "pcsc_nacl.h"

static DomRequestsManager *pcscNaClDRM = NULL;

void pcscNaClInit(pp::Instance *instance, pp::Core *core) {
	DomRequestsManager::PpDelegateImpl *drmDelegateImpl;
	PcscNacl *pcsc_nacl;

	drmDelegateImpl = new DomRequestsManager::PpDelegateImpl(instance, core);

	pcscNaClDRM = new DomRequestsManager("pcsc-nacl", drmDelegateImpl);

	pcsc_nacl = new PcscNacl(pcscNaClDRM, "khpfeaanjngmcnplbdlpegiifgpfgdco", "CACKey");

	if (!pcsc_nacl->Initialize()) {
		return;
	}

	SetPcscNaclGlobalInstance(pcsc_nacl);

	return;
}

bool pcscNaClHandleMessage(const pp::Var &message) {
	return(pcscNaClDRM->HandleMessage(message));
}
_EOF_

		## Make it
		make -C third_party/pcsc-lite/client-side || exit 1

		# Install the built libpcsc
		mkdir -p "${instdir}/lib" || exit 1
		cp third_party/pcsc-lite/client-side/libpcsc.a "${instdir}/lib" || exit 1
	) || return 1

	rm -rf "${workdir}"

	return 0
}

# Build our CACKey
function buildCACKey() {
	local platform
	local file copied
	local targets

	buildZlib || return 1
	buildPCSC || return 1

	(
		cd ../.. || exit 1

		make distclean

		./configure --host=nacl --with-pcsc-headers="${instdir}/include/PCSC" --with-pcsc-libs="-lpcsc" cackey_cv_pcsc_works=okay --disable-dod-certs || exit 1
		make || exit 1
	) || return 1

	mkdir -p "${instdir}/lib"
	cp ../../libcackey.a ../../libcackey_g.a "${instdir}/lib" || return 1

	return 0
}

instdir="workdir-${RANDOM}${RANDOM}${RANDOM}${RANDOM}.inst"
rm -rf "${instdir}"
mkdir "${instdir}" || exit 1
instdir="$(cd "${instdir}" && pwd)" || exit 1

CFLAGS="${CFLAGS} -I${instdir}/include"
CPPFLAGS="${CPPFLAGS} -I${instdir}/include"
CXXFLAGS="${CXXFLAGS} -I${instdir}/include"
LDFLAGS="${LDFLAGS} -L${instdir}/lib"
export CFLAGS CPPFLAGS CXXFLAGS LDFLAGS

buildCACKey || exit 1

exit 0
