#!/bin/bash

# set -x

help() {
cat <<HSD
`basename $0` is a tool that helps reviewing OpenQuake branches of others
or even your own (e.g. prior to creating a pull request).

To review one of your branches against your own repo's master you would
say:

    `basename $0` --branch=<branch-name>

To review a colleague's branch against your own repo's master you would
say e.g.

    `basename $0` --branch=<remote-name>/<branch-name>

To review against gem/master

    `basename $0` --other=gem/master --branch=<remote-name>/<branch-name>

To review a branch from a github pull request:

    `basename $0` --pull=59

Same thing against gem/master

    `basename $0` --other=gem/master --pull=59

The command line arguments are as follows:

    --other=[RN/][BN]   The reference branch where RN is the git remote
                        name and BN is the branch name e.g. "gem/master"
                        or "master" (defaults: RN="", BN="master")
    --branch=[RN/BN]    The branch you want to review e.g.
                        "andrea/fix-aggregate-curve" (defaults: RN="")
    --pull=PRN          Review the branch specified by a github pull
                        request. PRN is the pull request number e.g. 59
    -L|--local          Do not fetch any remotes
    -P|--skip-pylint    Skip the pylint check of the branch under review
    -R|--repo=N         Specify the repo name e.g. "openquake-server"
    -S|--skip-pyflakes  Skip the pyflakes check of the branch under review
    -8|--skip-pep8      Skip the pep8 step of the branch under review
    -C|--colordiff      Pipe the branch diff through the "colordiff"
                        tool
HSD
exit 0
}

if [ $# -eq 0 ]; then
    help
fi

git branch >/dev/null 2>&1
if [ $? -ne 0 ]; then
    if [ -z "$OQ_ROOT" ]
    then
        echo "This is not a git repository. Please cd to your OpenQuake"
        echo "repository."
        echo "Alternatively set the OQ_ROOT environment variable to contain"
        echo "the repository path and the script will use that."
        exit 1
    else
        cd $OQ_ROOT
    fi
fi

other_remote=""
other_reference="master"

branch_remote=""
branch_reference=""

fetch_remotes=1
use_colordiff=0
skip_pylint=0
skip_pyflakes=0
skip_pep8=0
branch_name=""
repo="oq-engine"
python_src="openquake"

git diff --quiet HEAD
if [ $? -ne 0 ]; then
    git submodule update
fi
git diff --quiet HEAD
if [ $? -ne 0 ]; then
    echo "Your branch has uncommitted changes. Please commit first."
    exit 2
fi

for i in $*
do
    case $i in
    --other=*)
        other=`echo $i | sed 's/[-a-zA-Z0-9]*=//'`
        other_remote=`dirname $other`
        if [ $other_remote == "." ]; then
            other_remote=""
        fi
        other_reference=`basename $other`
        ;;
    --branch=*)
        branch=`echo $i | sed 's/[-a-zA-Z0-9]*=//'`
        branch_remote=`dirname $branch`
        if [ $branch_remote == "." ]; then
            branch_remote=""
        fi
        branch_reference=`basename $branch`
        branch_name="$branch"
        ;;
    -R|--repo=*)
        repo=`echo $i | sed 's/[-a-zA-Z0-9]*=//'`
        if [ "$repo" == "openquake-server" ]; then
            python_src="bin geonode oqrunner"
        fi
        ;;
    --pull=*)
        pull=`echo $i | sed 's/[-a-zA-Z0-9]*=//'`
        pullreq_file="/tmp/pull.$pull"

        # Do *not* fetch the github pull request if we already have it.
        do_fetch_pullrequest=0

        if [ -f $pullreq_file ]; then
            # Make sure the pull request file is not zero-sized.
            pullreq_file_size=`wc -c $pullreq_file | awk '{print $1}'`
            if [ $pullreq_file_size -eq 0 ]; then
                do_fetch_pullrequest=1
            fi
        else
            do_fetch_pullrequest=1
        fi

        if [ $do_fetch_pullrequest -ne 0 ]; then
            # Pull request file does not exist or has size zero, fetch it from
            # github.
            curl -s https://github.com/gem/$repo/pull/$pull > $pullreq_file
            if [ $? -ne 0 ]; then
                echo "curl failed to get https://github.com/gem/$repo/pull/$pull"
                exit 3
            fi
        fi

        # Is the pull request still open?
        pr_state=`awk '/class="state/ { split($0,F,/[<>]/); print F[3];}' $pullreq_file | head -1`
        if [ "$pr_state" != "Open" ]; then
            echo "https://github.com/gem/$repo/pull/$pull is not open any more"
            exit 4
        fi

        # Find the branch author's github user name and the branch name.
        github_user=`cat $pullreq_file | perl -wane 'if ($_ =~ /<title>.+\s+by\s+(\S+)\s+.+$/) { print $1; }'`
        branch_reference=`grep class=.pull-branch. $pullreq_file | tail -n 1 | perl -wane 'if ($_ =~ /title="([^"]+)"/) { print $1; }'`
        branch="$github_user/$branch_reference"
        branch_name="$branch"

        # Map the author's github user name to a "git remote" name in our
        # local repository.
        branch_remote=`git remote -v | awk "/$github_user.+fetch.$/ {print \\$1}"`
        if [ -z "$branch_remote" ]; then
            echo "You don't have a remote repository defined for github user $github_user. Plase add one and try again."
            exit 5
        fi
        ;;
    -L|--local)
        fetch_remotes=0
        ;;
    -C|--colordiff)
        use_colordiff=1
        ;;
    -P|--skip-pylint)
        skip_pylint=1
        ;;
    -S|--skip-pyflakes)
        skip_pyflakes=1
        ;;
    -8|--skip-pep8)
        skip_pep8=1
        ;;
    -h|--help)
        help
        ;;
    *)
        echo "Uknown option: " $i
        exit 6
        ;;
    esac
