#!/bin/bash

# Download google-maps- & satellite images (20090829)
# spam.frank@ich-war-hier.de

# all tools available?
TOOLS="ping bc curl file montage"
for TOOL in $TOOLS; do
    if [ -z "`which $TOOL 2> /dev/null`" ]; then
        echo "ERROR: Could not find $TOOL"
        exit 1
    fi
done

# help message
function print_help()
{
    echo "Usage: $0 MODE MAP [OPTIONS]"
    echo "  --curl-cookie FILENAME             Read and write cookie-data to this file"
    echo "  --curl-proxy HOST[:PORT]           Advise curl to use this proxy-server"
    echo "  --curl-proxy-type TYPE             http (default), socks4, socks5"
    echo "  --curl-proxy-user USER[:PASSWORD]  Proxy-authentication"
    echo "  --curl-user-agent USERAGENT        Identify like this"
    echo "  --tiles-per-min NUMBER             Maximum number of satellite tiles/min"
    echo "                                     (0 = unlimited)"
    echo "  --wrong-mimetype-sleep MINUTES     Intervall to sleep after receiving file of"
    echo "                                     wrong mimetype (0 = no sleep)"
    echo "  --montage                          Combine tiles after download"
    echo "  --proxylist FILENAME               Use list of anonymous http-proxyies stored"
    echo "                                     in format \"ADDRESS:PORT\". Use of this"
    echo "                                     option overwrites other proxy settings."
    echo "MODE: url"
    echo "  --area WIDTHxHEIGHT   tile-area (range depends on zoom!)"
    echo "  --url URL             google-maps url"
    echo "MODE: tiles"
    echo "  --zoom ZOOM           0 = distant"
    echo "  --tiles X1,Y1,X2,Y2   tile-area (range depends on zoom!)"
    echo "                        CAUTION: if omitted 'whole map' will be assumed!"
    echo "MODE: resume"
    echo "  --resume OUTDIR       Resume a previous run with partial contents"
    echo "MAP: map, satellite, labels, terrain, osm-mapnik, osm-osmarender"
    exit 1
}

if [ $# -lt 3 ]; then
    print_help
fi

# defaults
PROXYLIST=
PROXYLIST_INDEX=0
PROXYLIST_ERRORS=0
PROXYLIST_ERRORS_MAX=5
PROXYLIST_PING_COUNT=4
PROXYLIST_PING_TIMEOUT=5
CURL_TIMEOUT=15
CURL_PROXY=""
CURL_PROXY_TYPE="http"
CURL_PROXY_USER=""
CURL_COOKIE="curl.cookie"
CURL_HEADER="curl.header"
CURL_USER_AGENT="Firefox/3.5.2"
GM_AREA_WIDTH=1
GM_AREA_HEIGHT=1
GM_URL=""
GM_ZOOM=0
GM_AREA_X1=-1
GM_AREA_Y1=-1
GM_AREA_X2=-1
GM_AREA_Y2=-1
# maximum number of satellite-tiles per minute (don't make google go
# mad and send advices to check to for viruses...).
DL_TILES_PER_MIN=30
# number of minutes to sleep when recieving a file we didn't
# expect (after getting blocked)
DL_WRONG_MIMETYPE_SLEEP=120
# combine tiles to one image
FLAG_MONTAGE="no"

# ==== fixed config

# filename for reading and writing configuration
CONFIG_FILE="config"

# output
if [ "$MODE" = "resume" ]; then
    OUT_DIR=$RESUMEDIR
else
    OUT_DIR=`date +%Y%m%d%H%M%S`
fi
OUT_FILE='map'

# ====

MODE=$1; shift
GM_MAP=$1; shift
# parse parameters
while [ $# -gt 0 ]; do
    case "$1" in
        "--curl-cookie")          CURL_COOKIE="$2"; shift;;
        "--curl-proxy")           CURL_PROXY="$2"; shift;;
        "--curl-proxy-type")      CURL_PROXY_TYPE="$2"; shift;;
        "--curl-proxy-user")      CURL_PROXY_USER="$2"; shift;;
        "--curl-user-agent")      CURL_USER_AGENT="$2"; shift;;
        "--tiles-per-min")        DL_TILES_PER_MIN=$2; shift;;
        "--wrong-mimetype-sleep") DL_WRONG_MIMETYPE_SLEEP=$2; shift;;
        "--montage")              FLAG_MONTAGE="yes";;
        "--proxylist")            PROXYLIST="$(pwd)/$2"; shift;;
        "--area")
            GM_AREA_WIDTH=`echo $2 | cut -d "x" -f 1`;
            GM_AREA_HEIGHT=`echo $2 | cut -d "x" -f 2`
            shift;;
        "--url")                  GM_URL="$2"; shift;;
        "--zoom")                 GM_ZOOM="$2"; shift;;
        "--tiles") 
            GM_AREA_X1=`echo $2 | cut -d "," -f 1`
            GM_AREA_Y1=`echo $2 | cut -d "," -f 2`
            GM_AREA_X2=`echo $2 | cut -d "," -f 3`
            GM_AREA_Y2=`echo $2 | cut -d "," -f 4`
            shift
            ;;
        "--resume")
            RESUMEDIR="$2"; shift;;
        *)
            echo "Unknown parameter: $1"
            exit 1
            ;;
    esac
    shift
