#! /usr/bin/env python3
# -*- coding: utf-8 -*-

# Copyright © 2016, IOhannes m zmölnig, IEM

# This file is part of virtshaus
#
# 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 3 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 iaem-downloader. If not, see <http://www.gnu.org/licenses/>.

import os

VERSION = "2.0"


def post(s):
    print(s)


def parseCmdlineArgs():
    import argparse

    parser = argparse.ArgumentParser()
    DEFAULT_CONFIGDIR = "/etc/virtshaus/iemsync.d/hosts.cfg"

    parser.add_argument(
        "config",
        nargs="*",
        help="configfiles or directories with configfiles" "(ending in .conf)",
    )

    p = parser.add_argument_group("backup")
    p.add_argument(
        "-s", "--source", help="source directory (overrides single configfile"
    )
    p.add_argument(
        "-t", "--target", help="target directory (overrides single configfile"
    )
    p.add_argument(
        "-f", "--filter", action="append", default=[], help="additional filter file"
    )

    p = parser.add_argument_group("logging")
    p.add_argument(
        "--logfile",
        "--log-file",
        help="logfile (created by rsync)",
    )
    p.add_argument(
        "-C",
        "--cron",
        action="store_true",
        help="cron-mode (only print, if an error occurred)",
    )

    p.add_argument(
        "-v",
        "--verbose",
        action="count",
        default=0,
        help="raise verbosity (can be given multiple times)",
    )
    p.add_argument(
        "-q",
        "--quiet",
        action="count",
        default=0,
        help="lower verbosity (can be given multiple times)",
    )
    p = parser.add_argument_group("debugging")
    p.add_argument(
        "-d",
        "--debug",
        action="store_true",
        help="debug mode (print command before running them)",
    )
    p.add_argument(
        "-n",
        "--dry-run",
        action="count",
        default=0,
        help="dry run (can be given multiple times to increase dryness)",
    )
    parser.add_argument(
        "-V",
        "--version",
        action="version",
        version="%%(prog)s %s" % (VERSION,),
        help="print version number and exit",
    )

    args = parser.parse_args()
    args.verbose = args.verbose - args.quiet
    del args.quiet
    return args


def find_file(filename, searchpaths):
    for p in [""] + searchpaths:
        f = os.path.join(p, filename)
        if os.path.exists(f):
            return os.path.realpath(f)
    return None


def find_unique_files(files, searchpaths):
    result = []
    for f in files:
        f = find_file(f, searchpaths)
        if f and f not in result:
            result.append(f)
    return result


# LATER use this to allow multiple configurations...
def readConfigFile(configfile):
    try:
        import configparser
    except ImportError:
        import ConfigParser as configparser
    config = configparser.ConfigParser()
    config.read(configfile)
    configdir = os.path.dirname(configfile)

    configs = []

    if config.sections():
        configs = [dict(config[section].items()) for section in config.sections()]
    else:
        configs = [config.defaults()]

    return (configs, configdir)


def readConf(configfile, source=None, target=None, filters=[], logfile=None):
    try:
        import configparser
    except ImportError:
        import ConfigParser as configparser
    config = configparser.ConfigParser()
    config.read(configfile)
    configdir = os.path.dirname(configfile)
    cfg = config.defaults()
    if config.sections():
        section = config[config.sections()[0]]
        cfg = {k: v for k, v in section.items()}
    source = source or cfg.get("source", None)
    target = target or cfg.get("target", None)
    logfile = logfile or cfg.get("logfile", None)
    filters = list(filters)
    f = cfg.get("filter")
    if f:
        filters = [f] + filters
    filters = find_unique_files(filters, [configdir])
    return (source, target, filters, logfile)


def expandConfigs(confs):
    result = []
    for c in confs:
        if os.path.isfile(c):
            c = [c]
        elif os.path.isdir(c):
            c = [
                os.path.join(c, f) for f in sorted(os.listdir(c)) if f.endswith(".conf")
            ]
        else:
            post("skipping non-existent config '%s'" % c)
            c = []
        for f in c:
            f = os.path.realpath(f)
            if f not in result:
                result.append(f)
    return result


def runCmd(config, args, dry=False):
    import subprocess

    options = []
    source, target, filters, logfile = readConf(
        config, args.source, args.target, args.filter, args.logfile
    )

    if args.dry_run:
        options += ["--dry-run"]
    if args.dry_run > 1:
        dry = True
    if args.verbose > 0:
        options += ["--stats"]
        options += ["-v"] * (args.verbose - 1)
    if logfile:
        options += ["--log-file", logfile]

    # remove extraneous files
    options += ["--delete"]
    # archive mode (permission preservation)
    options += ["--archive"]
    # preserve full paths
    options += ["--relative"]

    # character translation
    # options += ['--iconv=UTF8,ISO8859-15"']

    if filters:
        options += ["--delete-excluded"]
    for x in filters:
        options += ["--filter", "merge %s" % x]

    options += [source]
    options += [target]

    cmd = ["rsync"] + options

    if args.debug:
        post(cmd)

    if dry:
        cmd = ["echo"] + cmd
    subprocess.call(cmd)


if __name__ == "__main__":
    import sys

    args = parseCmdlineArgs()
    args.config = expandConfigs(args.config)

    for c in args.config:
        runCmd(c, args, False)