done

if [ -z "$branch_reference" ]; then
    echo "Please specify the branch you wish to review"
    echo "(run \"`basename $0` --help\" for more information)."
    exit 7
fi

if [ $skip_pylint -eq 0 ]; then
    which pylint >/dev/null 2>&1
    if [ $? -ne 0 ]; then
        echo "Cannot find pylint .. Is it installed?"
        exit 8
    fi
fi

if [ $skip_pyflakes -eq 0 ]; then
    which pyflakes >/dev/null 2>&1
    if [ $? -ne 0 ]; then
        echo "Cannot find pyflakes .. Is it installed?"
        skip_pyflakes=1
    fi
fi

if [ $skip_pep8 -eq 0 ]; then
    which pep8 >/dev/null 2>&1
    if [ $? -ne 0 ]; then
        echo "Cannot find pep8 .. Is it installed?"
        exit 10
    fi
fi

if [ -z "$branch_name" ]; then
    if [ ! -z "$branch_remote" ]; then
        branch_name="$branch_remote/$branch_reference"
    else
        branch_name="$branch_reference"
    fi
fi

# Update remotes
if [ $fetch_remotes -eq 1 ]; then
    if [ ! -z "$other_remote" ]; then
        echo "Fetching $other_remote .."
        git fetch $other_remote
        if [ $? -ne 0 ]; then
            echo "git failed to fetch $other_remote"
            exit 11
        fi
    fi
    if [ ! -z "$branch_remote" ]; then
        echo "Fetching $branch_remote .."
        git fetch $branch_remote
        if [ $? -ne 0 ]; then
            echo "git failed to fetch $branch_remote"
            exit 12
        fi
    fi
fi

git checkout master >/dev/null 2>&1
git branch -D ba >/dev/null 2>&1
git branch -D bb >/dev/null 2>&1

if [ ! -z "$other_remote" ]; then
    git checkout -b ba $other_remote/$other_reference
    if [ $? -ne 0 ]; then
        echo "git failed to create a tracking branch for $other_remote/$other_reference"
        exit 13
    fi
    other_branch="ba"
else
    other_branch="$other_reference"
fi

