#!/usr/bin/python3
#-*- coding:utf-8 -*-

#    Copyright © 2009, 2011-2014  B. Clausius <barcc@gmx.de>
#
#    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/>.


import sys, os, re

class Py2pyxParseError (Exception): pass


def main(argv):
    if not (1 < len(argv) <= 4):
        print('usage:', os.path.basename(__file__), 'python-file [pyx-filename [pxd-filename]]',
            file=sys.stderr)
        return 1
        
    arg_src = argv[1]
    arg_dst = argv[2] if len(argv) > 2 else None
    arg_pxd = argv[3] if len(argv) > 3 else None
    if not os.path.exists(arg_src):
        print('error:', arg_src, 'does not exist', file=sys.stderr)
        return 1
    create_pyx(arg_src, arg_dst, arg_pxd)
    return 0
    
def create_pyx(src_path, dst_path, pxd_path):
    errors = 0
    openr = lambda path: open(path, 'rt', encoding='utf-8')
    openw = lambda path: open(path, 'wt', encoding='utf-8') if path else sys.stdout
    next_cntlines, next_pyxpat, next_pxdpat = 0, None, None
    
    # key: px command
    # value:
    #      pattern for pyx-file or None (copy line),
    #      pattern for pxd-file or None (skip line),
    #      count next lines or "count" (use px argument),
    #      next lines replace pattern for pyx-file or None,
    #      next lines replace pattern for pxd-file or None
    fdict = {
                None:   (None,            None,     0,       None,              None),
                '+':    (r'\1\5\6 \2\n',  r'\1\5',  0,       None,              None),
                '-':    (r'\1#\2\5\6\n',  None,     1,       r'\1#\2\5\6\n',    r'\1\5'),
                ':':    (r'\1#\2\5\6\n',  None,     '*',     r'\1#\2\5\6\n',    r'\1\5'),
                '.':    (r'\1#\2\5\6\n',  None,     0,       None,              None),
                '/':    (r'\1\5\6 \2\n',  r'\1\5',  1,       r'\1#\2\5\6\n',    None),
                '>':    (r'\1#\2\5\6\n',  r'\1\5',  0,       None,              None),
            }
    fdict['#'] = fdict['>']
    
    # groups in match objects and patterns
    #   1: leading space
    #   2: px command
    #   3: pxd flag
    #   4: px key
    #   5: px argument
    #   6: trailing ":"
    
    pxd_flag = False
    with openr(src_path) as srcf, openw(dst_path) as dstf, openw(pxd_path) as pxdf:
        pxdf.write('#file: {}\n\n'.format(src_path))
        for lineno, line in enumerate(srcf):
            try:
                match = re.match(r'^( *)(#px *(d?)(.)|)(.*?)(:?)\n$', line)
                if match.group(2):
                    if next_cntlines == 0:
                        pxd_flag = match.group(3) == 'd'
                    elif next_cntlines == 1:
                        raise Py2pyxParseError('#px-command found, normal line expected')
                    elif next_cntlines == '*':
                        if match.group(4) != '.':
                            raise Py2pyxParseError('closing #px-command or normal line expected')
                    else:
                        assert False
                    pyxpat, pxdpat, next_cntlines, next_pyxpat, next_pxdpat = fdict[match.group(4)]
                elif next_cntlines == 0:
                    pyxpat, pxdpat, next_cntlines, next_pyxpat, next_pxdpat = fdict[match.group(4)]
                elif next_cntlines == 1:
                    pyxpat = next_pyxpat
                    pxdpat = next_pxdpat
                    next_cntlines = 0
                elif next_cntlines == '*':
                    pyxpat = next_pyxpat
                    pxdpat = next_pxdpat
            except Exception as e:
                print('%s:%d:'%(src_path, lineno+1), e, file=sys.stderr)
                print('  Invalid line:', line, file=sys.stderr, end='')
                pyxpat = None
                pxdpat = None
                next_cntlines = 0
                errors += 1
            finally:
                pyxline = line if pyxpat is None else match.expand(pyxpat)
                pxdline = pxdpat and match.expand(pxdpat)
                dstf.write(pyxline)
                if pxd_flag and pxdline is not None:
                    pxdline = pxdline.rstrip() + '    #line {}\n'.format(lineno+1)
                    pxdf.write(pxdline)
    if errors:
        raise Py2pyxParseError('create_pyx failed with %s errors' % errors)
        
    
if __name__ == '__main__':
    sys.exit(main(sys.argv))
    

