#! /bin/bash

###
# ============LICENSE_START=======================================================
# ONAP POLICY
# ================================================================================
# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#      http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============LICENSE_END=========================================================
##

# #############################################################
# Features Directory Layout:
#
# POLICY_HOME/
#   └── features/
#        └── <feature-name>*/
#            └── [config]/
#            │   └── <config-file>*
#            └── lib/
#            │   └── [dependencies]/
#            │   │   └── <dependent-jar>*
#            │   └── feature/
#            │       └── <feature-jar>
#            └── [install]
#                └── [enable]
#                └── [disable]
#                └── [other-future-operations]
#                └── [other-files]
#
#   <feature-name> directory should not have the "feature-" prefix.
#   <config-file> preferable with "feature-" prefix.
#
# Example:
#
# POLICY_HOME/
#   └── features/
#        ├── eelf/
#        │   ├── config/
#        │   │   ├── logback-eelf.xml
#        │   └── lib/
#        │   │   └── dependencies/
#        │   │   │   └── ECOMP-Logging-1.1.0-SNAPSHOT.jar
#        │   │   │   └── eelf-core-1.0.0.jar
#        │   │   └── feature/
#        │   │       └── feature-eelf-1.1.0-SNAPSHOT.jar
#        │   └── install/
#        │       └── enable
#        │       └── disable
#        └── healthcheck/
#            ├── config/
#            │   └── feature-healthcheck.properties
#            └── lib/
#                └── feature/
#                    └── feature-healthcheck-1.1.0-SNAPSHOT.jar
# #############################################################

if [[ ${DEBUG} == y ]]; then
	echo "-- MAIN --"
	set -x
fi
	
# The directories at play

LIB=${POLICY_HOME}/lib
CONFIG=${POLICY_HOME}/config
FEATURES=${POLICY_HOME}/features

if [[ ! ( -d "${LIB}" && -x "${LIB}" ) ]]; then
	echo "ERROR: no ${LIB} directory"
	exit 1
fi

if [[ ! ( -d "${CONFIG}" && -x "${CONFIG}" ) ]]; then
	echo "ERROR: no ${CONFIG} directory"
	exit 2
fi

if [[ ! ( -d "${FEATURES}" && -x "${FEATURES}" ) ]]; then
	echo "ERROR: no ${FEATURES} directory"
	exit 3
fi

# relative per Feature Directory Paths

FEATURE_DEPS="lib/dependencies"
FEATURE_LIB="lib/feature"
FEATURE_CONFIG="config"
FEATURE_INSTALL="install"

featureJars=$(find "${FEATURES}" -name "feature-*.jar" -type f -exec basename {} \; 2> /dev/null)
if [[ -z ${featureJars} ]]; then
	echo "no features"
	usage
	exit 0
fi

# default field lengths
nameLength=20
versionLength=15

# update field lengths, if needed
for jar in ${featureJars} ; do
	# get file name without 'jar' suffix
	tmp="${jar%\.jar}"

	# remove feature prefix
	tmp="${tmp#feature-}"
		
	# get feature name by removing the version portion
	name="${tmp%%-[0-9]*}"

	# extract version portion of name
	version="${tmp#${name}-}"

	# grow the size of the name/version field, if needed
	if (( "${#name}" > nameLength )) ; then
		nameLength="${#name}"
	fi
	if (( "${#version}" > versionLength )) ; then
		versionLength="${#version}"
	fi
done

# ##########################################################
# usage: usage information
# ##########################################################
function usage
{
		# print out usage information
		cat >&2 <<-'EOF'
		Usage:  features status
		            Get enabled/disabled status on all features
		        features enable <feature> ...
		            Enable the specified feature
		        features disable <feature> ...
		            Disable the specified feature
		EOF
}

# ##########################################################
# status: dump out status information
# ##########################################################
function status
{
	if [[ ${DEBUG} == y ]]; then
		echo "-- ${FUNCNAME[0]} $@ --"
		set -x
	fi
	
	local tmp name version status
	local format="%-${nameLength}s %-${versionLength}s %s\n"
	
	printf "${format}" "name" "version" "status"
	printf "${format}" "----" "-------" "------"
	
	for jar in ${featureJars} ; do
		# get file name without 'jar' suffix
		tmp="${jar%\.jar}"
		
		# remove feature prefix
		tmp="${tmp#feature-}"

		# get feature name by removing the version portion
		name="${tmp%%-[0-9]*}"

		# extract version portion of name
		version="${tmp#${name}-}"

		# determine status
		status=disabled
		if [[ -e "${LIB}/${jar}" ]] ; then
			status=enabled
		fi
		printf "${format}" "${name}" "${version}" "${status}"
	done
}

