#!/bin/bash

# GETTEXT_KEYWORD="gt"
# GETTEXT_KEYWORD="pfgt"
# GETTEXT_KEYWORD="ctitle"
# GETTEXT_KEYWORD="fmt_size"
# GETTEXT_KEYWORD="error_box_pf"

source /usr/local/lib/antiX/antiX-common.sh  "$@"

SAFETY_MARGIN=10

# Files listed on the main screen
ACTIVE_FILES="rootfs rootfs.bak homefs homefs.new homefs.old rootfs.old linuxfs.old"

# Files we will delete even if good
OLDEN_FILES="rootfs.tmp rootfs.bak homefs.old rootfs.old linuxfs.old"

# Files we will only delete after e2fsck fails
LIVE_FILES="rootfs homefs homefs.new"

CONF_FILE=/live/config/linuxrc.out

TITLE="$(gt "antiX Create or Resize Persistence")"
BLURB="$(gt "Create or resize the files need for root and/or home persistence.")"

EXTRA_USAGE=$(cat <<End_Extra_Usage
\n[b]$ME specific options:[/]
    -d|--dev=<[p]device[/]>  $(pfgt "Use <%s> as the persistence device" "[p]device[/]")
End_Extra_Usage
)

     CHANGE_DEV="Device: $(gt "Change Persistence Device")"
    CHANGE_PATH="Path: $(gt "Change Peristence Path")"
    MAKE_ROOTFS="Root: $(gt "Create Root Persistence")"
  RESIZE_ROOTFS="Root: $(gt "Resize Root Persistence")"
MAKE_ROOTFS_NEW="New: $(gt "Make rootfs.new file for remastering")"
    MAKE_HOMEFS="Home: $(gt "Create Home Persistence")"
  RESIZE_HOMEFS="Home: $(gt "Resize Home Persistence")"
#   HELP_CHOICE="?: $(gt "Show some help")"
           QUIT="Quit: $(gt "Quit this program")"
  UPDATE_CHOICE="Update: $(gt "Update information")"
     FIX_CHOICE="Fix: $(gt "Fix or delete broken and outdated files")"

         FIX_IT="$(gt "Fix")"
      DELETE_IT="$(gt "Delete")"
        EXPLORE="$(gt "Explore")"

   EXPLORE_MENU="$(gt "Exploration Menu")"
      EXPLORING="$(gt "Exploring")"
    EXPLORE_DEV="$(gt "Persistence Device")"
    EXPLORE_DIR="$(gt "Persistence Directory")"
   EXPLORE_FILE="$(gt "Persistence file: %s")"
 STOP_EXPLORING="$(gt "Stop Exploring")"

    STOP_FIXING="$(gt "Stop Fixing and deleting")"
 FIX_FILES_MENU="$(gt "Fix and Delete Files Menu")"

 DOES_NOT_EXIST="$(gt "does not exist")"
         BROKEN="$(gt "Broken!")"

  FTYPE_CHOICES=(
            "ext2: $(gt "Simple, stable, no journaling")"
            "ext3: $(gt "Adds journaling.")"
            "ext4: $(gt "Journaling, checksums, fast fsck")" 
        )

OPTIONS="
   d,device|o|DEV
     f,fast||FAST
  p,pretend||PRETEND
"

add_options "$OPTIONS"
read_options "$@"

SYS_TYPE="LiveCD/USB/HD"
conf_file=$CONF_DIR/linuxrc.out
read_conf $conf_file || read_conf_error rootfs $conf_file

show_cli_title
need_root
start_logging
trap clean_up EXIT
create_lock

# push_text() {
#     for t in "$@"; do
#         MAIN_TEXT[${#MAIN_TEXT[*]}]="$t"
#     done
# }

select_ftype() {
    local choice ftype
    for ftype in "${FTYPE_CHOICES[@]}"; do
        choice="$choice!$ftype"
    done
    combo_box "$(gt "File System Type")" "$choice" "" ""
    FTYPE="$(echo $UI_RESULT | cut -d: -f1)"

}

