#!/usr/bin/env bash

# OS relevant options
case "${OSTYPE}" in
    "linux-gnu")
        MD5_PROG="md5sum"
        ;;
    "darwin")
        MD5_PROG="md5 -r"
        ;;
    *)
        echo "OS type '${OSTYPE}' not supported. Exiting now!"
        exit 1
        ;;
esac

print_help () {
cat <<EOM

Usage: $0 [-w] [-d] [-i] [-a <APP_SCRIPT>] APP_NAME

    APP_NAME            Name of the app, used as folder and class name.

    -a <APP_SCRIPT>     If not provided, it will be created automatically by
                        converting the APP_NAME from camel case to snake case.

                        If APP_NAME or APP_SCRIPT ends with IPE or similar, it
                        will be assumed as IPE and will be created under ipes.

    -w                  if true, FlaskRunner is used and basic index.html is
                        created

    -i                  if true, IN-AE is expected and default endpoint is
                        configured to backend

    -d                  if true, no setup file and docker files will be created
EOM
}

optspec=":wdia:"

WEB_APP="false"
IN_AE="false"
NO_DOCKER="false"

while getopts "${optspec}" optchar; do
    case "${optchar}" in
        w)
            WEB_APP="true"
            ;;
        i)
            IN_AE="true"
            ;;
        d)
            NO_DOCKER="true"
            ;;
        a)
            if [[ ${OPTARG} == -* ]]; then
                print_help
                exit 1
            fi
            APP_SCRIPT=${OPTARG}
            ;;
        \?|:)
            print_help
            exit 1
    esac
done

shift $((OPTIND-1))