# ##########################################################
# depEnableAnalysis(featureName):  
#                   reports on potential dependency conflicts
#   featureName: name of the feature
# ##########################################################
function depEnableAnalysis()
{
	if [[ ${DEBUG} == y ]]; then
		echo "-- ${FUNCNAME[0]} $@ --"
		set -x
	fi
	
	local featureName="$1"
	local featureDepJars featureDepJarPath depJarName multiVersionJars
	
	if [[ -z ${featureName} ]]; then
		echo "WARN: no feature name"
		return 1
	fi
	
	featureDepJars=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_DEPS}"/*.jar 2> /dev/null)
	for featureDepJarPath in ${featureDepJars}; do
		depJarName=$(basename "${featureDepJarPath}")
		
		# it could be a base jar

		if [[ -f "${LIB}"/"${depJarName}" ]]; then
			echo "WARN: dependency ${depJarName} already in use"
			continue
		fi
		
		# it could be a link from another feature

		if [[ -L "${LIB}"/"${depJarName}" ]]; then
			continue
		fi
		
		# unadvisable if multiple versions exist

		multiVersionJars=$(ls "${LIB}"/"${depJarName%%-[0-9]*.jar}"-*.jar 2> /dev/null)
		if [[ -n "${multiVersionJars}" ]]; then
			echo "WARN: other version of library ${depJarName} present: ${multiVersionJars}"
			return 2
		fi
	done
}

# ##########################################################
# configEnableAnalysis(featureName):  
#                   reports on potential dependency conflicts
#   featureName: name of the feature
# ##########################################################
function configEnableAnalysis()
{
	if [[ ${DEBUG} == y ]]; then
		echo "-- ${FUNCNAME[0]} $@ --"
		set -x
	fi
	
	local featureName="$1"
	local featureConfigs configPath configFileName
	
	if [[ -z ${featureName} ]]; then
		echo "WARN: no feature name"
		return 1
	fi
	
	featureConfigs=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_CONFIG}"/ 2> /dev/null)
	for configPath in ${featureConfigs}; do
		configFileName=$(basename "${configPath}")
		if [[ -e "${LIB}"/"${configFileName}" ]]; then
			echo "ERROR: a config file of the same name is already in the base: ${configFileName}"
			return 2
		fi
	done
}

# ##########################################################
# enableFeatureDeps(featureName):  
#                               enables feature dependencies
#   featureName: name of the feature
# ##########################################################
function enableFeatureDeps()
{
	if [[ ${DEBUG} == y ]]; then
		echo "-- ${FUNCNAME[0]} $@ --"
		set -x
	fi
	
	local featureName="$1"
	local featureDeps featureDepPath depJarName
	
	if [[ -z ${featureName} ]]; then
		echo "WARN: no feature name"
		return 1
	fi
	
	featureDeps=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_DEPS}"/*.jar 2> /dev/null)
	for featureDepPath in ${featureDeps}; do
		depJarName=$(basename "${featureDepPath}")
		if [[ ! -f "${LIB}"/"${depJarName}" ]]; then
			ln -s -f "${featureDepPath}" "${LIB}/"
		fi
	done
}

# ##########################################################
# enableFeatureConfig(featureName):  
#                               enables feature configuration
#   featureName: name of the feature
# ##########################################################
function enableFeatureConfig()
{
	if [[ ${DEBUG} == y ]]; then
		echo "-- ${FUNCNAME[0]} $@ --"
		set -x
	fi
	
	local featureName="$1"
	local featureConfigs featureConfigPath
	
	if [[ -z ${featureName} ]]; then
		echo "WARN: no feature name"
		return 1
	fi
	
	featureConfigs=$(find "${FEATURES}"/"${featureName}"/"${FEATURE_CONFIG}"/ -type f -maxdepth 1 2> /dev/null)
	for featureConfigPath in ${featureConfigs}; do
		ln -s -f "${featureConfigPath}" "${CONFIG}/"
	done 
}