if [ ! -z "$branch_remote" ]; then
    git checkout -b bb $branch_remote/$branch_reference
    if [ $? -ne 0 ]; then
        echo "git failed to create a tracking branch for $branch_remote/$branch_reference"
        exit 14
    fi
    branch_branch="bb"
else
    branch_branch="$branch_reference"
fi

DISABLE_PYLINT_FLAGS="--disable=E1101,E0611,F0401,W0201"

# How many python files were added or modified?
num_of_py_files=`git diff ${other_branch}...${branch_branch} --name-only --diff-filter=AM | grep py$ | wc -l`

if [ $skip_pylint -eq 0 ] && [ $num_of_py_files -gt 0 ]; then
    git checkout $other_branch
    echo "Running pylint on $other_branch.."
    pylint -f parseable -dI -rn $DISABLE_PYLINT_FLAGS $python_src | sed -e 's/:[0-9][0-9]*//' -e 's/\([0-9][0-9]*\/[0-9][0-9]*\)//' > /tmp/pl.a

    git checkout $branch_branch
    echo "Running pylint on $branch_name.."
    pylint -f parseable -dI -rn $DISABLE_PYLINT_FLAGS $python_src | sed -e 's/:[0-9][0-9]*//' -e 's/\([0-9][0-9]*\/[0-9][0-9]*\)//' > /tmp/pl.b

    echo ""
    diff -w -U 0 /tmp/pl.a /tmp/pl.b > /tmp/pl.diff
    if [ $? -ne 0 ]; then
        added_issues=`cat /tmp/pl.diff | grep -v '^+++' | grep '^+' | wc -l`
        if [ $added_issues -gt 0 ]; then
            # Perform a "fuzzy" comparison of the pylint issues added and
            # removed respectively. If the source paths and pylint codes do
            # match there is a high chance that the branch under review is
            # actually fine.
            awk '/^[+][^+]/ {printf("%s,.,%s\n", substr($1,2), $2)}' /tmp/pl.diff  >/tmp/pl.added
            awk '/^[-][^-]/ {printf("%s,.,%s\n", substr($1,2), $2)}' /tmp/pl.diff  >/tmp/pl.removed
            diff -uw /tmp/pl.added /tmp/pl.removed > /dev/null 2>&1
            if [ $? -ne 0 ]; then
                echo "!! $branch_name adds $added_issues pylint issues.."
            else
                echo "!! $branch_name is *probably* not adding any"
                echo "   pylint issues but please take a look."
            fi
            echo ""
            if [ $use_colordiff -ne 0 ]; then
                cat /tmp/pl.diff | colordiff
            else
                cat /tmp/pl.diff
            fi
        else
            fixed_issues=`cat /tmp/pl.diff | grep -v '^---' | grep '^-' | wc -l`
            echo "++ $branch_name fixes $fixed_issues existing pylint issues.."
        fi
    else
        echo "** $branch_name introduces no new pylint issues.."
    fi
    echo ""
    echo "Press <enter> to continue"
    read
fi

if [ $skip_pep8 -eq 0 ] && [ $num_of_py_files -gt 0 ]; then
    echo "Running pep8 on $branch_name.."
    echo ""
    # the SQL in the .py files in db/schema/upgrades confuses pep8
    git diff ${other_branch}...${branch_branch} --name-only --diff-filter=AM | grep py$ | grep -v db/schema/upgrades | xargs pep8 --repeat
    echo ""
    echo "Press <enter> to continue"
    read
fi

if [ $skip_pyflakes -eq 0 ] && [ $num_of_py_files -gt 0 ]; then
    echo "Running pyflakes on $branch_name.."
    echo ""
    # the SQL in the .py files in db/schema/upgrades confuses pyflakes
    git diff ${other_branch}...${branch_branch} --name-only --diff-filter=AM | grep py$ | grep -v db/schema/upgrades | xargs pyflakes
    echo ""
    echo "Press <enter> to continue"
    read
fi

if [ $use_colordiff -ne 0 ]; then
    git diff -M $other_branch...$branch_branch | colordiff | less -R
else
    git diff -M $other_branch...$branch_branch --color=auto
fi