if [ $# -ne 1 ]; then
    print_help
    exit 1
fi

# set variables
APP_NAME="$1"
APP_SCRIPT=${APP_SCRIPT-$(printf "${APP_NAME}" | \
    perl -p -e 's/([a-z0-9])([A-Z]+)/\1-\L\2/g' | \
    perl -p -e 's/^([A-Z])/\l$1/g')}
MOD_NAME=$(printf "${APP_SCRIPT}" | tr "-" "_" | sed 's/.*/\L&/g')
PKG_NAME=$(printf "${APP_SCRIPT}" | tr -d "-" | sed 's/.*/\L&/g')

if [[ ${PKG_NAME} == *"ipe" ]]; then
    APPS_FOLDER="ipes"
else
    APPS_FOLDER="apps"
fi
APP_FOLDER="${APPS_FOLDER}/${APP_NAME}"

# check if existing
if [ -e "${APP_FOLDER}" ]; then
    printf "App folder already existing. Aborting.\n"
    exit 1
fi

# check if APP_NAME and APP_SCRIPT are the same. Testing lowercase for case-insensitive filesystems
if [ "${APP_NAME,,}" == "${APP_SCRIPT,,}" ]; then
    echo "APP_NAME and APP_SCRIPT shouldn't be the same, specially in case-insensitive systems. Aborting\n"
    exit 1
fi

# change to root folder
cd "$(dirname ${0})"

# set port suffix
APP_PORT_SUFFIX=$(printf %04d $((0x$(${MD5_PROG} <<< "${APP_NAME}" | cut -c1-4) % 10000)))

################################################################################
# creating module files
mkdir -p "${APP_FOLDER}"

SRC_FOLDER="${APP_FOLDER}/src/${PKG_NAME}"
mkdir -p "${SRC_FOLDER}"

# init file
cat > "${SRC_FOLDER}/__init__.py" << EOF
"""
TODO: Add description here
"""

__version__ = "ADD-VERSION-HERE"
__description__ = "${APP_NAME}"
__author_name__ = "ADD_AUTHOR_HERE"
__author_mail__ = "ADD_MAIL_HERE"
__requires__ = []
EOF

# main file
MAIN_MODULE="${SRC_FOLDER}/__main__.py"
cat > "${MAIN_MODULE}" << EOF
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser

from openmtc_app.util import prepare_app, get_value
from openmtc_app.flask_runner import FlaskRunner as Runner
from .${MOD_NAME} import ${APP_NAME}

# defaults
default_name = "${APP_NAME}"
default_ep = "http://localhost:8000"

# args parser
parser = ArgumentParser(
    description="An IPE called ${APP_NAME}",
    prog="${APP_NAME}",
    formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument("-n", "--name", help="Name used for the AE.")
parser.add_argument("-s", "--ep", help="URL of the local Endpoint.")

# args, config and logging
args, config = prepare_app(parser, __loader__, __name__, "config.json")

# variables
nm = get_value("name", (unicode, str), default_name, args, config)
cb = config.get("cse_base", "onem2m")
ep = get_value("ep", (unicode, str), default_ep, args, config)
poas = config.get("poas", ["http://auto:2${APP_PORT_SUFFIX}"])
originator_pre = config.get("originator_pre", "//openmtc.org/mn-cse-1")
ssl_certs = config.get("ssl_certs", {})
port = int(config.get("port", 1${APP_PORT_SUFFIX}))

# start
app = ${APP_NAME}(
    name=nm, cse_base=cb, poas=poas,
    originator_pre=originator_pre, **ssl_certs
)
Runner(app, port=port).run(ep)

print ("Exiting....")
EOF

if [ ${WEB_APP} == "false" ]; then
    perl -i -p -e 's/flask_runner/runner/' "${MAIN_MODULE}"
    perl -i -p -e 's/FlaskRunner/AppRunner/' "${MAIN_MODULE}"
    perl -i -p -e 's/app, port=port/app/' "${MAIN_MODULE}"
    perl -i -n -e 'print unless /port = int\(/' "${MAIN_MODULE}"
fi

if [ ${IN_AE} == "true" ]; then
    perl -i -p -e 's/mn-cse-1/in-cse-1/' "${MAIN_MODULE}"
    perl -i -p -e 's/localhost:8000/localhost:18000/' "${MAIN_MODULE}"
fi

# main module
cat > "${SRC_FOLDER}/${MOD_NAME}.py" << EOF
from openmtc_app.onem2m import XAE


class ${APP_NAME}(XAE):
    interval = 10

    def _on_register(self):
        # start endless loop
        self.run_forever(self.interval)
EOF

# index html if web app
if [ ${WEB_APP} == "true" ]; then
    mkdir -p "${SRC_FOLDER}/static"
    cat > "${SRC_FOLDER}/static/index.html" << EOF
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
Hello World!
</body>
</html>
EOF
fi

################################################################################
# creating config files

mkdir -p "${APP_FOLDER}/etc/conf"

# main config
MAIN_CONFIG="${APP_FOLDER}/config.json"
cat > "${MAIN_CONFIG}" << EOF
{
    "name": "${APP_NAME}",
    "ep": "http://localhost:8000",
    "cse_base": "onem2m",
    "poas": [
        "http://auto:2${APP_PORT_SUFFIX}"
    ],
    "originator_pre": "//openmtc.org/mn-cse-1",
    "ssl_certs": {
        "cert_file": null,
        "key_file": null,
        "ca_certs": null
    },
    "port": 1${APP_PORT_SUFFIX},
    "logging": {
        "level": "ERROR",
        "file": null
    }
}
EOF

# dist config
DIST_CONFIG="${APP_FOLDER}/etc/conf/config.json.dist"
cat > "${DIST_CONFIG}" << EOF
{
    "name": "${APP_NAME}",
    "ep": "http://localhost:8000",
    "cse_base": "onem2m",
    "poas": [
        "http://auto:2${APP_PORT_SUFFIX}"
    ],
    "originator_pre": "//openmtc.org/mn-cse-1",
    "ssl_certs": {
        "cert_file": "/etc/openmtc/certs/${PKG_NAME}.cert.pem",
        "key_file": "/etc/openmtc/certs/${PKG_NAME}.key.pem",
        "ca_certs": "/etc/openmtc/certs/ca-chain.cert.pem"
    },
    "port": 1${APP_PORT_SUFFIX},
    "logging": {
        "level": "INFO",
        "file": "/var/log/openmtc/${PKG_NAME}.log"
    }
}
EOF

if [ ${WEB_APP} == "false" ]; then
    perl -i -n -e 'print unless /"port": 1/' "${MAIN_CONFIG}"
    perl -i -n -e 'print unless /"port": 1/' "${DIST_CONFIG}"
fi

if [ ${IN_AE} == "true" ]; then
    perl -i -p -e 's/mn-cse-1/in-cse-1/' "${MAIN_CONFIG}"
    perl -i -p -e 's/localhost:8000/localhost:18000/' "${MAIN_CONFIG}"
    perl -i -p -e 's/mn-cse-1/in-cse-1/' "${DIST_CONFIG}"
    perl -i -p -e 's/localhost:8000/localhost:18000/' "${DIST_CONFIG}"
fi

################################################################################
# create start script
START_SCRIPT="${APPS_FOLDER}/${APP_SCRIPT}"
cat > "${START_SCRIPT}" << EOF
#!/usr/bin/env bash

cd \$(dirname \${0})

. ./prep-env.sh

cd ${APP_NAME}

PYTHONPATH=\${PYTHONPATH}:src exec python -m ${PKG_NAME} \$@
EOF

chmod +x "${START_SCRIPT}"

################################################################################
# create bin script
mkdir -p "${APP_FOLDER}/bin"
BIN_SCRIPT="${APP_FOLDER}/bin/openmtc-${APP_SCRIPT}"
cat > "${BIN_SCRIPT}" << EOF
#!/usr/bin/env bash

exec python -m ${PKG_NAME} \$@
EOF

chmod +x "${BIN_SCRIPT}"

################################################################################
# create systemd unit file
SYSTEMD_FOLDER="${APP_FOLDER}/etc/systemd/system"
mkdir -p ${SYSTEMD_FOLDER}
cat > "${SYSTEMD_FOLDER}/openmtc-${PKG_NAME}.service" << EOF
[Unit]
Description=OpenMTC ${APP_NAME}
After=network.target
Wants=ntp.service

[Service]
ExecStart=/usr/local/bin/${APP_SCRIPT}

[Install]
WantedBy=multi-user.target
EOF

################################################################################
# return here if docker is not needed
if [ ${NO_DOCKER} == "true" ]; then
    exit 0
fi

################################################################################
# create setup file
SETUP_FILE="${APP_FOLDER}/setup-${PKG_NAME}.py"
cat > "${SETUP_FILE}" << EOF
#!/usr/bin/env python

from setuptools import setup
from distutils.core import setup
from glob import glob
import sys

from utils import get_packages, get_pkg_files, OpenMTCSdist, move_config_files

# name and dir
NAME = "${PKG_NAME}"
BASE_DIR = "."

# import pkg
sys.path.append(BASE_DIR + "/src")
pkg = __import__(NAME)

# setup name and version
SETUP_NAME = "openmtc-" + NAME
SETUP_VERSION = pkg.__version__
SETUP_DESCRIPTION = pkg.__description__

# meta
SETUP_AUTHOR = pkg.__author_name__
SETUP_AUTHOR_EMAIL = pkg.__author_mail__
SETUP_URL = "http://www.openmtc.org"
SETUP_LICENSE = "Fraunhofer FOKUS proprietary"

# requirements
SETUP_REQUIRES = pkg.__requires__
SETUP_INSTALL_REQUIRES = pkg.__requires__

# packages
PACKAGES = [NAME]
PACKAGE_DIR = {"": BASE_DIR + "/src"}
all_packages = []
for package in PACKAGES:
    all_packages.extend(get_packages(package, PACKAGE_DIR))

# scripts
SETUP_SCRIPTS = glob(BASE_DIR + "/bin/*")

# package data
PACKAGE_DATA = {NAME: get_pkg_files(BASE_DIR, NAME)}

# data files
CONFIG_FILES = ("config.json",)
CONFIG_DIR = "/etc/openmtc/" + NAME
CONFIG_DIST_FILES = (BASE_DIR + "/etc/conf/config.json.dist",)
DATA_FILES = [(CONFIG_DIR, CONFIG_DIST_FILES)]

# cmd class
CMD_CLASS = {'sdist': OpenMTCSdist}

if __name__ == "__main__":
    if 'bdist_wheel' in sys.argv:
        raise RuntimeError("This setup.py does not support wheels")

    ############################################################################
    # setup
    setup(name=SETUP_NAME,
          version=SETUP_VERSION,
          description=SETUP_DESCRIPTION,
          author=SETUP_AUTHOR,
          author_email=SETUP_AUTHOR_EMAIL,
          url=SETUP_URL,
          license=SETUP_LICENSE,
          requires=SETUP_REQUIRES,
          install_requires=SETUP_INSTALL_REQUIRES,
          package_dir=PACKAGE_DIR,
          packages=all_packages,
          scripts=SETUP_SCRIPTS,
          package_data=PACKAGE_DATA,
          data_files=DATA_FILES,
          cmdclass=CMD_CLASS
          )

    ############################################################################
    # install
    if "install" in sys.argv:
        # only do this during install
        move_config_files(CONFIG_DIR, CONFIG_FILES)
EOF

chmod +x "${SETUP_FILE}"

# copy needed helper files
cp MANIFEST.in utils.py "${APP_FOLDER}/."

################################################################################
# create docker files

DOCKER_FOLDER="${APP_FOLDER}/docker"
mkdir -p ${DOCKER_FOLDER}

# create configure script
DOCKER_SCRIPT="${DOCKER_FOLDER}/configure-${PKG_NAME}-and-start"
cat > "${DOCKER_SCRIPT}" << EOF
#!/usr/bin/env bash

CONFIG_FILE="/etc/openmtc/${PKG_NAME}/config.json"

NAME=\${NAME-"${APP_NAME}"}
EP=\${EP-"http://localhost:8000"}
CSE_BASE=\${CSE_BASE-"onem2m"}
POAS=\${POAS-'["http://auto:2${APP_PORT_SUFFIX}"]'}
ORIGINATOR_PRE=\${ORIGINATOR_PRE-"//openmtc.org/mn-cse-1"}
SSL_CRT=\${SSL_CRT-"/etc/openmtc/certs/${PKG_NAME}.cert.pem"}
SSL_KEY=\${SSL_KEY-"/etc/openmtc/certs/${PKG_NAME}.key.pem"}
SSL_CA=\${SSL_CA-"/etc/openmtc/certs/ca-chain.cert.pem"}
PORT=\${PORT-"1${APP_PORT_SUFFIX}"}

# defaults logging
LOGGING_FILE=\${LOGGING_FILE-"/var/log/openmtc/${PKG_NAME}.log"}
LOGGING_LEVEL=\${LOGGING_LEVEL-"ERROR"}

# ensure correct level
case \${LOGGING_LEVEL} in
    FATAL|ERROR|WARN|INFO|DEBUG)
    ;;
    *)
    LOGGING_LEVEL="ERROR"
    ;;
