#   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import sys
import subprocess


def find_all(srcstr, substr):
    '''
    to find all desired substring in the source string
     and return their starting indices as a list

    Args:
        srcstr(str): the parent string
        substr(str): substr
    
    Returns:
        list: a list of the indices of the substrings 
              found
    '''

    indices = []

    gotone = srcstr.find(substr)

    while (gotone != -1):

        indices.append(gotone)

        gotone = srcstr.find(substr, gotone + 1)

    return indices


def check_indent(cdline):
    '''
    to check the indent of a given code line
    
    to get the number of starting blank chars,
    e.t. blankspaces and \t
    
    \t will be interpreted as 4 single blankspaces, 
    e.t. '\t'='    '
    
    Args:
        cdline(str) : a single line of code from the source file

    Returns:
        int : the indent of the number of interpreted 
             blankspaces
    '''

    indent = 0
    for c in cdline:
        if c == '\t':
            indent += 4
        elif c == ' ':
            indent += 1
        if c != ' ' and c != '\t':
            break

    return indent


#srccom: raw comments in the source,including ''' and original indent


def sampcd_extract_and_run(srccom,
                           name,
                           logf,
                           htype="def",
                           hname="",
                           show_details=False):
    '''
    Extract and run sample codes from source comment and
    the result will be returned.

    As an ultimate result, this function returns a list of 
    status codes for each sample code (in top-down order)
    found in srccom.

    status code deciphering:

        3:error sample code
        2:have sample code but format is wrong
        1:no sample code
        0:successful
        -1:no comments found 
        -2:in white list
    
    there may be several examples in a source comment
    so status deserves a list to contain the states.
    For instance, some API has three example codes, 
    code 1 is successful, code 2 is error, code 3 is successful
    so the list to return is [0,3,0]

    Args:
        srccom(str): the source comment of some API whose
                     example codes will be extracted and run.
        name(str): the name of the API.
        logf(file): for logging the output in case they are
                    flushed.
        htype(str): the type of hint banners, def/class/method.
        hname(str): the name of the hint  banners , e.t. def hname.
        show_details(bool):  Set it to False to print wrong sample 
                             codes only.
    
    Returns:
        list: the status code of all the sample codes found in srccom.
                  

    

    '''

    def sampcd_header_print(name, sampcd, htype, hname, logf):
        '''
        print hint banner headers.
        
        Args:
            name(str): the name of the API.
            sampcd(str): sample code string
            htype(str): the type of hint banners, def/class/method.
            hname(str): the name of the hint  banners , e.t. def hname.
            logf(file): for logging the output in case they are
            flushed.
        '''
        print_header(logf, htype, hname)

        print "Sample code " + str(y) + " extracted for " + name + "   :"
        print "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
        print(sampcd)
        print "----example code check----\n"
        print "executing sample code ....."
        print "execution result:"
        logf.write("\nSample code extracted for " + name + "   :\n")
        logf.write("\n" + sampcd + "\n")
        logf.write("\n----example code check----\n")
        logf.write("\nexecuting sample code .....\n")
        logf.write("\nexecution result:\n")

    sampcd_begins = find_all(srccom, " code-block:: python")

    status = []

    if (len(sampcd_begins) == 0):

        print_header(logf, htype, hname)
        '''
        detect sample codes using >>> to format
        and consider this situation as wrong
        '''
        if (srccom.find("Examples:") != -1):
            print "----example code check----\n"
            logf.write("\n----example code check----\n")

            if (srccom.find(">>>") != -1):
                logf.write(
                    "Deprecated sample code style:\n\n    Examples:\n\n        >>>codeline\n        >>>codeline\n\n\n "
                    + "Please use '.. code-block:: python' to " +
                    "format sample code.\n")
                print(
                    "Deprecated sample code style:\n\n    Examples:\n\n        >>>codeline\n        >>>codeline\n\n\n "
                    + "Please use '.. code-block:: python' to " +
                    "format sample code.\n")
                status.append(2)
                print "status code for all sample codes in " + name + " : " + str(
                    status)

        else:
            print "No sample code!\n"
            logf.write("\nNo sample code!\n")
            status.append(1)
            print "status code for all sample codes in " + name + " : " + str(
                status)

    for y in range(1, len(sampcd_begins) + 1):

        sampcd_begin = sampcd_begins[y - 1]
        sampcd = srccom[sampcd_begin + len(" code-block:: python") + 1:]

        sampcd = sampcd.split("\n")

        #remove starting empty lines
        while sampcd[0].replace(' ', '').replace('\t', '') == '':
            sampcd.pop(0)

        #the mininmum indent, which is the indent of the first 
        #non-empty line
        min_indent = check_indent(sampcd[0])

        sampcd_to_write = []

        for i in range(0, len(sampcd)):

            cdline = sampcd[i]

            #handle empty lines or those only with spaces/tabs
            if cdline.strip() == '':
                continue

            this_indent = check_indent(cdline)
            if (this_indent < min_indent):
                break

            else:
                cdline = cdline.replace('\t', '    ')
                sampcd_to_write.append(cdline[min_indent:])

        sampcd = '\n'.join(sampcd_to_write)
        if sys.argv[1] == "cpu":
            sampcd = '\nimport os\n' + 'os.environ["CUDA_VISIBLE_DEVICES"] = ""\n' + sampcd
        if sys.argv[1] == "gpu":
            sampcd = '\nimport os\n' + 'os.environ["CUDA_VISIBLE_DEVICES"] = "0"\n' + sampcd
        sampcd += '\nprint ' + '\"' + name + ' sample code is executed successfully!\"\n'

        if (len(sampcd_begins) > 1):
            tfname = name + "_example_" + str(y) + ".py"
        else:
            tfname = name + "_example" + ".py"

        tempf = open("samplecode_temp/" + tfname, 'w')
        tempf.write(sampcd)
        tempf.close()

        cmd = ["python", "samplecode_temp/" + tfname]

        subprc = subprocess.Popen(
            cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        output, error = subprc.communicate()

        msg = "".join(output)
        err = "".join(error)

        if (subprc.returncode != 0):

            print("\nSample code error found in " + name + ":\n")
            sampcd_header_print(name, sampcd, htype, hname, logf)
            print "subprocess return code: " + str(subprc.returncode)
            print("Error Raised from Sample Code " + name + " :\n")
            print err
            print msg
            logf.write("\nError Raised from Sample Code " + name + " :\n")
            logf.write("\n" + msg + "\n")

            status.append(3)
            print "status code for all sample codes in " + name + str(status)
        #It works!
        else:
            status.append(0)
            if show_details:
                sampcd_header_print(name, sampcd, htype, hname, logf)
                print "subprocess return code: " + str(subprc.returncode)