#!/bin/sh
#set -x
#IS_DEBUG=1

IS_ROOT=/tmp/is-backup
APP_LIST_FILE=/etc/istore/app.list
BACKUP_CONFIG_FILE=/etc/config/istore

action=${1}
shift


is_init() {
    mkdir -p ${IS_ROOT}
}

opkg_list_installed_packages() {
    local target=$1
    local dir
    local OPKG_INFO_DIR
    case $target in
        "preinstalled")
            OPKG_INFO_DIR="/rom/usr/lib/opkg/info"
        ;;
        "userinstalled")
            OPKG_INFO_DIR="/overlay/upper/usr/lib/opkg/info /ext_overlay/upper/usr/lib/opkg/info"
        ;;
        "allinstalled")
            OPKG_INFO_DIR="/usr/lib/opkg/info"
        ;;
        *)
            echo "invalid target"
            exit
        ;;
    esac
    for dir in $OPKG_INFO_DIR ; do
        [ -d "$dir" ] || continue
        (cd "$dir" && find . -depth -maxdepth 1 -name "*.list" -type f | sed 's#^\./\(.*\)\.list$#\1#g')
    done
}

ipk_build() {
    PKG_NAME_TEMP=$1
    IPK_OUTPUT_DIR=$2

    UCI_BAK_DIR="/etc/istore/uci-defaults_bak/"
    UCI_DEF_DIR="etc/uci-defaults"
    local OPKG_INFO_DIR="/usr/lib/opkg/info/"

    [ -n "${PKG_NAME_TEMP}" ] || exit 1
    #get real pkg name in opkg
    PKG_NAME_TEMP=`cat ${IS_ROOT}/all_installed_package.list | sort -u | grep "^${PKG_NAME_TEMP}" | head -n 1`
    [ -n "${PKG_NAME_TEMP}" ] || exit 1

    PKG_NAME=`cat ${OPKG_INFO_DIR}${PKG_NAME_TEMP}.control | grep "^Package: " |  cut -d ' ' -f2`
    PKG_VER=`cat ${OPKG_INFO_DIR}${PKG_NAME}.control | grep "^Version: " |  cut -d ' ' -f2`
    PKG_ARCH=`cat ${OPKG_INFO_DIR}${PKG_NAME}.control | grep "^Architecture: " |  cut -d ' ' -f2`
    IPK_FILE_NAME="${PKG_NAME}_${PKG_VER}_${PKG_ARCH}"

    rm -rf ${IS_ROOT}/${IPK_FILE_NAME}
    mkdir -p ${IS_ROOT}/${IPK_FILE_NAME}

    #(1)make CONTROL dir; (2)copy control file to dir
    cd ${IS_ROOT}/${IPK_FILE_NAME}
    mkdir -p CONTROL
    for control_file in `ls  ${OPKG_INFO_DIR}${PKG_NAME}.* | grep -v ".list$"`; do
        file=${control_file##*/}
        suffix=${file##*.}
        cp ${control_file} CONTROL/${suffix}
    done

    #(1)make DATA depend dir; (2)copy uci-defaults_bak file to dir; (3)copy other file to dir
    for pkgfile in `cat ${OPKG_INFO_DIR}${PKG_NAME}.list | cut -b 2-`; do
        file=${pkgfile##*/}
        path=${pkgfile%/*}
        mkdir -p ${path}
        if [ `echo "${path}" | grep "^${UCI_DEF_DIR}"` ]; then
            cp "${UCI_BAK_DIR}${file}" "${pkgfile}"
        else
            cp "/${pkgfile}" "${pkgfile}"
        fi
    done

    #call ipkg-build script to build ipk
    /usr/libexec/istore/ipkg-build ${IS_ROOT}/${IPK_FILE_NAME} ${IPK_OUTPUT_DIR}
    echo "${IPK_FILE_NAME}.ipk" >> ${IPK_OUTPUT_DIR}/appdepipk.list

    [ -n "${IS_DEBUG}" ] || rm -rf ${IS_ROOT}/${IPK_FILE_NAME}
}

# if arg is NULL, use light backup, otherwise use local backup
backup() {
    [ -n "$1" ] && BACKUP_PATH=$1

    #1.add all istore self data to sysupgrade config file,
    #sysupgrade will backup/restore it auto when flash new firmware
    echo "/etc/.app_store.id" > /lib/upgrade/keep.d/luci-app-store
    cat /usr/lib/opkg/info/luci-app-store.list >> /lib/upgrade/keep.d/luci-app-store
    echo "/etc/rc.d/S45istore" >> /lib/upgrade/keep.d/luci-app-store
    echo "/etc/istore/uci-defaults_bak" >> /lib/upgrade/keep.d/luci-app-store
    echo "${APP_LIST_FILE}" >> /lib/upgrade/keep.d/luci-app-store
    echo "${BACKUP_CONFIG_FILE}" >> /lib/upgrade/keep.d/luci-app-store

    #write user installed package list to file
    opkg_list_installed_packages "userinstalled" 2>/dev/null | sort -u > ${IS_ROOT}/user_installed_package.list

    #write installed package list by istore feed to file
    cat ${IS_ROOT}/user_installed_package.list | \
        grep '^app-meta-' > ${IS_ROOT}/istore_installed_package.list

    #if no input backup path, only back app.list
    mkdir -p /etc/istore
    cp ${IS_ROOT}/istore_installed_package.list ${APP_LIST_FILE}
    echo "backup installed package list to ${APP_LIST_FILE}"

    if [ ! -n "${BACKUP_PATH}" ]; then
        echo "backup success"
        exit 0
    fi

    if [ ! -d "${BACKUP_PATH}" ] && ! mkdir -p "${BACKUP_PATH}" ; then
        echo "invalid backup path, can not backup ipk"
        exit 1
    fi

    #write all installed package list to file
    opkg_list_installed_packages "allinstalled" 2>/dev/null | sort -u > ${IS_ROOT}/all_installed_package.list

    #write system pre installed package list to file
    opkg_list_installed_packages "preinstalled" 2>/dev/null | sort -u > ${IS_ROOT}/pre_installed_package.list

    #write installed packages and depends list by istore feed to file by depend sequence
    appdep_list=""
    temp_list=`cat ${IS_ROOT}/istore_installed_package.list | sed 's/^/\t/'`
    while [ -n "${temp_list}" ]
    do
        #get real pkg name
        for PKG_NAME_TEMP in ${temp_list}; do
            REAL_PKG_NAME=`cat ${IS_ROOT}/all_installed_package.list | sort -u | grep "^${PKG_NAME_TEMP}" | head -n 1`
            if [ "${REAL_PKG_NAME}" != "${PKG_NAME_TEMP}" ]; then
                temp_list=`echo "${temp_list}" | sed 's/^\t'"${PKG_NAME_TEMP}"'$/\t'"${REAL_PKG_NAME}"'/'`
            fi
        done

        appdep_list=`echo -e "${temp_list}\n${appdep_list}"`
        [ -n "${IS_DEBUG}" ] && echo -e "temp_list:\n""${temp_list}"
        [ -n "${IS_DEBUG}" ] && echo -e "appdep_list:\n""${appdep_list}"

        temp_list=`echo "${temp_list}" | xargs opkg depends | grep -v "depends on:" | grep -v " (>= " | grep -v " (= " | sort -u`
    done

    appdep_list_all=`echo "${appdep_list}" | cut -f2 | grep -v "^$" | awk '!seen[$0]++'`
    [ -n "${IS_DEBUG}" ] && echo -e "appdep_list_all:\n""${appdep_list_all}"
    echo "${appdep_list_all}" > ${IS_ROOT}/appdep.list

    #3.rebuild all istore installed package to ipk and backup to userdata partation

    # 4. create dir
    date=$(date +%Y-%m%d-%H%M)
    if [ ! -d "$BACKUP_PATH/backup_istore_$date" ];then
        mkdir $BACKUP_PATH/backup_istore_$date
    fi
    cp ${IS_ROOT}/istore_installed_package.list $BACKUP_PATH/backup_istore_$date/app.list
    cp ${IS_ROOT}/appdep.list $BACKUP_PATH/backup_istore_$date/appdep.list

    #only backup non pre installed ipk
    cp ${IS_ROOT}/appdep.list ${IS_ROOT}/appdep_strip.list
    for pre_installed_pkg in `cat ${IS_ROOT}/appdep.list ${IS_ROOT}/pre_installed_package.list | sort -n | uniq -d`; do
        sed -i '/^'"$pre_installed_pkg"'$/d' ${IS_ROOT}/appdep_strip.list
    done

    rm -f $BACKUP_PATH/backup_istore_$date/appdepipk.list
    echo "build ipk"
    for pkg_name in `cat ${IS_ROOT}/appdep_strip.list`; do
        ipk_build ${pkg_name} $BACKUP_PATH/backup_istore_$date
    done

    # 5. create tar.gz file,and remove dir
    cd $BACKUP_PATH
    echo "write backup file to $BACKUP_PATH/backup_istore_$date.backup.tar.gz"
    tar -czf $BACKUP_PATH/backup_istore_$date.backup.tar.gz backup_istore_$date
    rm -rf $BACKUP_PATH/backup_istore_$date
    echo "backup success"
}

# if arg is NULL, use light backup, otherwise use local backup
restore() {
    if [ -n "$1" ]; then
        BACKUP_PATH_FILE=$1
    else
        echo "install package by ${APP_LIST_FILE}"
        is-opkg update
        for app in `cat ${APP_LIST_FILE}`; do
            #skip resotre istore self
            [ "A${app}" == "A""luci-app-store" ] && continue
            is-opkg install ${app}
        done
        exit 0
    fi

    if [ ! -f "${BACKUP_PATH_FILE}" ];then
        echo "invalid backup file, can not restore ipk"
        exit 1
    fi

    #1. Unzip file to dir
    BACKUP_PATH_FILE_NAME=${BACKUP_PATH_FILE##*/}
    BACKUP_PATH=/tmp/${BACKUP_PATH_FILE_NAME%.backup.tar.gz*}
    if [ -d "$BACKUP_PATH" ];then
        rm -rf $BACKUP_PATH
    fi
    mkdir -p $BACKUP_PATH
    echo "unpack input file..."
    # fix tar path error
    tar -zxf ${BACKUP_PATH_FILE} -C /tmp/

    echo "check file"
    if [ ! -f "${BACKUP_PATH}/appdep.list" ];then
        echo "no available appdep.list, can not restore ipk"
        exit 1
    fi
    echo "check success"

    #2. install ipk by backup path
    echo "restore begin"
    ( cd ${BACKUP_PATH}; opkg install `cat ${BACKUP_PATH}/appdepipk.list` )

    #3. rm dir
    rm -rf ${BACKUP_PATH}
    echo "restore success"
}

get_support_backup_features() {
    echo "light_backup"
    #istore custom img mean support local_backup
    if [ -f /etc/istore_img_flag ];then
        echo "local_backup"
    fi
}

get_backup_app_list_file_path() {
    echo "${APP_LIST_FILE}"
}

get_backup_app_list() {
    if [ ! -f "${APP_LIST_FILE}" ];then
        echo "no app.list, can not get backup app list"
        exit 1
    fi
    cat ${APP_LIST_FILE}
}

get_available_backup_file_list() {
    local backup_file
    if [ -n "$1" ]; then
        for backup_file in `cd $1 && ls backup_istore_*.backup.tar.gz`; do
            echo "${backup_file}"
        done
    else
        echo "input backup path is null"
        exit 1
    fi
}

usage() {
    echo "usage: backup sub-command [arguments...]"
    echo "where sub-command is one of:"
    echo "      backup [dir]                    Backup all installed package(s) to [directory]"
    echo "      restore [dir]                   Restore package(s) by [directory]"
    echo "      get_support_backup_features     get device support backup features"
    echo "      get_backup_app_list_file_path   get light backup app list file path"
    echo "      get_backup_app_list             get light backup app list"
    echo "      get_available_backup_file_list  get local available backup file list"
}

is_init >/dev/null 2>&1

case $action in
    "get_support_backup_features")
        get_support_backup_features
    ;;
    "backup")
        backup "$@"
    ;;
    "restore")
        restore "$@"
    ;;
    "get_backup_app_list_file_path")
        get_backup_app_list_file_path
    ;;
    "get_backup_app_list")
        get_backup_app_list
    ;;
    "get_available_backup_file_list")
        get_available_backup_file_list "$@"
    ;;
    *)
        usage
    ;;
esac
