# -*- encoding: utf-8 -*-
# (C) Copyright 2008 Tarek Ziadé <tarek@ziade.org>
"""
Custom config parser for Atomisator, and logging utilities.
"""
import os
from os.path import dirname
import sys
from os.path import join
from ConfigParser import ConfigParser
import re
import logging

from atomisator.main import PROCESSES

# XXX make logging level configurable
logger = logging.getLogger("atomisator")
formatter = logging.Formatter()
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO)

class ConfigurationError(Exception):
    pass

# logging
def log(msg):
    """Shortcut to logger."""
    logger.info(msg)

# see how this can be done with logging
def dotlog(msg):    
    """Flushes to stdout."""
    sys.stdout.write(msg)
    sys.stdout.flush()

DEFAULT_CONFIG_FILE = 'atomisator.cfg'
CONF_TMPL = join(dirname(__file__), 
                 'atomisator.cfg_tmpl')
CONF_TMPL = open(CONF_TMPL).read()

def generate_config(path):
    """Creates a default config file."""
    if os.path.exists(path):
        raise ValueError('%s already exists.' % path)
    file_ = open(path, 'w')
    try:
        file_.write(CONF_TMPL)
    finally:
        file_.close()
    logger.info('Default config generated at "%s."' % path)

class AtomisatorConfig(object):
    """Custom config parser
    """

    def __init__(self, cfg=None):
        self._parser = ConfigParser()
        if cfg is None:
            cfg = DEFAULT_CONFIG_FILE
        if not os.path.exists(cfg):
            raise ValueError('Could not read configuration (%s)' % cfg)
        self._file = cfg
        self._parser.read([cfg])

    #
    # basic APIs
    #
    def _get_simple_field(self, field, default=None):
        """Returns an option located in the `atomisator` section."""
        if not self._parser.has_option('atomisator', field):
            return default
        return self._parser.get('atomisator', field).strip()
    
    def _set_simple_field(self, field, value):
        """Set an option located in the `atomisator` section."""
        self._parser.set('atomisator', field, value)    

    def _set_multiline_value(self, name, value):
        """Set a multiline option located in the `atomisator` section."""
        def _quote(value):
            if ' ' in value:
                value = value.replace('"', "'")
                return '"%s"' % value
            return value

        def _line(entry):
            main = entry[0]
            params = [_quote(e) for e in entry[1]]
            return '%s %s' % (main, ' '.join(params))
        
        values = '\n'.join([_line(v) for v in value])
        self._parser.set('atomisator', name, values)

    def _get_multiline_value(self, name, default=None):
        """Returns a multiline option located in the `atomisator` section."""
        if not self._parser.has_option('atomisator', name):
            return default is None and None or []
        lines = self._parser.get('atomisator', name).split('\n')
        # crappy pattern matching
        def _rep(match):
            return match.groups()[0].replace(' ', ':::')
        def _args(line):
            line = re.sub(r'"(.*?)"', _rep, line)
            line = [element.replace(':::', ' ') 
                    for element in line.split() if element != '']
            return line[0].strip(), tuple([el.strip() 
                                           for el in line[1:]])
        return [_args(line) for line in lines if line.strip() != '']

    # 
    # properties and public APIs
    #
    def write(self):
        """ Saves the configuration.
        
        Writes the configuration into the file provided at initialization."""
        file_ = open(self._file, 'w')
        try:
            self._parser.write(file_)
        except:
            file_.close()

    def get_reader(self, name):
        """Returns the readers."""
        if not self._parser.has_section('readers'):
            return None
        if not self._parser.has_option('readers', name):
            return None
        return self._parser.get('readers', name)

    def _get_sources(self):
        return self._get_multiline_value('sources')
    def _set_sources(self, sources):
        self._set_multiline_value('sources', sources)
    sources = property(_get_sources, _set_sources)

    def _get_outputs(self):
        return self._get_multiline_value('outputs') 
    def _set_outputs(self, outputs):
        self._set_multiline_value('outputs', outputs)
    outputs = property(_get_outputs, _set_outputs)

    def _get_filters(self):
        return self._get_multiline_value('filters') 
    def _set_filters(self, filters):
        self._set_multiline_value('filters', filters)
    filters = property(_get_filters, _set_filters)

    def _get_enhancers(self):
        return self._get_multiline_value('enhancers')
    def _set_enhancers(self, value):
        self._set_multiline_value('enhancers', value)
    enhancers = property(_get_enhancers, _set_enhancers)

    def _get_database(self):
        return self._get_simple_field('database', 'sqlite:///_temp_.db')
    def _set_database(self, value):
        self._set_simple_field('database', value)
    database = property(_get_database, _set_database)

    def _get_timeout(self):
        return float(self._get_simple_field('timeout', '5'))
    def _set_timeout(self, value):
        self._set_simple_field('timeout', str(value))
    timeout = property(_get_timeout, _set_timeout)

    def _get_store_entries(self):
        value = self._get_simple_field('store-entries', 'true')
        value = value.lower()
        return value in ('true', '1')
    def _set_store_entries(self, value):
        value = value and 'true' or 'false'
        self._set_simple_field('store-entries', value)
    store_entries = property(_get_store_entries, _set_store_entries)

    def _get_max_age(self):
        return int(self._get_simple_field('max-age', '30'))
    def _set_max_age(self, value):
        self._set_simple_field('max-age', str(value))
    max_age = property(_get_max_age, _set_max_age)

    def _get_processes(self):
        return int(self._get_simple_field('processes', PROCESSES))
    def _set_processes(self, value):
        self._set_simple_field('processes', str(value))
    processes = property(_get_processes, _set_processes)


