#!/usr/bin/python
#coding: utf-8


"""
NouvelOrdre exe

© Feth Arezki - feth >AT< tuttu.info, 2011

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 this program. If not, see <http://www.gnu.org/licenses/>.
"""


from argparse import ArgumentParser
from nouvelordre import NewOrder
from os.path import join
from StringIO import StringIO
from sys import exit, stderr, stdin, stdout
from tempfile import mkdtemp, mkstemp


def _do_pass(infile):
    """
    returns a temp file name where the result was stored
    """
    neworder = NewOrder(infile)
    handle = StringIO()
    neworder.reorder(handle)
    result = handle.getvalue()
    handle.close()
    return result


def compileerror(infile):
    try:
        if hasattr(infile, 'name') and not infile.isatty():
            with open(infile.name, 'r') as handle:
                try:
                    compile(handle.read(), infile.name, 'exec')
                except (SyntaxError, TypeError):
                    stderr.write(
                            "An error occured with your file, however I cannot"\
                            " even compile it, thus exiting with status 129\n"
                            )
                    exit(129)
    except Exception, err:
        stderr.write("Unexpected err: %s\n" % err)


def notimplemented(err):
    stderr.write(
        "Something is not implemented -you may ask for the feature."\
        "Exiting with status 130. Error:%s\n" % err)
    exit(130)


def main(infile, outfile, keep_files):
    """
    BEWARE:
    infile: a file descriptor like (must return lines when iterated over)
    outfile: a filename or a file descriptor (with a write() method)
    """
    #1st pass: outfile -> tmp1
    try:
        value1 = _do_pass(infile)
    except NotImplementedError, err:
        notimplemented(err)
    except (SyntaxError, TypeError):
        compileerror(infile)
    except Exception:
        stderr.write("Error in first pass on file %s .\n" % infile.name)
        raise
    #2nd pass: tmp1 -> tmp2
    try:
        value2 = _do_pass(StringIO(value1))
    except Exception:
        if keep_files:
            handle, tmpfile = mkstemp()
            with open(tmpfile, 'w') as handle:
                handle.write(value1)
        stderr.write("Error in second pass on file %s .\n" % infile.name)
        if keep_files:
            stderr.write("Saved temp file in %s .\n" % tmpfile)
        else:
            stderr.write(
                    "Would have saved the temp file with the --dump option\n."
                )
        raise

    #check that a processed file can be processed twice without a change
    if value1 != value2:
        if not keep_files:
            assert False, "Sorry, there is a bug in this program when ran "\
            "against %s. You may want to rerun with --dump."

        tmpdir = mkdtemp()
        for dump, name in ((value1, '1st_pass'), (value2, '2nd_pass')):
            with open(join(tmpdir, name), 'w') as handle:
                handle.write(dump)

        assert False, "Sorry, there is a bug in this program when ran "\
            "against %s. Work files kept in %s." % (infile.name, tmpdir)


    #deliver the result

    if isinstance(outfile, (str, unicode)):
        with open(outfile, 'w') as fdesc:
            fdesc.write(value1)
            return

    #we got a handle, for instance stdout
    outfile.write(value1)


def _parseargs():
    """
    returns parsed sys.argv
    """
    parser = ArgumentParser(
            description="""A tool for reordering
            import statements inside Python code blocks.
            Blocks separated by anything (white line for instance)
            are considered separately.
            """,
            epilog="""Limitations: This script will only handle first level
            statements. This script will not handle several "import module"
            for the same module in the same block.
            """,
            )
    parser.add_argument('--version', '-v', action='version', version='%(prog)s 0.1')
    parser.add_argument(
            '--infile',
            '-i',
            default=stdin,
            help='a Python source file, defaults to standard input',
            type=lambda name: open(name, 'r')
            )
    parser.add_argument(
        '--outfile',
        '-o',
        default=stdout,
        help='output, defaults to standard output',
        )
    parser.add_argument(
        '--dump',
        '-d',
        action='store_true',
        default=False,
        help='In case of failure, keep processing data in files',
        )
    return parser.parse_args()


if __name__ == '__main__':
    ARGS = _parseargs()
    main(ARGS.infile, ARGS.outfile, ARGS.dump)

