#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Module that sanitizes source files with specified modifiers."""


import commands
import os
import sys


_FILE_EXTENSIONS_TO_SANITIZE = ['cpp', 'h', 'c', 'gyp', 'gypi']

_SUBDIRS_TO_IGNORE = ['.git', '.svn', 'third_party']


def SanitizeFilesWithModifiers(directory, file_modifiers, line_modifiers):
  """Sanitizes source files with the specified file and line modifiers.

  Args:
    directory: string - The directory which will be recursively traversed to
        find source files to apply modifiers to.
    file_modifiers: list - file-modification methods which should be applied to
        the complete file content (Eg: EOFOneAndOnlyOneNewlineAdder).
    line_modifiers: list - line-modification methods which should be applied to
        lines in a file (Eg: TabReplacer).
  """
  for item in os.listdir(directory):

    full_item_path = os.path.join(directory, item)

    if os.path.isfile(full_item_path):  # Item is a file.

      # Only sanitize files with extensions we care about.
      if (len(full_item_path.split('.')) > 1 and
          full_item_path.split('.')[-1] in _FILE_EXTENSIONS_TO_SANITIZE):
        f = file(full_item_path)
        try:
          lines = f.readlines()
        finally:
          f.close()

        new_lines = []  # Collect changed lines here.
        line_number = 0  # Keeps track of line numbers in the source file.
        write_to_file = False  # File is written to only if this flag is set.

        # Run the line modifiers for each line in this file.
        for line in lines:
          original_line = line
          line_number += 1

          for modifier in line_modifiers:
            line = modifier(line, full_item_path, line_number)
            if original_line != line:
              write_to_file = True
          new_lines.append(line)

        # Run the file modifiers.
        old_content = ''.join(lines)
        new_content = ''.join(new_lines)
        for modifier in file_modifiers:
          new_content = modifier(new_content, full_item_path)
        if new_content != old_content:
          write_to_file = True

        # Write modifications to the file.
        if write_to_file:
          f = file(full_item_path, 'w')
          try:
            f.write(new_content)
          finally:
            f.close()
          print 'Made changes to %s' % full_item_path

    elif item not in _SUBDIRS_TO_IGNORE:
      # Item is a directory recursively call the method.
      SanitizeFilesWithModifiers(full_item_path, file_modifiers, line_modifiers)


############## Line Modification methods ##############


def TrailingWhitespaceRemover(line, file_path, line_number):
  """Strips out trailing whitespaces from the specified line."""
  stripped_line = line.rstrip() + '\n'
  if line != stripped_line:
    print 'Removing trailing whitespace in %s:%s' % (file_path, line_number)
  return stripped_line


def CrlfReplacer(line, file_path, line_number):
  """Replaces CRLF with LF."""
  if '\r\n' in line:
    print 'Replacing CRLF with LF in %s:%s' % (file_path, line_number)
  return line.replace('\r\n', '\n')


def TabReplacer(line, file_path, line_number):
  """Replaces Tabs with 4 whitespaces."""
  if '\t' in line:
    print 'Replacing Tab with whitespace in %s:%s' % (file_path, line_number)
  return line.replace('\t', '    ')


############## File Modification methods ##############


def CopywriteChecker(file_content, unused_file_path):
  """Ensures that the copywrite information is correct."""
  # TODO(rmistry): Figure out the legal implications of changing old copyright
  # headers.
  return file_content


def EOFOneAndOnlyOneNewlineAdder(file_content, file_path):
  """Adds one and only one LF at the end of the file."""
  if file_content and (file_content[-1] != '\n' or file_content[-2:-1] == '\n'):
    file_content = file_content.rstrip()
    file_content += '\n'
    print 'Added exactly one newline to %s' % file_path
  return file_content


def SvnEOLChecker(file_content, file_path):
  """Sets svn:eol-style property to LF."""
  output = commands.getoutput(
      'svn propget svn:eol-style %s' % file_path)
  if output != 'LF':
    print 'Setting svn:eol-style property to LF in %s' % file_path
    os.system('svn ps svn:eol-style LF %s' % file_path)
  return file_content


#######################################################


if '__main__' == __name__:
  sys.exit(SanitizeFilesWithModifiers(
      os.getcwd(),
      file_modifiers=[
          CopywriteChecker,
          EOFOneAndOnlyOneNewlineAdder,
          SvnEOLChecker,
      ],
      line_modifiers=[
          CrlfReplacer,
          TabReplacer,
          TrailingWhitespaceRemover,
      ],
  ))
