#!/usr/bin/env bash

# Copyright (C) 2008-2013 Bob Hepple
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

# http://bhepple.freeshell.org

usage() {
    local FLAGS=$(print_short_flags)
    [ "$FLAGS" ] && FLAGS="[-$FLAGS]"
    local LFLAGS=$(print_long_flags)
    [ "$LFLAGS" ] && LFLAGS=" [$LFLAGS]"
    local ARGS=$(print_all_args)
    local FMT="fmt -w $__PG_COLUMNS -s"
    type fmt &> /dev/null || FMT=cat
    echo "Usage: $PROG $FLAGS$LFLAGS$ARGS $ARGUMENTS" |$FMT
    echo "
NOTE: Unless the -$(get_opt_letter CONFIRM) or --$(get_opt_string CONFIRM) \
option is given, nothing is actually done and $PROG just lists the actions it \
would take.

Fits everything in the current directory into new subdirectories called \
${PREFIX}0..${PREFIX}_M so that each subdirectory is small enough to fit \
onto one CD.

This version uses the -k option for du which is a GNU and POSIX \
extension - you may need to change it for non-GNU, non-POSIX systems \
(Solaris? HP? AIX?) which report in 512-byte blocks only. \
Unfortunately, GNU du does not respond to the documented \
POSIXLY_CORRECT variable and always reports in 1k-blocks.

The limits have not yet been optimised to achieve maximum capacity for \
actual CD or DVD usage but are more or less OK." |$FMT

    echo
    echo "Options:"
    print_all_opts
}
   
check_and_process_opts() {
    local NO_OPTS TEMP

    ARGS="
ARGP_DELETE=quiet
ARGP_VERSION=$VERSION
ARGP_PROG=$PROG
##############################################################   
#OPTIONS:
#name=default sname arg       type range   description
##############################################################   
CD=''         c     ''        b    ''      fit to 700Mb CD (default).
DVD=''        d     ''        b    ''      fit to 4.3Gb DVD.
MB=''         m     'limit'   i    ''      fit to 'limit' Mb.
PREFIX='$PREFIX' p  'prefix'  s    ''      use prefix for dir names.
CONFIRM=''    n     ''        b    ''      no dry run - just do it!
##############################################################   
ARGP_ARGS=[--] $ARGUMENTS
ARGP_SHORT=$SHORT_DESC
ARGP_USAGE=$USAGE"

    exec 4>&1
    eval $(echo "$ARGS" | argp.sh "$@" 3>&1 1>&4 || echo exit $? )
    exec 4>&-
    NEW_ARGS=( "$@" )
    return 0
}

PROG=$(basename $0)
VERSION="1.2"

PREFIX="fit_"
LIMIT=`expr 700 \* 1024`
VERBOSE=""
CONFIRM=""

NEW_ARGS=( )
check_and_process_opts "$@" && set -- "${NEW_ARGS[@]}"

CURRENT_DIR=-1
NUM_DIRS=0
DIR_SIZES=""
[ "$VERBOSE" ] && set -x

du -ks * |sort -rn | while read SIZE FILE; do
    SIZE=`echo $SIZE |tr -d 'K'`
    if [ "$SIZE" -gt "$LIMIT" ]; then
        echo "$PROG: $FILE is too big to ever fit: not moved" >&2
        continue;
    fi
    
    # find a directory with room:
    DIR=0
    set -- $DIR_SIZES
    while [ $DIR -lt $NUM_DIRS ]; do
        CURRENT_SIZE=$1
        shift
        NEW_SIZE=`expr $CURRENT_SIZE + $SIZE`
        if [ $NEW_SIZE -lt $LIMIT ]; then
            break
        fi
        DIR=`expr $DIR + 1`
    done

    if [ $DIR -eq $NUM_DIRS ]; then
        # need to create a new directory
        # DIR=$NUM_DIRS
        NUM_DIRS=`expr $NUM_DIRS + 1`
        CMD="mkdir \"$PREFIX$DIR\""
        [ "$CONFIRM" ] && eval $CMD || echo "dry run: $CMD"
        if [ $? -ne 0 ]; then
            echo "$PROG: Can't mkdir $PREFIX$DIR" >&2
            exit 1
        fi
        DIR_SIZES="$DIR_SIZES 0"
        NEW_SIZE=$SIZE
    fi

    # DIR, NEW_SIZE are set
    CMD="mv \"$FILE\" \"$PREFIX$DIR\""
    [ "$CONFIRM" ] && eval $CMD || echo "dry run: $CMD"

    # Update our record of directory content size:
    D=0
    set -- $DIR_SIZES
    DIR_SIZES=""
    while [ $D -lt $NUM_DIRS ]; do
        if [ $D -eq $DIR ]; then
            DIR_SIZES="$DIR_SIZES $NEW_SIZE"
        else
            DIR_SIZES="$DIR_SIZES $1"
        fi
        shift
        D=`expr $D + 1`
    done
done