done

# ==== configuration file

function config_write()
{
    # first value does NOT append! (overwrites old config)
    echo "map=${GM_MAP}" > "$CONFIG_FILE"
    echo "curl-cookie=${CURL_COOKIE}" >> "$CONFIG_FILE"
    echo "curl-proxy=${CURL_PROXY}" >> "$CONFIG_FILE"
    echo "curl-proxy-type=${CURL_PROXY_TYPE}" >> "$CONFIG_FILE"
    echo "curl-proxy-user=${CURL_PROXY_USER}" >> "$CONFIG_FILE"
    echo "curl-user-agent=${CURL_USER_AGENT}" >> "$CONFIG_FILE"
    echo "tiles-per-min=${DL_TILES_PER_MIN}" >> "$CONFIG_FILE"
    echo "wrong-mimetype-sleep=${DL_WRONG_MIMETYPE_SLEEP}" >> "$CONFIG_FILE"
    echo "montage=${FLAG_MONTAGE}" >> "$CONFIG_FILE"
    echo "proxylist=${PROXYLIST}" >> "$CONFIG_FILE"
    echo "proxylist_index=${PROXYLIST_INDEX}" >> "$CONFIG_FILE"
    echo "zoom=${GM_ZOOM}" >> "$CONFIG_FILE"
    echo "area_x1=${GM_AREA_X1}" >> "$CONFIG_FILE"
    echo "area_y1=${GM_AREA_Y1}" >> "$CONFIG_FILE"
    echo "area_x2=${GM_AREA_X2}" >> "$CONFIG_FILE"
    echo "area_y2=${GM_AREA_Y2}" >> "$CONFIG_FILE"
}

function config_read()
{
    local LINE=
    local VAR=
    local VAL=
    if [ -f "$CONFIG_FILE" ]; then
        while read LINE; do
            VAR=`echo $LINE | cut -d "=" -f 1`
            VAL=`echo $LINE | cut -d "=" -f 2-`
            case "$VAR" in 
                "map")                  GM_MAP=$VAL;;
                "curl-cookie")          CURL_COOKIE=$VAL;;
                "curl-proxy")           CURL_PROXY=$VAL;;
                "curl-proxy-type")      CURL_PROXY_TYPE=$VAL;;
                "curl-proxy-user")      CURL_PROXY_USER=$VAL;;
                "curl-user-agent")      CURL_USER_AGENT=$VAL;;
                "tiles-per-min")        DL_TILES_PER_MIN=$VAL;;
                "wrong-mimetype-sleep") DL_WRONG_MIMETYPE_SLEEP=$VAL;;
                "montage")              FLAG_MONTAGE=$VAL;;
                "proxylist")            PROXYLIST=$VAL;;
                "proxylist_index")      PROXYLIST_INDEX=$VAL;;
                "zoom")                 GM_ZOOM=$VAL;;
                "area_x1")              GM_AREA_X1=$VAL;;
                "area_y1")              GM_AREA_Y1=$VAL;;
                "area_x2")              GM_AREA_X2=$VAL;;
                "area_y2")              GM_AREA_Y2=$VAL;;
            esac
        done < "$CONFIG_FILE"
    else
        echo "Could not find configuration file: $(pwd)/${CONFIG_FILE}"
        exit 1
    fi
}