make_filefs() {
    local file="$1"
    local ptype="$2"
    local min_size="$3"
    local max_size="$4"
 
    vmsg "make_filefs(\"$1\", \"$2\", \"$3\", \"$4\")"
    if [ -f "$FULL_PATH/$file" ]; then
        yes_no_box "$TITLE" ""                                       \
            "$(pfgt "The file %s already exists" "[f]$file[/]")" ""  \
            "${INFO_HASH[$file]}" ""                                 \
            "$(gt "Do you want to over-write it?")" ""               \
            || return 1
    fi

    local text=(
        "$(gt "Please select for the new partition")"
        ""
        "$(pfgt "For %s persistence" "[b]$ptype[/]")"
    )
    if ! select_size -min "$min_size" -Max "$max_size" "$msg"; then
        warn_box "$TITLE" "" \
            "$(pfgt "Not enough space on device to %s" "$ptype")" "" 

        return
    fi
    local size="$UI_RESULT"

    select_ftype
    vmsg "FTYPE=$FTYPE"

    make_path_if_needed "$DEV_MP" "$P_PATH"

    _make_filefs "$DEV_MP" "$P_PATH" "$file" "$size" "$FTYPE"  "$opts"
}

_make_filefs() {
    local mp="$1" dir="$2" name="$3" size="$4" type="$5" opts="$6"
    shift 6

    noisy_yes_no_box "$TITLE" "" \
        "$(pfgt "About to create %s file" "[f]$name[/]")" ""   \
        "$(pf "%16s: %s" "`gt size`"      "[n]$size[/] Meg")"  \
        "$(pf "%16s: %s" "`gt type`"      "[b]$type[/]")"      \
        "$(pf "%16s: %s" "`gt directory`" "[f]$dir[/]")" ""    \
        "$(pf "%16s: %s" "`gt device`"    "[f]$DEV[/]")" ""    \
        "$(gt "Shall we proceed?")"                            \
        || return

    local full=$mp/$dir/$name
    dd if=/dev/zero of=$full bs=1M count=0 seek=$size
    mkfs -q -t $type $opts -F $full && MADE_FS="$name"
}

