#!/bin/bash

maxSearchTries=40


PATH=$PATH:$(dirname "$0")

# Check for required programs before starting 
requiredPrograms=( "concatenateImages" "convert" "expr" )
requiredOk=1
for prg in "${requiredPrograms[@]}"; do
    path=$(which $prg)
    if [[ "" == "$path" ]]; then
        requiredOk=0
        echo "ERROR: Required program '$prg' not found" >&2
    fi
done

if [[ $requiredOk -eq 0 ]]; then
    echo "       Please provide those programs in order to use this script" >&2
    exit 2
fi


# General help that is shown whenever the script cannot be run because the user
# did not provide sufficient parameters
function showHelp {
    cmd=$(basename "$0")
    echo ""
    echo "        -----===== $cmd =====-----"
    echo ""
    echo "   \"$cmd --of=OUTFILE sf=SEARCHFOLDER [OPTIONAL PARAMETERS] RES1 RES2 [RES3 [...]]\""
    echo ""
    echo "Concatenates the images in the given order and writes the result to the file"
    echo "specified by --of"
    echo ""
    echo "Obligatory parameters:"
    echo " --of=         The out-file, meaning the file to write the concatenated image "
    echo "               to. This file will be overwritten without notice."
    echo " --sf=         The folder to search in for images."
    echo " RES[1-x]      The resolution of the Desktops for which the background should"
    echo "               be created from left to right. The RES-parameters must have the"
    echo "               form of WIDTHxHEIGHT (e.g. '1280x1024'). There have to be at least"
    echo "               two RES-parameters"
    echo ""
    echo "Optional parameters:"
    echo " --mode=       The mode for choosing the images. Possible values:"
    echo "                 exact  - For every resolution an image with exactly the given"
    echo "                          size is used. No resizing of images."
    echo "                 exact2 - Like exact, but needs a preordered search-folder. Use the"    
    echo "                          orderImagesByResolution script to order it. (Faster)"
    echo "                 down   - Uses images that are at least as big as the given size"
    echo "                          and resizes bigger images. This might lead to images being"
    echo "                          cropped because they have a differnt ratio."
    echo "                 any    - Resizes any picture to the given resolution. This might"
    echo "                          look bad if you have for example a 24\" monitor and some"
    echo "                          640x480 images in the search-folder"
    echo "               Default: exact"
    echo " --background= The color of the background if the pictures do not have the same"
    echo "               size. (default: black)"
    echo " --same        Use the same image for all resolutions. Only works with mode 'down'"
    echo "               or 'any'"
    echo " --align=      Vertical alignment of images that are smaller that the biggest "
    echo "               image given"
    echo " --verbose     Output additional information"
    echo ""
    echo "Example: "
    echo "   \"$cmd --of=bg.png --sf=Pictures/OrderedWallpapers 1920x1200 1280x1024\""
    echo ""
    echo "Exit-Codes:"
    echo "  0 - Everything is fine"
    echo "  1 - Wrong parameters"
    echo "  3 - Not enough RES-parameters"
    echo "  4 - No subfolder for given resolution in search-folder"
    echo "  5 - Unknown mode"
    echo "  6 - RES-pramatemer has wrong format."
    echo "  7 - Incompatible parameters used"
    echo "  8 - No matching image found after $maxSearchTries tries"
    echo " 11 - Image concatenating failed (concatenateImages error)"
    echo " 23 - I am watching you."
    echo " 42 - Please state the question."
    echo " 99 - The requested feature is not (yet) implemented"
    echo ""
    
}

# Used to output certain information only in verbose-mode
function echoIfVerbose {
    if [[ $verbose -eq 1 ]]; then
        echo $@
    fi
}

# initializing variables
exitStatus=0
concatParams=""
searchFolder=""
outFile=""
mode="exact"
resolutions=()
verbose=0
hidden=0
same=0