# ==== fixed config

# output
if [ $MODE = "resume" ]; then
    OUT_DIR=$RESUMEDIR
    cd $OUT_DIR
else
    OUT_DIR=`date +%Y%m%d%H%M%S`
    OUT_DIR="`pwd`/${OUT_DIR}"
fi
OUT_FILE='map'

# check operation mode
if [ $MODE = "resume" ]; then
    config_read
else
    if [ $MODE != "url" ] && [ $MODE != "tiles" ]; then
        echo "Unknown mode '$MODE'"
        exit 1
    fi
fi

# check map type
# (Google Maps)
if [ $GM_MAP = "map" ]; then
    GM_ENTRY_PAGE="http://maps.google.com"
    GM_ZOOM_MAX=17
    GM_SERVER=('http://mt0.google.com/' 'http://mt1.google.com/' 'http://mt2.google.com/' 'http://mt3.google.com/')
    GM_SERVER_URL='vt/v=w2.106'
    GM_TILE_SIZE=256
    GM_TILE_TYPE=('image/png')
    GM_TILE_EXT=('png')
    MONTAGE_PARAMS="-quality 100"
elif [ $GM_MAP = "satellite" ]; then
    GM_ENTRY_PAGE="http://maps.google.com/?t=k"
    GM_ZOOM_MAX=19
    GM_SERVER=('http://khm0.google.com/' 'http://khm1.google.com/' 'http://khm2.google.com/' 'http://khm3.google.com/')
    GM_SERVER_URL='kh/v=44'
    GM_TILE_SIZE=256
    GM_TILE_TYPE=('image/jpeg')
    GM_TILE_EXT=('jpg')
    MONTAGE_PARAMS="-quality 95"
elif [ $GM_MAP = "labels" ]; then
    GM_ENTRY_PAGE="http://maps.google.com/?t=h"
    GM_ZOOM_MAX=16
    GM_SERVER=('http://mt0.google.com/' 'http://mt1.google.com/' 'http://mt2.google.com/' 'http://mt3.google.com/')
    GM_SERVER_URL='vt/v=w2t.106'
    GM_TILE_SIZE=256
    GM_TILE_TYPE=('image/png')
    GM_TILE_EXT=('png')
    MONTAGE_PARAMS="-background none -quality 100"
elif [ $GM_MAP = "terrain" ]; then
    GM_ENTRY_PAGE="http://maps.google.com/?t=p"
    GM_ZOOM_MAX=15
    GM_SERVER=('http://mt0.google.com/' 'http://mt1.google.com/' 'http://mt2.google.com/' 'http://mt3.google.com/')
    GM_SERVER_URL='vt/v=w2p.106'
    GM_TILE_SIZE=256
    GM_TILE_TYPE=('image/jpg' 'image/png')
    GM_TILE_EXT=('jpg' 'png')
    MONTAGE_PARAMS="-quality 98"

# (OpenStreetMap)
elif [ $GM_MAP = "osm-mapnik" ]; then
    GM_ENTRY_PAGE="http://www.openstreetmap.org"
    GM_ZOOM_MAX=18
    GM_SERVER=('http://a.tile.openstreetmap.org/' 'http://b.tile.openstreetmap.org/' 'http://c.tile.openstreetmap.org/')
    GM_SERVER_URL=
    GM_TILE_SIZE=256
    GM_TILE_TYPE=('image/png')
    GM_TILE_EXT=('png')
    MONTAGE_PARAMS="-background none -quality 100"
