#!/usr/bin/env python

import os, os.path, re, sys
from warnings import warn

sys.path.append("/usr/share/linux-support-2.6.32-5/lib/python")

from debian_linux.patches import PatchSeries, PatchSeriesList

_default_home = "/usr/src/kernel-patches/all/2.6.32/debian"
revisions = "orig base 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 34squeeze1 35 35squeeze1 35squeeze2 36 37 38 39 39squeeze1 40 41 41squeeze1 41squeeze2 42 43 44 45 46 46squeeze1 47 48 48squeeze1 48squeeze2 48squeeze3 48squeeze4 48squeeze5 48squeeze6".split()
upstream = "2.6.32"

class MatchExtra(object):
    def __init__(self, arch, featureset):
        self.arch, self.featureset = arch, featureset

        self.matched_arch = self.matched_featureset = False

    def __call__(self, obj):
        if not self:
            return False

        data = obj.data

        match_arch = []
        match_featureset = []
        for i in data:
            if i.startswith("arch="):
                match_arch.append(i[5:])
            elif i.startswith("featureset="):
                match_featureset.append(i[11:])
            else:
                raise RuntimeError('Ignored unknown modifier: %s' % i)

        ret_arch = ret_featureset = False

        if match_arch:
            if self.arch is not None:
                if self.arch in match_arch:
                    self.matched_arch = True
                    ret_arch = True

        else:
            ret_arch = True

        if match_featureset:
            if self.featureset is not None:
                if self.featureset in match_featureset:
                    self.matched_featureset = True
                    ret_featureset = True

        else:
            ret_featureset = True

        return ret_arch and ret_featureset

    def __nonzero__(self):
        return self.arch is not None or self.featureset is not None

    def info(self):
        ret = []
        if self.matched_arch:
            ret.append("arch=%s" % self.arch)
        if self.matched_featureset:
            ret.append("featureset=%s" % self.featureset)
        return ret

_marker = object()

class version_file(object):
    _file = 'version.Debian'
    extra = None
    in_progress = False

    def __init__(self, upstream = None):
        if os.path.exists(self._file):
            s = file(self._file).readline().strip()
            self._read(s)
        elif upstream:
            warn('No %s file, assuming Debian Linux %s' % (self._file, upstream))
            self.upstream = upstream
            self.revision = 'orig'
        else:
            raise RuntimeError, "Not possible to determine version"

    def __str__(self):
        if self.in_progress:
            return "unstable"
        ret = [self.upstream, self.revision]
        if self.extra is not None:
            ret.extend(self.extra.info())
        return ' '.join(ret)

    def _read(self, s):
        if s == 'unstable':
            raise RuntimeError("Tree is in an unstable condition. Can't continue!")
        list = s.split()
        self.upstream, self.revision = list[0:2]

        arch = featureset = None
        for i in list[2:]:
            if i.startswith("arch="):
                arch = i[5:]
            elif i.startswith("featureset="):
                featureset = i[11:]
            else:
                raise RuntimeError("Can't parse extra information")
        self.extra = MatchExtra(arch, featureset)

    def _write(self):
        if os.path.lexists(self._file):
            os.unlink(self._file)
        file(self._file, 'w').write('%s\n' % self)

    def begin(self):
        self.in_progress = True
        self._write()

    def commit(self, revision, extra = _marker):
        self.in_progress = False
        self.revision = revision
        if extra is not _marker:
            self.extra = extra
        self._write()

def main():
    options, args = parse_options()

    if len(args) > 1:
        print "Too much arguments"
        return

    home = options.home

    vfile = version_file(upstream)
    current_revision = vfile.revision
    current_extra = vfile.extra

    if len(args) == 1:
        target_revision = args[0]
    else:
        target_revision = revisions[-1]
    target_extra = MatchExtra(options.arch, options.featureset)

    if vfile.upstream != upstream:
        raise RuntimeError("Upstream version differs between tree (%s) and package (%s)" % (vfile.upstream, upstream))
    if current_revision not in revisions:
        raise RuntimeError, "Current revision is not in our list of revisions"
    if target_revision not in revisions:
        raise RuntimeError, "Target revision is not in our list of revisions"

    if current_revision == target_revision and current_extra == target_extra:
        print "Nothing to do"
        return

    current_index = revisions.index(current_revision)
    target_index = revisions.index(target_revision)

    if current_extra:
        if current_revision != revisions[-1]:
            raise RuntimeError, "Can't patch from %s with options %s" % (current, ' '.join(current_extra))
        consider = ['%s-extra' % i for i in revisions[1:current_index + 1]]
        s = PatchSeriesList.read(home, consider)
        vfile.begin()
        s(cond = current_extra, reverse = True)
        vfile.commit(current_revision, None)

    if current_index < target_index:
        consider = revisions[current_index + 1:target_index + 1]
        s = PatchSeriesList.read(home, consider)
        vfile.begin()
        s()
        vfile.commit(target_revision)
    elif current_index > target_index:
        consider = revisions[target_index + 1:current_index + 1]
        s = PatchSeriesList.read(home, consider)
        vfile.begin()
        s(reverse = True)
        vfile.commit(target_revision)

    if target_extra:
        consider = ['%s-extra' % i for i in revisions[1:target_index + 1]]
        s = PatchSeriesList.read(home, consider)
        vfile.begin()
        s(cond = target_extra)
        vfile.commit(target_revision, target_extra)

def parse_options():
    from optparse import OptionParser
    parser = OptionParser(
        usage = "%prog [OPTION]... [TARGET]",
    )
    parser.add_option(
        '-a', '--arch',
        dest = 'arch',
        help = "arch",
    )
    parser.add_option(
        '-f', '--featureset',
        dest = 'featureset',
        help = "featureset",
    )
    parser.add_option(
        '-H', '--overwrite-home',
        default = _default_home, dest = 'home',
        help = "overwrite home [default: %default]",
    )

    options, args = parser.parse_args()

    if options.arch is None and options.featureset is not None:
        raise RuntimeError('You specified a featureset without an arch, this is not really working')

    return options, args

if __name__ == '__main__':
    def showwarning(message, category, filename, lineno):
        sys.stderr.write("Warning: %s\n" % message)
    import warnings
    warnings.showwarning = showwarning
    try:
        main()
    except RuntimeError, e:
        sys.stderr.write("Error: %s\n" % e)
        raise SystemExit, 1

