#!/usr/bin/env python
################################################################################
#
#  CDDL HEADER START
#
#  The contents of this file are subject to the terms of the Common Development
#  and Distribution License Version 1.0 (the "License").
#
#  You can obtain a copy of the license at
#  http:# www.opensource.org/licenses/CDDL-1.0.  See the License for the
#  specific language governing permissions and limitations under the License.
#
#  When distributing Covered Code, include this CDDL HEADER in each file and
#  include the License file in a prominent location with the name LICENSE.CDDL.
#  If applicable, add the following below this CDDL HEADER, with the fields
#  enclosed by brackets "[]" replaced with your own identifying information:
#
#  Portions Copyright (c) [yyyy] [name of copyright owner]. All rights reserved.
#
#  CDDL HEADER END
#
#  Copyright (c) 2017, Regents of the University of Minnesota.
#  All rights reserved.
#
#  Contributor(s):
#     Ellad B. Tadmor
#
################################################################################

# The docstring below is vc_description
'''Check that the model has no memory leaks. This is tested using the Valgrind
memory debugging tool (http://valgrind.org) by performing a series of
energy and force calculations on a randomly distorted face-centered cubic (FCC)
cube base structure for both non-periodic and periodic boundary conditions.
Separate configurations are tested for each species supported by the model, as
well as one containing a random distribution of all species. Configurations
used for testing are provided as auxiliary files.'''

# Python 2-3 compatible code issues
from __future__ import print_function
try:
   input = raw_input
except NameError:
   pass

import sys
import kimvc
from kimcalculator import KIMCalculator, KIM_get_supported_species_list
from ase import Atoms
import runner2
import subprocess
import re

__version__ = "000"
__author__ = "Ellad Tadmor"
vc_name = "vc-memory-leak"
vc_description = kimvc.vc_stripall(__doc__)
vc_category = "informational"
vc_grade_basis = "passfail"
vc_files = []

################################################################################
#
#   FUNCTIONS
#
################################################################################

################################################################################
def do_vc(model, vc):
    '''
    Do memory leak check
    '''
    # call the code used to look for memory leaks durectky to generate
    # the output for the report
    runner2.memory_leaks_test_process(model, vc, vc_files)

    # call memory leaks test process using valigrind to look for leaks
    valgrind_out = subprocess.check_output(['valgrind',
                                    '--suppressions=./valgrind-python.supp',
                                    'python', 'runner2.py', model],
                                    stderr=subprocess.STDOUT,
                                    universal_newlines=True)

    # print valgrind output to report
    dashwidth = 80
    vc.rwrite('')
    vc.rwrite('='*dashwidth)
    vc.rwrite(' '*7 + 'VALGRIND OUTPUT')
    vc.rwrite('='*dashwidth)
    vc.rwrite('')
    vc.rwrite(valgrind_out)
    vc.rwrite('='*dashwidth)

    # determine grade
    vc.rwrite('')
    vc.rwrite('To pass this verification check the number of bytes that are '
              '"definitely lost" ')
    vc.rwrite('or "indirectly lost" must be zero.')
    vc.rwrite('')
    vc.rwrite('NOTE that Valgrind will typically report non-zero '
              '"possibly lost" bytes due to')
    vc.rwrite("Python's internal memory allocation and garbage collection "
              "that it does not monitor.")
    vc.rwrite('')
    definitely_lost = int(re.search('definitely lost: ([0-9]+)',
                                    valgrind_out).group(1))
    indirectly_lost = int(re.search('indirectly lost: ([0-9]+)',
                                    valgrind_out).group(1))
    leak_detected = definitely_lost > 0 or indirectly_lost > 0

    # Run again and generate full valgrind output in aux file
    valgrind_out = subprocess.check_output(['valgrind',
                                            '--leak-check=full',
                                            '--show-leak-kinds=all',
                                            'python', 'runner2.py', model],
                                            stderr=subprocess.STDOUT,
                                            universal_newlines=True)
    aux_file = 'valgrind.out'
    vc_files.append(aux_file)
    vc.write_aux_string(aux_file, valgrind_out)
    vc.rwrite('Full Valgrind output written to auxiliary file "{}"'.
              format(aux_file))
    if leak_detected:
        vc.rwrite('(Search for the word "lost" in the file to identify '
                  'memory leaks.)')
    vc.rwrite('')

    # report grade
    if not leak_detected:
        vc_grade = 'P'
        vc_comment = 'No memory leak detected.'
    else:
        vc_grade = 'F'
        vc_comment = 'Memory leak detected.'

    vc.rwrite('Grade: {}'.format(vc_grade))
    vc.rwrite('')
    vc.rwrite('Comment: '+vc_comment)
    vc.rwrite('')

    return vc_grade, vc_comment

################################################################################
#
#   MAIN PROGRAM
#
###############################################################################
if __name__ == '__main__':

    # Get the model extended KIM ID:
    model = input("Model Extended KIM ID = ")

    # Define VC object and do verification check
    vc = kimvc.vc_object(vc_name, vc_description, __author__)
    with vc:
        # Perform verification check and get grade
#       try:
        vc_grade, vc_comment = do_vc(model, vc)
#       except:
#           vc_grade = "N/A"
#           vc_comment = "Unable to perform verification check due to an error."
#           sys.stderr.write('ERROR: Unable to perform verification check.\n')

        # Pack results in a dictionary and write VC property instance
        results = {"vc_name"        : vc_name,
                   "vc_description" : vc_description,
                   "vc_category"    : vc_category,
                   "vc_grade_basis" : vc_grade_basis,
                   "vc_grade"       : vc_grade,
                   "vc_comment"     : vc_comment,
                   "vc_files"       : vc_files}
        vc.write_results(results)