"""Utility functions for Schema Sync"""

import re
import os
import datetime
import glob
import cStringIO

#REGEX_NO_TICKS = re.compile('`')
#REGEX_INT_SIZE = re.compile('int\(\d+\)')
REGEX_MULTI_SPACE = re.compile(r'\s\s+')
REGEX_DISTANT_SEMICOLIN = re.compile(r'(\s+;)$')
REGEX_FILE_COUNTER = re.compile(r"\_(?P<i>[0-9]+)\.(?:[^\.]+)$")
REGEX_TABLE_COMMENT = re.compile(r"COMMENT(?:(?:\s*=\s*)|\s*)'(.*?)'", re.I)
REGEX_TABLE_AUTO_INC = re.compile(r"AUTO_INCREMENT(?:(?:\s*=\s*)|\s*)(\d+)", re.I)


def versioned(filename):
    """Return the versioned name for a file.
       If filename exists, the next available sequence # will be added to it.
       file.txt => file_1.txt => file_2.txt => ...
       If filename does not exist the original filename is returned.

       Args:
            filename: the filename to version (including path to file)

       Returns:
            String, New filename.
    """
    name, ext = os.path.splitext(filename)
    files = glob.glob(name + '*' + ext)
    if not files:
        return filename

    files= map(lambda x: REGEX_FILE_COUNTER.search(x, re.I), files)
    file_counters = [i.group('i') for i in files if i]

    if file_counters:
        i = int(max(file_counters)) + 1
    else:
        i = 1

    return name + ('_%d' % i) + ext


def create_pnames(db, tag=None, date_format="%Y%m%d"):
    """Returns a tuple of the filenames to use to create the migration scripts.
       Filename format: <db>[_<tag>].<date=DATE_FORMAT>.(patch|revert).sql

        Args:
            db: srting, databse name
            tag: string, optional, tag for the filenames
            date_format: string, the current date format
                         Default Format: 21092009

        Returns:
            tuple of strings (patch_filename, revert_filename)
    """
    d = datetime.datetime.now().strftime(date_format)
    if tag:
        tag = re.sub('[^A-Za-z0-9_-]', '', tag)
        basename = "%s_%s.%s" % (db, tag, d)
    else:
        basename = "%s.%s" % (db, d)

    return ("%s.%s" % (basename, "patch.sql"),
            "%s.%s" % (basename, "revert.sql"))


class PatchBuffer(object):
    """Class for creating patch files

        Attributes:
            name: String, filename to use when saving the patch
            filters: List of functions to map to the patch data
            tpl: The patch template where the data will be written
                 All data written to the PatchBuffer is palced in the
                template variable %(data)s.
            ctx: Dictionary of values to be put replaced in the template.
            version_filename: Bool, version the filename if it already exists?
            modified: Bool (default=False), flag to check if the
                      PatchBuffer has been written to.
    """

    def __init__(self, name, filters, tpl, ctx, version_filename=False):
        """Inits the PatchBuffer class"""
        self._buffer = cStringIO.StringIO()
        self.name = name
        self.filters = filters
        self.tpl = tpl
        self.ctx = ctx
        self.version_filename = version_filename
        self.modified = False

    def write(self, data):
        """Write data to the buffer."""
        self.modified = True
        self._buffer.write(data)

    def save(self):
        """Apply filters, template transformations and write buffer to disk"""
        data = self._buffer.getvalue()
        if not data:
            return False

        if self.version_filename:
            self.name = versioned(self.name)
        fh = open(self.name, 'w')

        for f in self.filters:
            data = f(data)

        self.ctx['data'] = data

        fh.write(self.tpl % self.ctx)
        fh.close()

        return True

    def delete(self):
        """Delete the patch once it has been writen to disk"""
        if os.path.isfile(self.name):
            os.unlink(self.name)

    def __del__(self):
        self._buffer.close()