# Copyright (C) 2005 Maciej Katafiasz
#
# Author: Maciej Katafiasz
#
# This file is part of Purrr programme.
#
# Purrr 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 2, or (at your
# option) any later version.
#
# Purrr 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 Purrr; see the file COPYING. If not, write to the
# Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.

import re
import types

class NoTemplateDefinedError:
    pass

class FileRenamer:
    class State:
        pass
    def __init__(self, template):
        self.init(template)
        
    def init(self, template):
        self.template = template
        self.__state = self.State()
        self.__parse_template()

    def valid_counter(self, str):
        return re.match("^[Cc](\d*,)?([+-]?\d+)?,?([+-]?\d+)?,?(\d+)?$", str)

    def valid_nameref(self, str):
        return re.match("^[Nn]_?$", str)

    def valid_baseref(self, str):
        return re.match("^[Bb]_?$", str)

    def valid_extref(self, str):
        return re.match("^[Ee]\.?$", str)

    def parse_nameref(self, str):
        if re.match("^[Nn](?P<replace>_)?$", str).group("replace"):
            return 'info.old_name.replace("_", " ")'
        return 'info.old_name'

    def parse_baseref(self, str):
        if re.match("^[Bb](?P<replace>_)?$", str).group("replace"):
            return 'info.base_name.replace("_", " ")'
        return 'info.base_name'

    def parse_extref(self, str):
        if re.match("^[Ee](?P<dot>\.)?$", str).group("dot"):
            return '((info.extension and ("." + info.extension)) or "")'
        return 'info.extension'

    def parse_counter(self, str):
        m = re.match("^[Cc](?P<name>\d+)?$", str)
        if m:
            return (m.group('name'), 1, 1, 1)
        
        m = re.match("^[Cc](?P<name>\d+)?,(?P<start>[+-]?\d+)?$", str)
        if m:
            return (m.group('name'), 1, 1,int(m.group('start') or 1))

        m = re.match("^[Cc](?P<name>\d+)?,(?P<start>[+-]?\d+)?,(?P<step>[+-]?\d+)?$", str)
        if m:
            return (m.group('name'), int(m.group('step') or 1), 1, int(m.group('start') or 1))

        m = re.match("^[Cc](?P<name>\d+)?,(?P<start>[+-]?\d+)?,(?P<step>[+-]?\d+)?,(?P<pad>[+-]?\d+)?$", str)
        if m:
            return (m.group('name'),
                    int(m.group('step') or 1), int(m.group('pad') or 1), int(m.group('start') or 1))

    def __parse_template(self):
        t = self.template
        last = t.find("[")
        
        init = "def __init__(self):\n"
        code = """
def next_name(self, info):
    name = \"\""""
        fini = ""
        n_counters = 0
        counters = {}
        for chunk in self.chunkify(t):
            if (chunk[1] == "special") and self.valid_counter(chunk[0]):
                cnt = self.parse_counter(chunk[0])
                try:
                    cnt = counters[cnt[0] or n_counters]
                except KeyError:
                    counters[cnt[0] or n_counters] = cnt
                    init += "    self.C" + (cnt[0] or repr(n_counters)).replace("-","_") \
                    + " = " + repr(cnt[1]) + "\n"

                    fini += "    self.C" + (cnt[0] or repr(n_counters)).replace("-","_") \
                    + " += " + repr(cnt[2]) + "\n"

                code += " + repr(self.C" + (cnt[0] or repr(n_counters)).\
                replace("-","_") + ").zfill(" + repr(cnt[3]) + ")"
                
                n_counters += 1

            elif (chunk[1] == "special") and self.valid_nameref(chunk[0]):
                code += " + " + self.parse_nameref(chunk[0])

            elif (chunk[1] == "special") and self.valid_baseref(chunk[0]):
                code += " + " + self.parse_baseref(chunk[0])

            elif (chunk[1] == "special") and self.valid_extref(chunk[0]):
                code += " + " + self.parse_extref(chunk[0])

            else:
                if chunk[0]:
                    code += " + \"" + chunk[0].replace("\"","\\\"") + "\""

        # If there are no counters, make sure __init__ is still valid code
        init += "    pass\n"
        code += "\n"
        fini += "    return name\n"

        c = compile(init + code + fini, repr(self), "exec")
        x = {}
        exec c in globals(), self.State.__dict__
        self.__state = self.State()
        self.State.next_name = types.MethodType(self.State.__dict__['next_name'], self.__state)

    def next_name(self, old_name):
        return self.__state.next_name(old_name)

    def chunkify(self, s):
        """Parse string, chopped into "plain" and "special" slices.
Slice is special if enclosed in [ and ], unless braces are escaped with \\.
Everything else is plain. Unescaped braces are swallowed"""
        r = ""
        state = "plain"
        escape = False
        for i in s:
            if i == "\\" and not escape:
                escape = True
                continue
            if state == "plain":
                if i == "[" and not escape:
                    state = "special"
                    if r: yield (r, "plain")
                    r = ""
                else:
                    r = r + i
            else:
                if i == "]" and not escape:
                    state = "plain"
                    if r: yield (r, "special")
                    r = ""
                else:
                    r = r + i
            escape = False
        yield (((state == "special" and "[") or "") + r, "plain")