elif [ $GM_MAP = "osm-osmarender" ]; then
    GM_ENTRY_PAGE="http://www.openstreetmap.org"
    GM_ZOOM_MAX=17
    GM_SERVER=('http://a.tah.openstreetmap.org/' 'http://b.tah.openstreetmap.org/' 'http://c.tah.openstreetmap.org/' 'http://d.tah.openstreetmap.org/' 'http://e.tah.openstreetmap.org/' 'http://f.tah.openstreetmap.org/')
    GM_SERVER_URL='Tiles/tile/'
    GM_TILE_SIZE=256
    GM_TILE_TYPE=('image/png')
    GM_TILE_EXT=('png')
    MONTAGE_PARAMS="-background none -quality 100"

else
    echo "Unknown map '$GM_MAP'"
    exit 1
fi

# check proxylist
if [ -n "$PROXYLIST" ]; then
    if ! [ -f "$PROXYLIST" ]; then
        echo "Error: proxylist not found: $(pwd)/${PROXYLIST}"
        exit 1
    fi
    if [ $(wc -l "$PROXYLIST" | cut -d ' ' -f1) -eq 0 ]; then
        echo "Error: Proxylist is empty"
        exit 1
    fi
fi

# ====

# returns needle-position
# $1: haystack
# $2: needle
function strpos()
{
    if [ ${#1} -eq 0 ] || [ ${#2} -eq 0 ] ; then
        # haystack or needle is zero-sized
        return 1
    fi
    local POS=0
    while [ $POS -lt ${#1} ]; do
        if [ "${1:${POS}:${#2}}" = "$2" ]; then
            echo $POS
            return 0
        fi
        ((POS++))
    done
    return 1
}

# select map-size according to GM_ZOOM
function set_map_size()
{
    if [ $GM_ZOOM -lt 0 ] || [ $GM_ZOOM -gt $GM_ZOOM_MAX ]; then
        echo "Wrong zoom $GM_ZOOM. Range: 0 to $GM_ZOOM_MAX"
        exit 1
    else
        GM_MAP_SIZE=$((2**$GM_ZOOM))
    fi
}

# x-tile-coordinate from longitude
# $1: longitude
# $2: map width (tiles)
function longitude_to_tile_x()
{
    echo `echo "($1 + 180) * $2 / 360" | bc`
}

# y-tile-coordinate from longitude
# $1: latitude
# $2: map height (tiles)
# Thanks go out to wikipedia and especially Charlie Savage
# (http://cfis.savagexi.com/articles/2006/05/03/google-maps-deconstructed)
function latitude_to_tile_y()
{
    echo `echo "\
        scale=10; \
        phi=$1; \
        mapsize=$2; \
        pi=3.1415926535; \
        phi_rad=phi*2*pi/360; \
        mercator=l((1+s(phi_rad))/(1-s(phi_rad)))/(2); \
        tile=(1-mercator/pi)*(mapsize/2); \
        scale=0; tile/1" \
        | bc -l`
}

if [ "$MODE" = "url" ]; then
    if [ -z $GM_URL ]; then
        echo "No url specified"
        exit 1
    fi

    # extract parameters
    GM_NEEDLE='/?'
    POS=`strpos "$GM_URL" "$GM_NEEDLE"`
    if [ -z "$POS" ]; then
        echo "Malformed url. Could not locate '$GM_NEEDLE'"
        exit 1
    fi
    GM_PARAMS=`echo ${GM_URL:$(($POS+${#GM_NEEDLE}))} | tr '&' ' '`
    
    # parse get-parameters
    for PARAM in $GM_PARAMS; do
        PARAM_NAME=`echo "$PARAM" | cut -d '=' -f 1`
        PARAM_VALUE=`echo "$PARAM" | cut -d '=' -f 2`
        case $PARAM_NAME in
            "ll")
                GM_LATITUDE=`echo "$PARAM_VALUE" | cut -d ',' -f 1`
                GM_LONGITUDE=`echo "$PARAM_VALUE" | cut -d ',' -f 2`
                ;;
            "z")
                GM_ZOOM=$PARAM_VALUE
                ;;
        esac
    done
    
    # do we have everything we need?
    if [ -z $GM_LONGITUDE ]; then
        echo "Could not extract longitude"
        exit 1
    fi
    if [ -z $GM_LATITUDE ]; then
        echo "Could not extract latitude"
        exit 1
    fi
    if [ -z $GM_ZOOM ]; then
        echo "Could not extract zoom"
        exit 1
    fi

    set_map_size

    # Thanks go out to wikipedia and especially Charlie Savage
    # (http://cfis.savagexi.com/articles/2006/05/03/google-maps-deconstructed)
    GM_LONGITUDE_X=`longitude_to_tile_x $GM_LONGITUDE $GM_MAP_SIZE`
    GM_LATITUDE_Y=`latitude_to_tile_y $GM_LATITUDE $GM_MAP_SIZE`

    GM_AREA_X1=$(($GM_LONGITUDE_X - ($GM_AREA_WIDTH - 1) / 2))
    GM_AREA_X2=$(($GM_LONGITUDE_X + $GM_AREA_WIDTH - 1 - $GM_LONGITUDE_X + $GM_AREA_X1))
    GM_AREA_Y1=$(($GM_LATITUDE_Y - ($GM_AREA_HEIGHT - 1) / 2))
    GM_AREA_Y2=$(($GM_LATITUDE_Y + $GM_AREA_HEIGHT - 1 - $GM_LATITUDE_Y + $GM_AREA_Y1))
fi

if [ "$MODE" = "tiles" ] || [ "$MODE" = "resume" ]; then
    set_map_size

    # check coordinates
    
    if [ $GM_AREA_X1 -lt 0 ] || [ $GM_AREA_X1 -gt $GM_AREA_X2 ] || [ $GM_AREA_X2 -ge $GM_MAP_SIZE ] \
       || [ $GM_AREA_Y1 -lt 0 ] || [ $GM_AREA_Y1 -gt $GM_AREA_Y2 ] || [ $GM_AREA_Y2 -ge $GM_MAP_SIZE ]; then
        echo "Incorrect area coordinates. Using full map."
        GM_AREA_X1=0
        GM_AREA_X2=$(($GM_MAP_SIZE-1))
        GM_AREA_Y1=0
        GM_AREA_Y2=$(($GM_MAP_SIZE-1))
    fi
    GM_AREA_WIDTH=$(($GM_AREA_X2-$GM_AREA_X1+1))
    GM_AREA_HEIGHT=$(($GM_AREA_Y2-$GM_AREA_Y1+1))
fi

# pretend we are a user with limited display size
# (try to hide from google's blocking algorithm)
DL_SCREEN_WIDTH=$(($RANDOM % 4 + 2)) # width of 2 to 5
DL_SCREEN_HEIGHT=$(($RANDOM % 3 + 2)) # height of 2 to 4
(($DL_SCREEN_WIDTH > $GM_AREA_WIDTH)) && DL_SCREEN_WIDTH=$GM_AREA_WIDTH
(($DL_SCREEN_HEIGHT > $GM_AREA_HEIGHT)) && DL_SCREEN_HEIGHT=$GM_AREA_HEIGHT

# ====

echo ""
echo "Configuration:"
echo "    Mode: $MODE"
echo "    Map: $GM_MAP"
echo "    Ouput-directory: $OUT_DIR"
echo "    Ouput-file: $OUT_FILE"
echo "    Tile-size: ${GM_TILE_SIZE} pixels"
echo -n "    Tiles/min: "
if [ $DL_TILES_PER_MIN -gt 0 ]; then
    echo $DL_TILES_PER_MIN
else
    echo "unlimited (not recommended)"
fi
echo -n "    After wrong mimetype: "
if [ $DL_WRONG_MIMETYPE_SLEEP -gt 0 ]; then
    echo "sleep $DL_WRONG_MIMETYPE_SLEEP minute(s)"
else
    echo "continue immediately (not recommended)"
fi
if [ "$MODE" = "url" ]; then
    echo "    Longitude: $GM_LONGITUDE"
    echo "    Latitude: $GM_LATITUDE"
fi
echo "    Zoom: $GM_ZOOM (${GM_MAP_SIZE}x${GM_MAP_SIZE} tiles)"
echo "    Area: ${GM_AREA_WIDTH}x${GM_AREA_HEIGHT} tiles"
echo "    Area-coordinates: (${GM_AREA_X1}x${GM_AREA_Y1})-(${GM_AREA_X2}x${GM_AREA_Y2})"

# ==== prepare output directory

if [ "$MODE" != "resume" ]; then
    if [ -d $OUT_DIR ]; then
        echo "Output directory already exists."
        exit 1
    else
        mkdir $OUT_DIR &> /dev/null
        if [ `echo $?` -ne 0 ]; then
            echo "Could not create output directory. Do not use spaces and check permissions."
            exit 1
        fi
        # enter output directory
        cd $OUT_DIR
        # write config
        config_write
    fi
fi

# ==== build curl parameters

# return curl proxy parameters for currently select proxy
function proxylist_get()
{
    local LINE=$(head -n $PROXYLIST_INDEX "$PROXYLIST" | tail -n 1)
    echo " --proxy $LINE"
}

    # if we use a proxy list, change proxy after a certain number of errors
function proxylist_error()
{
    PROXYLIST_ERRORS=$((PROXYLIST_ERRORS + 1))
    if [ $PROXYLIST_ERRORS -ge $PROXYLIST_ERRORS_MAX ]; then
        proxylist_next
    fi
}

function proxylist_next()
{
    local TESTED="no"
    local LINE=
    while [ "$TESTED" != "yes" ]; do
        PROXYLIST_INDEX=$((PROXYLIST_INDEX + 1))    
        if [ $PROXYLIST_INDEX -gt $(wc -l "$PROXYLIST" | cut -d ' ' -f1) ]; then
            echo "Proxylist: No new proxy left in list. Restarting with first one."
            PROXYLIST_INDEX=1
        fi
        # save proxy index within config
        config_write
        
        # read line
        LINE=$(head -n $PROXYLIST_INDEX "$PROXYLIST" | tail -n 1)
        echo "Proxylist: Selected proxy #${PROXYLIST_INDEX} (${LINE})"
        
        # test
        echo -n "    Ping... "
        ping -c $PROXYLIST_PING_COUNT -w $PROXYLIST_PING_TIMEOUT $(echo "$LINE" | cut -d ':' -f 1) &> /dev/null
        if [ $? -eq 0 ]; then
            echo "ok"
            echo -n "    Receive entry page... "
            curl $(curl_params) $GM_ENTRY_PAGE &> /dev/null
            if [ $? -eq 0 ]; then
                echo "ok"
                # this proxy is usable
                TESTED="yes"
            else
                echo "failed"
            fi
        else
            echo "failed"
        fi
    done
    
    # reset number of errors    
    PROXYLIST_ERRORS=0
}

function curl_params_misc()
{
    local RET=" --connect-timeout $CURL_TIMEOUT"
    
    if [ -n "$CURL_HEADER" ]; then
        RET="$RET --dump-header $CURL_HEADER"
    fi
    if [ -n "$CURL_COOKIE" ]; then
        RET="$RET --cookie $CURL_COOKIE --cookie-jar $CURL_COOKIE"
    fi
    if [ -n "$CURL_USER_AGENT" ]; then
        RET="$RET --user-agent $CURL_USER_AGENT"
    fi
    echo "$RET"
}

function curl_params_proxy()
{
    local RET=

    # use proxy from list
    if [ -n "$PROXYLIST" ] ; then
        RET=$(proxylist_get)
    # use "normal" proxy
    elif [ -n "$CURL_PROXY" ]; then
        case "$CURL_PROXY_TYPE" in
            "http")   RET="$RET --proxy $CURL_PROXY";;
            "socks4") RET="$RET --socks4 $CURL_PROXY";;
            "socks5") RET="$RET --socks5 $CURL_PROXY" ;;
            *)        echo "Error: Unknown proxy-type: $CURL_PROXY_TYPE"; exit 1;;
        esac
        if [ -n "$CURL_PROXY_USER" ]; then
            RET="$RET --proxy-user $CURL_PROXY_USER"
        fi
    fi
    
    echo "$RET"
}

