#!/usr/bin/env python
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

"""Trims trailing whitespace from files."""

from __future__ import print_function

import os
import re
import subprocess
import sys

try:
    from StringIO import StringIO
except ImportError:
    from io import StringIO


__version__ = '0.1.2'


CR = '\r'
LF = '\n'
CRLF = '\r\n'


def trim(text):
    """Return trailing whitespace removed."""
    split_lines = StringIO(text).readlines()
    line_ending = find_line_ending(split_lines)
    return line_ending.join(
        [line.rstrip() for line in split_lines]).rstrip() + line_ending


def find_line_ending(source):
    """Return type of line ending used in source."""
    cr, lf, crlf = 0, 0, 0
    for s in source:
        if s.endswith(CRLF):
            crlf += 1
        elif s.endswith(CR):
            cr += 1
        elif s.endswith(LF):
            lf += 1
    _max = max(cr, crlf, lf)
    if _max == lf:
        return LF
    elif _max == crlf:
        return CRLF
    elif _max == cr:
        return CR
    else:
        return LF


def trim_file(filename):
    """Remove trailing whitespace from a file in place."""
    with open_unicode_file(filename) as input_file:
        original_text = input_file.read()

    trimmed_text = trim(original_text)

    if trimmed_text != original_text:
        with open_unicode_file(filename, 'w') as output_file:
            output_file.write(trimmed_text)


def is_text(filename):
    """Return True if file is a text file."""
    process = subprocess.Popen(['file', filename], stdout=subprocess.PIPE)
    result = process.communicate()[0].decode('utf-8').lower()

    for encoding in ['ascii', 'utf-8 unicode']:
        if re.match(r'.*:[^:]* ' + encoding + r' text\s*', result):
            return True

    return False


def open_unicode_file(filename, mode='r'):
    """Return opened Unicode file."""
    import io
    return io.open(filename, mode=mode, encoding='utf-8',
                   newline='')  # Preserve line endings


def main():
    """Main."""
    import argparse
    parser = argparse.ArgumentParser(description=__doc__, prog='trim')
    parser.add_argument('--version', action='version', version=__version__)
    parser.add_argument('files', nargs='+',
                        help='files to trim')

    args = parser.parse_args()

    filenames = list(set(args.files))
    while filenames:
        name = filenames.pop(0)
        if os.path.isdir(name):
            for root, directories, children in os.walk(name):
                filenames += [os.path.join(root, f) for f in children
                              if not f.startswith('.')]
                for d in directories:
                    if d.startswith('.'):
                        directories.remove(d)
        elif is_text(name):
            print('[file:%s]' % name, file=sys.stderr)
            try:
                trim_file(name)
            except IOError as error:
                print(str(error), file=sys.stderr)
        else:
            # Ignore binary files.
            pass


if __name__ == '__main__':
    try:
        sys.exit(main())
    except KeyboardInterrupt:
        sys.exit(1)