# ##########################################################
# enableFeatureOp(featureName): 'enable' feature operation
#   featureName: name of the feature
# ##########################################################
function enableFeatureOp()
{
	if [[ ${DEBUG} == y ]]; then
		echo "-- ${FUNCNAME[0]} $@ --"
		set -x
	fi
	
	local featureName="$1"
	
	if [[ -z ${featureName} ]]; then
		echo "WARN: no feature name"
		return 1
	fi
	
	enableScript="${FEATURES}"/"${featureName}"/"${FEATURE_INSTALL}"/enable
	if [[ -f ${enableScript} ]]; then
		(
			cd "${FEATURES}"/"${featureName}"/"${FEATURE_INSTALL}"
			chmod u+x enable
			./enable
		)
	fi
}

# ##########################################################
# enableFeature(featureName, featureJar):  enables a feature
#   featureName: name of the feature
#   featureJar:  path to feature jar implementation
# ##########################################################
function enableFeature()
{
	if [[ $DEBUG == y ]]; then
		echo "-- ${FUNCNAME[0]} $@ --"
		set -x
	fi
	
	local featureName="$1"
	local featureJar="$2"
	
	if [[ -z ${featureName} ]]; then
		echo "WARN: no feature name"
		return 1
	fi
	
	if [[ -z ${featureJar} ]]; then
		echo "WARN: no feature jar"
		return 2
	fi
	
	if ! depEnableAnalysis "${featureName}"; then
		return 3
	fi
	
	if ! configEnableAnalysis "${featureName}"; then
		return 4
	fi
	
	# enable feature itself

	ln -s -f "${featureJar}" "${LIB}/"
		
	# enable dependent libraries if any
	
	enableFeatureDeps "${featureName}"
	
	# enable configuration

	enableFeatureConfig "${featureName}" 
	
	# TODO: run feature install DB scripts if any

	# run custom enable if any

	enableFeatureOp "${featureName}"
}