function curl_params()
{
    echo "$(curl_params_misc)$(curl_params_proxy)"
}

# ==== select first proxy OR
# ==== entry page (mainly used to receive cookie data)

echo ""
if [ -n "$PROXYLIST" ]; then
    # check proxy server
    if [ $PROXYLIST_INDEX -ne 0 ]; then
        # if there is already a selected entry, re-select it with "next"
        PROXYLIST_INDEX=$(($PROXYLIST_INDEX - 1))
    fi
    proxylist_next
else
    echo -n "Receive entry page ($GM_ENTRY_PAGE)..."
    curl $(curl_params) $GM_ENTRY_PAGE >> curl.log 2>> curl.log

    if [ `echo $?` -eq 0 ]; then
        echo "Ok"
    else
        echo "Error (see curl.log)"
        exit 1
    fi
fi

# ==== download tiles
echo ""
echo "Downloading..."

# return current unix timestamp
function timestamp()
{
    echo `date +%s`
}

# animated sleep
# $1: length (number of dots)
# $2: multiplier 
function sleepani()
{
    local LENGTH=$1
    echo -n "    "
    while [ $LENGTH -gt 0 ]; do
        echo -n "."
        sleep $2
        LENGTH=$(($LENGTH-1))
    done
    echo "" # new line
}

