from datetime import datetime
from models import Project, WebNotifier
from django.template.loader import render_to_string
from bbotui.utils import get_obj_comment

try:
    from pygments import highlight
    from pygments.lexers import PythonLexer as PyLex
    from pygments.formatters import HtmlFormatter as HtmlFmtr
    def python_to_html(code, title):
        return highlight(code, PyLex(), HtmlFmtr(full=True, title=title))
except ImportError:
    def python_to_html(code, title):
        return "<h2>%s</h2><pre>%s</pre>" % (title, code)
    

class ProjectNotReady(Exception):
    """
    Project does not contain sufficient data to generate configuration
    """
    def __init__(self, project):
        self.msg = "Insufficient data for Project (%s)" % unicode(project)


class ProjectConfig(object):
    """
    Buildbot configuration for a specific project.
    """
    def __init__(self, pid):
        """
        Initialises obj with data for project with id == pid.
        
        Raises bbotui.models.DoesNotExist if pid invalid.
        
        Raises ProjectNotReady if project is not ready for deployment.
        """
        p = self.project = Project.objects.get(id=pid) # may raise DoesNotExist
        p.update_status()
        if not p.is_complete:
            raise ProjectNotReady(self.project)
        
        self._compile_basic_info()
        self._compile_generic_config(p.repository_set, "change_source", True)
        self._compile_generic_config(p.scheduler_set, "schedulers", True)
        self._compile_generic_config(p.notifier_set, "status", True)
        self._compile_generic_config(p.buildslave_set, "slaves")
        self._compile_generic_config(p.builder_set, "builders")
        
        # set buildbot url
        web_notifiers = WebNotifier.objects.filter(project=p)
        if web_notifiers.count():
            first = web_notifiers[0]
            self.data["buildbot_url"] = "http://localhost:%d" % first.port
        else:
            self.data["buildbot_url"] = ""
    
    def render_config_to_html(self, title=""):
        return python_to_html(self.render_config_to_string(), title)
        
    def render_config_to_string(self, template_name = "bbotui/config/master.cfg"):
        return render_to_string(template_name, self.data)
        
    def _compile_basic_info(self):
        p = self.project
        self.data = {
            "project_name" : str(p.name),
            "project_description" : str(p.description),
            "project_website" : str(p.website),
            "generated_by" : str(p.owner.username),
            "generated_on" : datetime.now().strftime("%d %h %Y, %H:%M:%S"),
            "slave_port" : p.slave_port,
        }
    
    def _get_import_list_for_iterable(self, obj_list, require_cast=False):
        """
        Returns a list of import statements given a list of models. Duplicates are removed from
        the list.
        """
        import_list = []
        for obj in obj_list:
            if require_cast:
                obj = obj.cast()
            import_list.extend(obj.get_import_list())
        # return list with no dups
        import_list = list(set(import_list))
        import_list.sort(key=len)
        return "\n".join(import_list)
        
    def _get_config_block(self, cfg_name, obj_list, require_cast=False):
        out = []
        for obj in obj_list:
            if require_cast:
                obj = obj.cast()
            if obj.needs_extra_config:
                out.append(obj.get_extra_config_str())
                
        if out:
            out.append("") # add separator
            
        out.append("c['%s'] = [" % (cfg_name))
        for obj in obj_list:
            if require_cast:
                obj = obj.cast()
            out.append("\n    %s" % get_obj_comment(obj))
            out.append("    %s," % (obj.get_config_str(),))
        out.append("]")
        return "\n".join(out)
    
    def _compile_generic_config(self, queryset, cfg_name, require_cast=False):
        objs = queryset.all()
        self.data[cfg_name] = {
            "imports" : self._get_import_list_for_iterable(objs, require_cast),
            "config"  : self._get_config_block(cfg_name, objs, require_cast),
        }
        
        
        
    
        