# Analyze given parameters
for param in "$@"; do
    knownParam=0

    if [[ "${param:0:5}" == "--of=" ]]; then
        knownParam=1
        outFile="${param:5}"
    fi
   
    if [[ "${param:0:5}" == "--sf=" ]]; then
        knownParam=1
        searchFolder=${param:5}
        # All directories should end with an "/" to prevent problems with the 
        # path later on
        if [[ "/" != ${searchFolder: -1} ]]; then
            searchFolder="$searchFolder/"
        fi
    fi

    if [[ "${param:0:7}" == "--mode=" ]]; then
        knownParam=1
        mode=${param:7}
        # All directories should end with an "/" to prevent problems with the 
        # path later on
        if [[ "/" != ${searchFolder: -1} ]]; then
            searchFolder="$searchFolder/"
        fi
    fi

    if [[ "${param:0:2}" != "--" ]]; then
        knownParam=1
        match=$(expr match "$param" '\([0-9]*x[0-9]*\)')
        if [[ "$match" != "$param" ]]; then
            echo "ERROR: RES-pramatemer format must be WIDTHxHEIGHT (e.g. '1024x768'): '$param'" >&2
            exitStatus=6
        fi
        
        resolutions[${#resolutions[*]}]=$param
    fi

    if [[ "${param:0:9}" == "--verbose" ]]; then
        knownParam=1
        verbose=1
    fi

    if [[ "${param:0:8}" == "--hidden" ]]; then
        knownParam=1
        hidden=1
    fi
    
    if [[ "${param:0:6}" == "--same" ]]; then
        knownParam=1
        same=1
    fi

    # Parameters handed down to the used program
    if [[ "${param:0:8}" == "--align=" || "${param:0:13}" == "--background=" ]]; then
        knownParam=1
        concatParams="$concatParams $param"
    fi

    # Check for unknown parameters
    # Has to be at the end of this loop
    if [[ $knownParam -eq 0 ]]; then
        echo "ERROR: Unknown Parameter '$param'" >&2
        exitStatus=4
    fi
done

if [[ "$outFile" == "" ]]; then
    echo "ERROR: Obligatory Parameters not given" >&2
    echo "       Please provide an out-file (of) parameters" >&2
    exitStatus=1
fi

if [[ "$searchFolder" == "" ]]; then
    echo "ERROR: No search folder given" >&2
    echo "       Please provide a folder to search for images" >&2
    exitStatus=1
fi

if [[ ${#resolutions[@]} -lt 2 ]]; then
    echo "ERROR: Not enough RES-parameters given" >&2
    echo "       Please provide at least two image resolutions" >&2
    exitStatus=3
fi

if [[ "$mode" != "exact" && "$mode" != "exact2" && "$mode" != "down" && "$mode" != "any" ]]; then
    echo "ERROR: Unknown mode" >&2
    echo "       Please use 'exact', 'exact2', 'down' or 'any'" >&2
    exitStatus=5
fi

if [[ "$mode" == "exact" && $same -ne 0 ]]; then
    echo "ERROR: --same cannot be used with mode 'exact'" >&2
    echo "       Please use mode 'down' or 'any' or leave out --same" >&2
    exitStatus=7
fi
if [[ "$mode" == "exact2" && $same -ne 0 ]]; then
    echo "ERROR: --same cannot be used with mode 'exact'" >&2
    echo "       Please use mode 'down' or 'any' or leave out --same" >&2
    exitStatus=7
fi


if [[ $exitStatus -ne 0 ]]; then
    ## Code for debugging parameters
    #echo "Parameters received:"
    #x=0
    #while [[ "$1" != "" ]]; do
    #   let "x = $x + 1"
    #   echo "$x: $1"
    #   shift
    #done

    showHelp
    exit $exitStatus
fi

# $IFS must be set to newline so a space is not interpreted as separator for
# the files returned by the find-command
IFS=$'\n' 

sourceImages=()
deleteImages=()
lastImage=""
fileWidth=0
fileHeight=0        

# Find all files
if [[ $hidden -eq 1 ]]; then
    files=$(find "$searchFolder" -type f)
else
    files=$(find "$searchFolder" \( ! -regex '.*/\..*' \) -type f)
fi

fileArray=()
for filePath in $files; do
    fileArray[${#fileArray[*]}]=$filePath
done

for res in "${resolutions[@]}"; do
    #Todo: Find source images

    if [[ "$mode" == "exact" ]]; then
        # Mode: Only exact images will be used
      
        width=$(expr match "$res" '\([0-9]*\)x')
        height=$(expr match "$res" '.*x\([0-9]*\)')
        

        fileWidth=0
        fileHeight=0        
        echoIfVerbose "Searching for Picture >= $res..."
        x=0
        while [[ $width -ne $fileWidth || $height -ne $fileHeight ]]; do
            image=${fileArray[$RANDOM % ${#fileArray[*]}]}
            pictureSize=$(identify -format "%wx%h" "$image")
            fileWidth=$(expr match "$pictureSize" '\([0-9]*\)x')
            fileHeight=$(expr match "$pictureSize" '.*x\([0-9]*\)')
            echoIfVerbose "    ...$pictureSize"
            
            let "x = x + 1"
            if [[ $x -gt $maxSearchTries ]]; then
                echo "ERROR: Did not find suitable image after $maxSearchTries tries." >&2
                echo "       Please make sure there are enough $res images in the search-folder" >&2
                echo "       Or use 'exact2' mode with preordered search-folder" >&2
                exit 8
            fi
        done
        echoIfVerbose "done."
        
        numImg=${#deleteImages[*]}
        echoIfVerbose "Using picture: '$image'"
        
        tmpImage="$outFile.$numImg.tmp.png"
        
        convert "$image" -resize "$res^" -gravity center -extent "$res" -quality 10 "$tmpImage" #"-quality 10" is specifically for .png
        
        deleteImages[$numImg]=$tmpImage
        sourceImages[$numImg]=$tmpImage
    fi

    if [[ "$mode" == "exact2" ]]; then
        # Mode: Exact imagesize in preordered folder
        subFolder="$searchFolder$res"
        if [[ -d "$subFolder" ]]; then
            resimages=()

            # Find all files
            if [[ $hidden -eq 1 ]]; then
                files=$(find "$searchFolder$res/" -type f)
            else
                files=$(find "$searchFolder$res/" \( ! -regex '.*/\..*' \) -type f)
            fi

            fileArray=()
            for filePath in $files; do
                fileArray[${#fileArray[*]}]=$filePath
            done

            image=${fileArray[$RANDOM % ${#fileArray[*]}]}
            
            echoIfVerbose "Using picture: '$image'"

            sourceImages[${#sourceImages[*]}]=$image
        else
            echo "ERROR: No subfolder in search-folder for resolution '$res'" >&2
            echo "       The search-folder must have subfolders named after the resolution" >&2
            echo "       of the images inside. In this case: '$subFolder'" >&2
            exit 4
        fi
    fi
    


    if [[  "$mode" == "down" && $same -eq 1 ]]; then

        width=$(expr match "$res" '\([0-9]*\)x')
        height=$(expr match "$res" '.*x\([0-9]*\)')

        if [[ $same -eq 0 || "$lastImage" == "" || $width -gt $fileWidth || $height -gt $fileHeight ]]; then
            echoIfVerbose "Searching for Picture >= $res..."
            x=0
            while [[ $width -gt $fileWidth || $height -gt $fileHeight ]]; do
                image=${fileArray[$RANDOM % ${#fileArray[*]}]}
                pictureSize=$(identify -format "%wx%h" "$image")
                fileWidth=$(expr match "$pictureSize" '\([0-9]*\)x')
                fileHeight=$(expr match "$pictureSize" '.*x\([0-9]*\)')
                echoIfVerbose "    ...$pictureSize"

                let "x = x + 1"
                if [[ $x -gt $maxSearchTries ]]; then
                    echo "ERROR: Did not find suitable image after $maxSearchTries tries." >&2
                    echo "       Please make sure there are enough $res images in the search-folder" >&2
                    echo "       Or use 'exact2' mode with preordered search-folder" >&2
                    exit 8
                fi
            done
            echoIfVerbose "done."
        fi
        
        # continued outside the resolution for-loop
    fi 

    if [[ "$mode" == "down" && $same -eq 0 ]]; then
        # Mode: Only bigger images will be resized to the given resolution
      
        width=$(expr match "$res" '\([0-9]*\)x')
        height=$(expr match "$res" '.*x\([0-9]*\)')
        

        fileWidth=0
        fileHeight=0        
        echoIfVerbose "Searching for Picture >= $res..."
        x=0
        while [[ $width -gt $fileWidth || $height -gt $fileHeight ]]; do
            image=${fileArray[$RANDOM % ${#fileArray[*]}]}
            pictureSize=$(identify -format "%wx%h" "$image")
            fileWidth=$(expr match "$pictureSize" '\([0-9]*\)x')
            fileHeight=$(expr match "$pictureSize" '.*x\([0-9]*\)')
            echoIfVerbose "    ...$pictureSize"

            let "x = x + 1"
            if [[ $x -gt $maxSearchTries ]]; then
                echo "ERROR: Did not find suitable image after $maxSearchTries tries." >&2
                echo "       Please make sure there are enough $res images in the search-folder" >&2
                echo "       Or use 'exact2' mode with preordered search-folder" >&2
                exit 8
            fi
        done
        echoIfVerbose "done."
        
        numImg=${#deleteImages[*]}
        echoIfVerbose "Using picture: '$image'"
        
        tmpImage="$outFile.$numImg.tmp.png"
        
        convert "$image" -resize "$res^" -gravity center -extent "$res" -quality 10 "$tmpImage" #"-quality 10" is specifically for .png
        
        deleteImages[$numImg]=$tmpImage
        sourceImages[$numImg]=$tmpImage
    fi
    

    if [[ "$mode" == "any" ]]; then
        # Mode: Any image will be resized to the given resolution

        if [[ $same -eq 0 || "$lastImage" == "" ]]; then
            image=${fileArray[$RANDOM % ${#fileArray[*]}]}
            lastImage="$image"
        fi
        numImg=${#deleteImages[*]}
        
        echoIfVerbose "Using picture: '$image'"
        
        tmpImage="$outFile.$numImg.tmp.png"

        convert "$image" -resize "$res^" -gravity center -extent "$res" -quality 10 "$tmpImage" #"-quality 10" is specifically for .png
        
        deleteImages[$numImg]=$tmpImage
        sourceImages[$numImg]=$tmpImage
    fi
    
    
done

# continuing special case mode 'down' and --same from inside the loop
if [[  "$mode" == "down" && $same -eq 1 ]]; then
    for res in "${resolutions[@]}"; do
        numImg=${#deleteImages[*]}

        echoIfVerbose "Using picture: '$image'"

        tmpImage="$outFile.$numImg.tmp.png"
        
        convert "$image" -resize "$res^" -gravity center -extent "$res" -quality 10 "$tmpImage" #"-quality 10" is specifically for .png
        
        deleteImages[$numImg]=$tmpImage
        sourceImages[$numImg]=$tmpImage
    done    
fi

concatenateImages "${sourceImages[@]}" --of="$outFile" $concatParams
status=$?

if [[ $status -ne 0 ]]; then
    echo "ERROR: concatenateImages has failed to concatenate the images. Exit status: $status " >&2
    exit 11
fi


if [[ ${#deleteImages[*]} -gt 0 ]]; then
    rm "${deleteImages[@]}"
fi