# we want numbers with a fixed length of 6
function zeros()
{
    local OUT="000000${1}"
    echo ${OUT:$((${#OUT}-6)):6}
}

# compute value for the "s"-value (a special part of "Galileo")
# $1: x
# $2: y
function tileaddr_svar()
{
    local S="Galileo" LEN=0
    # x-part
    case "$(($1 % 3))" in
        1) LEN=3;;
        2) LEN=6;;
    esac
    LEN=$(($LEN + $1 / 3))
    # y-part
    LEN=$((($LEN + $2) % (${#S} + 1)))

    echo ${S:0:$LEN}
}

# build tile address
function tileaddress()
{
    case "$GM_MAP" in
        [osm-]*)
            echo "${GM_SERVER[${MIRROR}]}${GM_SERVER_URL}${GM_ZOOM}/${GM_X}/${GM_Y}.${GM_TILE_EXT[0]}";;
        *)  
            echo "${GM_SERVER[${MIRROR}]}${GM_SERVER_URL}&x=${GM_X}&y=${GM_Y}&z=${GM_ZOOM}&s=$(tileaddr_svar $GM_X $GM_Y)";;
    esac
}

# build (local) tile filename (make them sort properly for montage)
function tilefilename()
{
    echo "tile_`zeros $GM_ZOOM`_`zeros $GM_Y`_`zeros $GM_X`"
}

# $1: mimetype
function tiletypeok()
{
    local OK="no"
    for I in `seq 0 $((${#GM_TILE_TYPE[@]} - 1))`; do
        if [ "$1" = "${GM_TILE_TYPE[$I]}" ]; then
            OK="yes"
            break
        fi 
    done
    echo $OK
}

function tiledl()
{
    if [ $GM_MAP = 'satellite' ] && [ $DL_TILES_PER_MIN -gt 0 ] ; then
        # give the google-servers time to relax
        if [ $DL_TILES -ge $DL_TILES_PER_MIN ]; then
            ANILEN=$(($DL_SLEEP_TILL-`timestamp`))
            if [ $ANILEN -gt 0 ]; then
                echo "    Sleep $ANILEN second(s)"
                sleepani $ANILEN 1
            fi
            DL_TILES=0
            DL_SLEEP_TILL=$((`timestamp`+60))
        fi
    fi

    # build output filename
    OUTPUT="`tilefilename`"
    ADDRESS="`tileaddress`"

    # DEBUG
    # echo $ADDRESS

    local PERFORM_DL="yes"
    # check if this tile is already present
    if [ -f "$OUTPUT" ];then
        # check filetype
        FILETYPE="`file --brief --mime $OUTPUT`"
        if [ "`tiletypeok $FILETYPE`" = "yes" ]; then
            PERFORM_DL="no"
            ((TILES_SKIPPED++))
        fi
    fi
    if [ "$PERFORM_DL" = "yes" ]; then
        # print how many tiles have been skipped
        if [ $TILES_SKIPPED -gt 0 ]; then
            echo "    Skipped $TILES_SKIPPED tile(s)"
            TILES_SKIPPED=0
        fi
        while [ 0 ]; do
            if [ "$GM_MAP" = "satellite" ] && [ $DL_TILES_PER_MIN -gt 0 ] ; then
                DL_TILES=$(($DL_TILES + 1))
            fi
            # download
            echo -n "    Tile ${GM_X}x${GM_Y} ... "
            curl $(curl_params) --output $OUTPUT $ADDRESS >> curl.log 2>> curl.log

            if [ `echo $?` -ne 0 ]; then
                echo "ERROR (see curl.log)"
                if [ -n "$PROXYLIST" ]; then
                    proxylist_error
                fi
            else
                # compare mimetype of received data
                FILETYPE="`grep "^Content-Type: " $CURL_HEADER | tr --delete [:cntrl:] | cut -d " " -f 2`"
                if [ "`tiletypeok $FILETYPE`" != "yes" ]; then
                    echo "ERROR (wrong file type: $FILETYPE)"
                    # count error (using proxylist) or sleep
                    if [ -n "$PROXYLIST" ]; then
                        proxylist_error
                    elif [ $DL_WRONG_MIMETYPE_SLEEP -gt 0 ]; then
                        echo "    Sleep $DL_WRONG_MIMETYPE_SLEEP minutes"
                        sleepani $DL_WRONG_MIMETYPE_SLEEP 60
                    fi
                else
                    echo "Ok"
                    break
                fi
            fi

            # next mirror
            ((MIRROR++))
            if [ ${#GM_SERVER[*]} -eq $MIRROR ]; then
                MIRROR=0
            fi
        done
    fi
}

# ==== download

DL_TILES=0 # count tiles since last sleep
DL_SLEEP_TILL=$((`timestamp` + 60))

# start with first mirror
MIRROR=0
# did not skip tiles yet
TILES_SKIPPED=0
# classic way of downloading
for GM_Y in `seq $GM_AREA_Y1 $GM_AREA_Y2`; do
    for GM_X in `seq $GM_AREA_X1 $GM_AREA_X2`; do
        tiledl
    done
done

# ==== combine tiles

if [ "$FLAG_MONTAGE" = "yes" ]; then
    echo ""
    echo "Montage..."

    # -tile wxh: tile alignment
    # -geometry: size of each tile
    montage \
        -tile $(($GM_AREA_WIDTH))x$(($GM_AREA_HEIGHT)) \
        -geometry ${GM_TILE_SIZE}x${GM_TILE_SIZE} \
        $MONTAGE_PARAMS tile_* $OUT_FILE.${GM_TILE_EXT[0]} \
        &> montage.log

    if [ `echo $?` -eq 0 ]; then
        echo "    Ok"
    else
        echo "    ERROR (see montage.log)"
    fi
fi