esac

# local ip
LOCAL_IP=\$(ip r get 8.8.8.8 | awk 'NR==1 {print \$NF}')

# set hostname
HOST_NAME=\${EXTERNAL_IP-\${LOCAL_IP}}

# Configuration of the service.
CONFIG_TEMP=\${CONFIG_FILE}".tmp"
echo -n "Configuring M2M ${PKG_NAME}..."
JQ_STRING='.'

# basics
JQ_STRING=\${JQ_STRING}' |
    .name = "'\${NAME}'" |
    .ep = "'\${EP}'" |
    .cse_base = "'\${CSE_BASE}'" |
    .poas = '\${POAS}' |
    .originator_pre = "'\${ORIGINATOR_PRE}'" |
    .ssl_certs.cert_file = "'\${SSL_CRT}'" |
    .ssl_certs.key_file = "'\${SSL_KEY}'" |
    .ssl_certs.ca_certs = "'\${SSL_CA}'" |
    .port = "'\${PORT}'" |
    .logging.file |= "'\${LOGGING_FILE}'" |
    .logging.level |= "'\${LOGGING_LEVEL}'"
'

cat \${CONFIG_FILE} | jq -M "\${JQ_STRING}"> \${CONFIG_TEMP}
mv \${CONFIG_TEMP} \${CONFIG_FILE}