# ##########################################################
# disableFeatureDeps(featureName):  
#			disables feature dependencies
# ##########################################################
function disableFeatureDeps()
{
	if [[ ${DEBUG} == y ]]; then
		echo "-- ${FUNCNAME[0]} $@ --"
		set -x
	fi
	
	local featureName="$1"
	local xDepsEnabledMap featureBaseDirs aFeatureDir aFeatureName
	local featureDeps aFeatureDep 
	local depJarPath depJarName depJarRealPath
	
	if [[ -z ${featureName} ]]; then
		echo "WARN: no feature name"
		return 1
	fi
	
	declare -A xDepsEnabledMap
	
	featureBaseDirs=$(ls -d "${FEATURES}"/*/ 2> /dev/null)
	for aFeatureDir in ${featureBaseDirs}; do 
		aFeatureName=$(basename "${aFeatureDir}")
		if [[ "${aFeatureName}" == "${featureName}" ]]; then
			continue
		fi
		
		depJarPaths=$(ls "${aFeatureDir}"/"${FEATURE_DEPS}"/*.jar 2> /dev/null)
		for depJarPath in ${depJarPaths}; do
			if [[ "$?" == 0 ]] ; then
				depJarName=$(basename "${depJarPath}")
				xDepsEnabledMap[${depJarName}]="${depJarPath}"
			fi
		done
	done
	
	if [[ ${DEBUG} == y ]]; then
		echo "${!xDepsEnabledMap[@]}"
		echo "${xDepsEnabledMap[@]}"
	fi
	
	featureDeps=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_DEPS}"/*.jar 2> /dev/null)
	for aFeatureDep in ${featureDeps}; do
		depJarName=$(basename "${aFeatureDep}")
		if [[ -L "${LIB}"/"${depJarName}" ]]; then
			depJarRealPath=$(readlink -f "${LIB}"/"${depJarName}")
			if [[ "${depJarRealPath}" == "${aFeatureDep}" ]]; then
				rm -f "${LIB}"/"${depJarName}"
				
				# case there were multiple features using this library
				# re-enable link fron an enabled feature
		
				if [[ -n ${xDepsEnabledMap[${depJarName}]} ]]; then
					ln -s -f "${xDepsEnabledMap[${depJarName}]}" "${LIB}/"
				fi
			fi
		fi		
	done
}

# ##########################################################
# disableFeatureConfig(featureName):  
#                               disables feature configuration
#   featureName: name of the feature
# ##########################################################
function disableFeatureConfig()
{
	if [[ ${DEBUG} == y ]]; then
		echo "-- ${FUNCNAME[0]} $@ --"
		set -x
	fi
	
	local featureName="$1"
	local featureConfigs featureConfigPath
	
	if [[ -z ${featureName} ]]; then
		echo "WARN: no feature name"
		return 1
	fi
	
	featureConfigs=$(find "${FEATURES}"/"${featureName}"/"${FEATURE_CONFIG}"/ -type f -maxdepth 1 2> /dev/null)
	for featureConfigPath in ${featureConfigs}; do
		configFileName=$(basename "${featureConfigPath}")
		rm -f "${CONFIG}"/"${configFileName}" 2> /dev/null
	done 
}

# ##########################################################
# disableFeatureOp(featureName): 'enable' feature operation
#   featureName: name of the feature
# ##########################################################
function disableFeatureOp()
{
	if [[ ${DEBUG} == y ]]; then
		echo "-- ${FUNCNAME[0]} $@ --"
		set -x
	fi
	
	local featureName="$1"
	
	if [[ -z ${featureName} ]]; then
		echo "WARN: no feature name"
		return 1
	fi
	
	disableScript="${FEATURES}"/"${featureName}"/"${FEATURE_INSTALL}"/disable
	if [[ -f ${disableScript} ]]; then
		(
			cd "${FEATURES}"/"${featureName}"/"${FEATURE_INSTALL}"
			chmod u+x disable
			./disable
		)
	fi
}

# ##########################################################
# disableFeature(featureName, featureJar):  enables a feature
#   featureName: name of the feature
# ##########################################################
function disableFeature()
{
	if [[ ${DEBUG} == y ]]; then
		echo "-- ${FUNCNAME[0]} $@ --"
		set -x
	fi
	
	local featureName="$1"
	
	if [[ -z ${featureName} ]]; then
		echo "WARN: no feature name"
		return
	fi
	
	# disable feature itself

	(
	cd "${LIB}"
	rm -f feature-"${featureName}"-[0-9]*.jar 2> /dev/null
	)
		
	# disable dependencies if any

	disableFeatureDeps "${featureName}"
	
	# disable configuration if any

	disableFeatureConfig "${featureName}"
	
	# run feature uninstall DB scripts if any
	# TODO: future

	# run custom disable if any
	disableFeatureOp "${featureName}"
}

case "$1" in
	status)
	{
		# dump out status information
		status
	};;

	enable)
	{
		if [[ -f "${POLICY_HOME}"/PID ]]; then
			echo "ERROR: enable: not allowed when policy is running .."
			echo
			status
			exit 10
		fi
		
		# enable the specified options
		shift
		match=
		for name in "$@" ; do
			# look for matches - 'file' has the full path name
			file=$(ls "${FEATURES}"/"${name}"/"${FEATURE_LIB}"/feature-"${name}"-[0-9]*.jar 2> /dev/null)
			if [[ "$?" != 0 ]] ; then
				# no matching file
				echo "${name}:  no such option"
			else
				# make sure there is only one feature jar
				countFeatureJars=$(echo "${file}" | wc -w)
				if [[ ${countFeatureJars} != 1 ]]; then
					echo "WARNING: skipping ${name},  ${countFeatureJars} feature libraries found"
					continue
				fi			
				
				# found a match (handle multiple matches, just in case)
				match=true
				
				enableFeature "${name}" "${file}"
			fi
		done
		if [[ "${match}" ]] ; then
			echo
			status
		fi
	};;

	disable)
	{
		if [[ -f "${POLICY_HOME}"/PID ]]; then
			echo "ERROR: disable: not allowed when policy is running .."
			echo
			status
			exit 11
		fi
		
		# disable the specified options
		shift
		match=
		for name in "$@" ; do
			# look for matches -- 'file' has the last segment of the path name
			file=$(ls "${FEATURES}"/"${name}"/"${FEATURE_LIB}"/feature-"${name}"-[0-9]*.jar 2> /dev/null)
			if [[ "$?" != 0 ]] ; then
				echo "${name}:  no such option"
			else
				# found a match (handle multiple matches, just in case)
				match=true
				
				disableFeature "${name}"
			fi
		done
		if [[ "${match}" ]] ; then
			echo
			status
		fi
	};;

	*)
	{
		usage
	};;
esac
exit