copy_filefs() {
    local from="$1"
    local dest="$2"
    local size="$3"
    vmsg "copy_filefs(\"$1\", \"$2\", \"$3\")"

    from_dir=$(make_temp_dir copy-from)
    dest_dir=$(make_temp_dir copy-dest)
    if ! mount_if_needed -o loop "$from" "$from_dir"; then
        rmdir $from_dir $dest_dir
        return 1
    fi
    if ! mount_if_needed -o loop "$dest" "$dest_dir"; then
        my_umount $from_dir
        rmdir $from_dir $dest_dir
        return 2
    fi

    bg_info_box -o "$PULSATE" "$TITLE" ""                                        \
        "$(pfgt "Please be patient while %s megs are copied ..." "[n]$size[/]")" \

    cp -a $from_dir/* $dest_dir
    #rsync --progress -a --delete "$from_dir" "$dest_dir"
    my_umount $from_dir
    my_umount $dest_dir
    rmdir $from_dir $dest_dir
    kill_bg_info_box
    return 0
}

change_device() {
    while true; do

        unmount_device || error_box "$(pfgt "Was unable to unmount %s at %s" "[f]$DEV[/]" "[f]$DEV_MP[/]")"

        select_device -a "$TITLE" "" \
            "$(gt "Please select a new persistence device")" ""\
            "$(pfgt "Current device is: %s" "[f]$DEV[/]")" ""

        DEV=$UI_RESULT

        mount_device $DEV

        vmsg "Device: [n]$DEV[/],   Mountpoint: [f]$DEV_MP[/]"

        is_readwrite_mp $DEV_MP && break

        okay_box "$TITLE" \
            "$(pfgt "The selected device %s is read-only" "[f]$DEV[/]")" "" \
            "$(pfgt "Please select a different device")" ""
    done
}

change_path() {
    [ "$DISTRO" ]      || DISTRO="antiX"
    [ "$FULL_DISTRO" ] || FULL_DISTRO="antiX-12"
    local choice="$DISTRO!$FULL_DISTRO"

    local cnt=0
    for dir in $(ls $DEV_MP); do
        [ -d "$DEV_MP/$dir" ] || continue
        echo $dir | egrep -q "!" && continue
        echo $dir | egrep -q "^($DISTRO|$FULL_DISTRO|lost.found|boot|...)$" && continue

        choice="$choice!$dir"
        cnt=$(( $cnt + 1 ))
        [ "$cnt" -ge 20 ] && break
    done
    local custom=">>> $(gt "new") <<<"
    choice="$choice!$custom"

    combo_box "Path" "$choice" "$TITLE" "" \
        "$(gt "Please choice an new persistence directory")" "" \
        "$(pfgt "The current choice is %s" "[f]$P_PATH[/]")" ""

    if [ "$UI_RESULT" != "$custom" ]; then
        P_PATH="$UI_RESULT"
        return 0
    fi

    get_text "$TITLE" "" \
        "$(gt "Please select a new persistence directory")" "" \
        "$(pfgt "The current one is %s" "[f]$P_PATH[/]")" "" 

    P_PATH="$(echo "$UI_RESULT" | sed 's/!//g')"
}

make_path_if_needed() {
    local mp="$1"
    local path="$2"
    local full_path="$1/$2"
    [ -d "$full_path" ] && return 0

    if [ -e "$full_path" ]; then
        warn_box "$TITLE" ""                                                                \
            "$(pfgt "Expected a directory at %s but found a file." "[f]$full_path[/]")"  "" \
            "$(gf "Shall I try to erase it?")"                                           "" \
            "$(gt "The only other option is to quit.")"                                  "" \
            || error_box "$($pfgt "Not erasing %s at user's request." "[f]$full_path[/]")"

        vmsg "rm -f [f]$full_path[/]"
        rm -f $full_path
        [ -e "$full_path" ] && error_box "$(pfgt "Unable to erase file %s" "[f]$full_path[/]")"
    fi

    vmsg "mkdir [f]$full_path[/]"
    # FIXME: ask before making directory?
    mkdir $full_path
    [ -d "$full_path" ] || error_box "$(pfgt "Unable to make directory %s" "[f]$full_path[/]")"

}

fix_or_delete() {
    local ui_result="$1"
    local file=$(echo $ui_result | awk '{print $NF}')
    local type=$(echo $ui_result | cut -d: -f1)
    local full=$FULL_PATH/$file

    vmsg "fix_or_delete: type=$type file=$file"

    if [ "$type" = "$FIX_IT" ]; then
        e2fsck -p $full
        local ret=$?
        case "$ret" in
             0)
                info_box "$(pfgt "There were no errors to correct in %s." "[f]$file[/]")"
                ;;
            1|2)
                info_box "$(pfgt "File system in %s should be fixed." "[f]$file[/]")"
                ;;
            4)
                info_box"$(pfgt "File system in %s left uncorrected." "[f]$file[/]")" "" \
                    "$(gt "You may be able to fix this manually with the command:")" "" \
                    "[fixed]e2sfck $full[/]" ""
                ;;
            *)
                yes_no_box "$(pfgt "Failed to fix %s" "[f]$file[/]")" "" \
                    "$(pfgt "Shall I delete the file: %s?" "[f]$full[/]")" "" \
                    || return

                rm -f $full
                ;;
        esac

    elif [ "$type" = "$DELETE_IT" ]; then
        noisy_yes_no_box "$TITLE" "" "$(pfgt "Delete file %s?" "[f]$file[/]")" "" || continue
        rm -f $full
    else
        warn_box "Internal error in fix_or_delete($ui_result)"
        return
    fi

}

fix_files_menu() {
    local choice file

    while true; do
        if ! [ "$BROKEN_FS" -o "$STALE_FILES" ]; then
            info_box ""                         \
            "$(gt "No more files left to fix or delete")" "" 

            return

        fi

        unset choice
        for file in $BROKEN_FS; do
            [ -e "$FULL_PATH/$file" ] || continue
            choice="$choice!$FIX_IT: $file"
        done

        for file in $STALE_FILES; do
            [ -e "$FULL_PATH/$file" ] || continue
            choice="$choice!$DELETE_IT: $file"
        done

        if ! [ "$choice" ]; then
            info_box "$FIX_CHOICE" ""                              \
            "$(gt "Strange.  No more files left to fix or delete")" "" 

            return
        fi

        choice="$choice!$STOP_FIXING!$QUIT"
        combo_box "$FIX_FILES_MENU" "$choice" "" \
            "${FILE_TEXT[@]}"  ""                  

        case "$UI_RESULT" in

       "$STOP_FIXING")
            return
            ;;
        "$QUIT")
            noisy_yes_no_box "$TITLE" ""  "$(gt "Really Quit?")" && exit
            ;;
        *)
            fix_or_delete "$UI_RESULT"

            unset INFO_HASH USED_SPACE
            declare -A INFO_HASH USED_SPACE
            gather_file_info
            ;;
        esac

    done
}

explore_menu() {
    while true; do
        local choice="$EXPLORE_DEV"
        if [ -d "$FULL_PATH" ]; then
            choice="$choice!$EXPLORE_DIR"
            for file in $GOOD_FS; do
                choice="$choice!$(pf "$EXPLORE_FILE" "$file")"
            done
        else
            echo "FIXME:"
        fi

        choice="$choice!$STOP_EXPLORING!$QUIT"

        vmsg "choice=$choice"
        combo_box "$EXPLORE_MENU" "$choice" "" \
            "${MAIN_TEXT[@]}" "" "$(ctitle "Exploration Menu")" ""

        case "$UI_RESULT" in
        "$EXPLORE_DEV")
            explore_dir "$DEV_MP" "$EXPLORING device=[p]$DEV[/]"
            ;;
        "$EXPLORE_DIR")
            explore_dir "$FULL_PATH" "$EXPLORING [f]/$P_PATH[/] on [p]$DEV[/]"
            ;;
        "$STOP_EXPLORING")
            return
            ;;
        "$QUIT")
            noisy_yes_no_box "$TITLE" "$(gt "Really Quit?")" && exit
            ;;
        *)
            file=$(echo $UI_RESULT | awk '{print $NF}')
            vmsg "Exploring file: [f]$file[/]"
            explore_filefs $file
            ;;    
        esac
    done
}

explore_filefs() {
    local file="$1"
    local full="$FULL_PATH/$file"
    if ! [ -e "$full" ]; then
        warn_box "$TITLE" "" \
            "$(pfgt "Strange.  Could not find file %s" "[f]$full[/]")" "" \
            "$(gt "Cannot explore what we cannot find.")" ""
        
        return
    fi

    local dir=$(make_temp_dir $file)
    if ! mount_if_needed -o loop $full $dir; then
        rmdir $dir
        return
    fi

    explore_dir $dir "$EXPLORING [f]/$P_PATH/$file[/] on [p]$DEV[/]"
    my_umount $dir
    rmdir $dir
}

mk_filefs_info() {
    local file="$1"
    local full="$2"
    local info="$(pf "[f]%12s[/]" "$file")"

    if ! [ -e "$full" ]; then
        INFO_HASH[$file]="$info: [p]$DOES_NOT_EXIST[/]"
        return
    fi

    info="$info: [b]$(date -r $full '+%T %D')[/]"
    info="$info : $(pf "[n]%4d[/] meg" `du_size $full`) $(gt "total")"

    USED_SPACE[$file]="0"
    # if [ "$OPT_FAST" ]; then
    #     INFO_HASH[$file]="$info"
    #     return
    # fi

    local do_umount
    local mp=$(get_mountpoint $full)
    if ! [ "$mp" ]; then
        mp=$(make_temp_dir $file)
        if ! mount -o loop,ro $full $mp &>/dev/null; then
            BROKEN_FS="$BROKEN_FS $file"
            INFO_HASH[$file]="$info : [e]$BROKEN[/]"
            rmdir $mp
            return
        fi
        do_umount="true"
    fi

    GOOD_FS="$GOOD_FS $file"
    info="$info : [p]$(fs_type $mp)[/]"
    info="$info : [n]$(used_space $mp)[/] meg"
    local percent="$(pf "%3s" `fs_percent $mp`)"
    INFO_HASH[$file]="$info ([n]$percent[/]) $(gt "Used")"
    USED_SPACE[$file]=$(used_space $mp)

    [ "$do_umount" ] || return

    my_umount $mp || error_box "Could not umount $file at $mp"
    rmdir $mp
}

mount_device() {
    local dev="$1"

    DEV_MP=$(get_mountpoint $dev)
    if [ "$DEV_MP" ]; then
        unset UNMOUNT_DEV
    else
        DEV_MP=$(make_temp_dir mount-point)
        UNMOUNT_DEV="true"
        mount_if_needed $dev $DEV_MP || return 1
    fi
    return 0
}

unmount_device() {
    [ "$UNMOUNT_DEV" ] || return 0
    my_umount $DEV_MP || return 1
    rmdir $DEV_MP
    return 0
}

#------------------------------------------------------------------------------
# Main code starts here
#------------------------------------------------------------------------------

# return after errors in mount_if_needed()
nonfatal_mount_errors

P_PATH=$PERSIST_PATH
[ "$P_PATH" ] || P_PATH="$SQFILE_PATH"
[ "$P_PATH" ] || P_PATH="$DISTRO"

DEV=$PERSIST_DEV
[ "$DEV" ] || DEV="$BOOT_DEV"
[ "$OPT_DEV" ] && DEV="$OPT_DEV"

if ! [ -b "$DEV" ]; then
    okay_box  "$TITLE" "" \
        "$(pfgt "Default device %s is either missing or invalid" "[f]$DEV[/]")" "" \
        "$(gt   "Please select a new device for the persistence files")"  ""

    change_device

elif ! mount_device $DEV; then

    change_device

fi

vmsg "Device: [n]$DEV[/],   Mountpoint: [f]$DEV_MP[/]"

if is_readonly_mp $DEV_MP; then
    okay_box     "$TITLE" ""                                                   \
        "$(pf    "Read-only device found")"                                    \
        "$(pfgt  "The boot/persist device %s is read-only." "[f]$DEV[/]")" ""  \
        "$(gt    "Please select a new device for the persistence files")"  ""

    change_device
fi


gather_device_info() {
    FULL_PATH="$DEV_MP/$P_PATH"

    FS_TOTAL=$(all_space  $DEV_MP)
    FS_AVAIL=$(free_space $DEV_MP)
    SAFE_AVAIL=$(( $FS_AVAIL - $SAFETY_MARGIN ))
    
    TOTAL_RAM=$(ram_total)
    SAFE_RAM=$(($TOTAL_RAM + $SAFETY_MARGIN))

    MAX_SIZE=$(min_of $SAFE_RAM $SAFE_AVAIL)

    DEVICE_TEXT=(                                                    \
        "[fixed]"                                                    \
        "$(fmt_size "total space on device"  $FS_TOTAL)"             \
        "$(fmt_size "available space"        $FS_AVAIL)"             \
        "$(fmt_size "total RAM"              $TOTAL_RAM)"            \
        ""                                                           \
        "$(pf "%20s: %s" "`gt "persist device"`"  "[f]$DEV[/f]")"    \
        "$(pf "%20s: %s" "`gt "persist path"`"    "[f]/$P_PATH[/]")" \
        "[/]"                                                        \
        )
}

gather_file_info() {

    unset BROKEN_FS GOOD_FS MADE_FS STALE_FILES

    FILE_TEXT=("[fixed]")

    for file in $ACTIVE_FILES; do   
        mk_filefs_info $file $FULL_PATH/$file
        push_array FILE_TEXT "${INFO_HASH[$file]}"
        echo $file | grep -q rootfs || continue

        file_size=$(du_size $FULL_PATH/$file)

        [ "$file_size" -ge "$SAFE_RAM" ] && \
            push_array FILE_TEXT "$(pfgt "(The %s file is already as large as all RAM)" "[f]$file[/]")"
    done
    push_array FILE_TEXT "[/]"

    for file in $OLDEN_FILES; do
        [ -e "$FULL_PATH/$file" ] || continue
        STALE_FILES="$STALE_FILES $file"
    done

}

while true; do

    unset INFO_HASH USED_SPACE
    declare -A INFO_HASH USED_SPACE

    gather_device_info
    gather_file_info

    CHOICE=""

    # Only present Make X if X does not already exist
    # Only present Resize X if X exists and is not broken

    if [ -e "$FULL_PATH/rootfs" ]; then

        case " $BROKEN_FS " in
        " rootfs ") 
            ;;
        *)
            CHOICE="$CHOICE!$RESIZE_ROOTFS"
            ;;
        esac
    else
        CHOICE="$CHOICE!$MAKE_ROOTFS"
    fi

    CHOICE="$CHOICE!$MAKE_ROOTFS_NEW"

    if [ -e "$FULL_PATH/homefs" ]; then
        case " $BROKEN_FS " in
        " homefs ") 
            ;;
        *)
            CHOICE="$CHOICE!$RESIZE_HOMEFS"
            ;;
        esac
    else
        CHOICE="$CHOICE!$MAKE_HOMEFS"
    fi

    for t in "${DEVICE_TEXT[@]}" "${FILE_TEXT[@]}" ; do
        vmsg "$t"
    done


    [ "$BROKEN_FS" -o "$STALE_FILES" ] && CHOICE="$CHOICE!$FIX_CHOICE"

    CHOICE="$CHOICE!$CHANGE_DEV!$CHANGE_PATH!$EXPLORE_MENU!$UPDATE_CHOICE!$QUIT"

    combo_box action "$CHOICE" -a  "$TITLE"  \
        "${DEVICE_TEXT[@]}"                  \
        "${FILE_TEXT[@]}"  ""                  

    case "$UI_RESULT" in

        "$MAKE_ROOTFS")
            vmsg "Make rootfs"
            make_filefs rootfs "$(gt "Create Root")" "" "$MAX_SIZE" 
            ;;

        "$RESIZE_ROOTFS")
            rootfs="$FULL_PATH/rootfs"
            if [ "$(get_mountpoint $rootfs)" ]; then
                okay_box -c "[e]Warning[/]" "" \
                    "$(gt "Can't resize %s while it is already mounted." "[f]rootfs[/]")" ""

                continue
            fi
            vmsg "Resize rootfs"
            make_filefs rootfs.tmp "$(gt "Resize Root")" "${USED_SPACE[rootfs]}" "$MAX_SIZE" 

            if ! [ "$MADE_FS" = "rootfs.tmp" ]; then
                noisy_yes_no_box "$TITLE" "" \
                    "$(gt "The resize rootfs has failed.")" \
                    "$(pfgt "Shall I delete the %s file?" "[f]rootfs.tmp[/]")" \
                    && rm -f $rootfs.tmp
                continue
            fi
            copy_filefs "$rootfs" "$rootfs.tmp" "${USED_SPACE[rootfs]}" || continue
            mv -f $rootfs $rootfs.bak
            mv -f $rootfs.tmp $rootfs
            ;;

        "$MAKE_ROOTFS_NEW")
            vmsg "Make rootfs.new"
            make_filefs rootfs.new "$(gt "Create rootfs.new (for remastering)")" "" "$MAX_SIZE" 
            ;;

        "$MAKE_HOMEFS")
            vmsg "Make homefs"
            make_filefs "homefs" "$(gt "Create Home")" "" "$SAFE_AVAIL" "$(gt "Create Home")"
            ;;

        "$RESIZE_HOMEFS")
            vmsg "Resize homefs"
            homefs="$FULL_PATH/homefs"
            make_filefs "homefs.new" "$(gt "Resize Home")" "" "$SAFE_AVAIL" "$(gt "Resize Home")"

            copy_filefs "$homefs" "$homefs.new" "${USED_SPACE[homefs]}"
            ;;

        "$CHANGE_DEV")
            vmsg "Change device"
            change_device
            ;;

        "$CHANGE_PATH")
            vmsg "Change path"
            change_path
            ;;

        "$EXPLORE_MENU")
            vmsg "Explore menu"
            explore_menu
            ;;
        "$FIX_CHOICE")
            fix_files_menu
            ;;
        "$QUIT")
            #exit
            noisy_yes_no_box "$TITLE" "$(gt "Really Quit?")" && exit
            ;;
    esac

done