echo "done"

exec python -m ${PKG_NAME} \$@
EOF

if [ ${WEB_APP} == "false" ]; then
    perl -i -n -e 'print unless /PORT=/' "${DOCKER_SCRIPT}"
    perl -i -n -e 'print unless /.port =/' "${DOCKER_SCRIPT}"
fi

if [ ${IN_AE} == "true" ]; then
    perl -i -p -e 's/mn-cse-1/in-cse-1/' "${DOCKER_SCRIPT}"
    perl -i -p -e 's/localhost:8000/localhost:18000/' "${DOCKER_SCRIPT}"
fi

chmod +x "${DOCKER_SCRIPT}"

# create docker files
DOCKER_FILE_AMD64="${DOCKER_FOLDER}/${PKG_NAME}-amd64"
DOCKER_FILE_ARM="${DOCKER_FOLDER}/${PKG_NAME}-arm"
cat > "${DOCKER_FILE_AMD64}" << EOF
############################################################
# Dockerfile to run openmtc ${PKG_NAME} binary
############################################################

# Set the base image to use openmtc/sdk
FROM openmtc/sdk-amd64:latest

ENV MOD_NAME=${PKG_NAME}

# Set the file maintainer
MAINTAINER rst

# install openmtc dependencies
COPY tmp/\$MOD_NAME-dependencies.txt /tmp/requirements.txt
RUN pip install --upgrade --requirement /tmp/requirements.txt

# install openmtc-${PKG_NAME}
COPY tmp/openmtc-\$MOD_NAME.tar.gz /tmp/openmtc-\$MOD_NAME.tar.gz
RUN tar xzf /tmp/openmtc-\$MOD_NAME.tar.gz -C / \\
    --owner root --group root --no-same-owner --no-overwrite-dir \\
    --transform 's/json\.dist/json/' --show-transformed

RUN mkdir -p /var/log/openmtc

# add change config
COPY configure-\$MOD_NAME-and-start /usr/local/bin/configure-and-start

# entry point
ENTRYPOINT ["/usr/local/bin/configure-and-start"]
CMD [""]
EOF

cat "${DOCKER_FILE_AMD64}" | sed 's/-amd64/-arm/g' > "${DOCKER_FILE_ARM}"
