
from __future__ import with_statement

import os

from urlparse import urlsplit

from lxml import etree
from lxml.builder import ElementMaker

from cheshire3.baseObjects import Server
from cheshire3.bootstrap import BSLxmlParser, BootstrapDocument
from cheshire3.configParser import C3Object
from cheshire3.exceptions import (
    ConfigFileException,
    ObjectDoesNotExistException,
    XMLSyntaxError,
    FileSystemException,
    FileDoesNotExistException
    )
from cheshire3.internal import cheshire3Root, CONFIG_NS


class SimpleServer(Server):
    databases = {}
    # here because it's global to the install
    _possiblePaths = {'sortPath': {"docs": "Path to the 'sort' utility"}}

    def __init__(self, session, configFile="serverConfig.xml"):
        # self.defaultFunctionLog = "__api__"
        self.defaultFunctionLog = ""
        self.databaseConfigs = {}
        self.databases = {}
        self.id = "DefaultServer"
        session.server = self

        # Bootstrappage
        dom = self._getDomFromFile(session, configFile)
        try:
            topNode = dom.childNodes[0]
        except:
            topNode = dom
        C3Object.__init__(self, session, topNode, None)

        # Add default logger to session
        log = self.get_path(session, 'defaultLogger', None)
        if log:
            session.logger = log

    def _cacheDatabases(self, session):
        # Read in all Database configurations, build and cache Database objects
        for dbid in self.databaseConfigs.keys():
            db = self.get_object(session, dbid)
            self.databases[dbid] = db

    def _get_newDatabaseId(self, session, dbConfig):
        dbid = dbConfig.attrib.get('id', None)
        # Check that the identifier is not already in use by existing database
        try:
            self.get_object(session, dbid)
        except ObjectDoesNotExistException:
            # Doesn't exists, so OK to register it
            pass
        else:
            msg = ("Database with id '{0}' is already registered. "
                   "Please specify a different id in your configurations "
                   "file.".format(dbid))
            self.log_critical(session, msg)
            raise ConfigFileException(msg)
        return dbid

    def register_databaseConfigFile(self, session, file_path):
        """Register a Cheshire3 Database config file.

        Register a configuration file for a Cheshire3
        :py:class:`~cheshire3.baseObjects.Database` with the
        :py:class:`~cheshire3.baseObjects.Server`.

        This process simply tells the :py:class:`~cheshire3.baseObjects.Server`
        that it should include the configuration(s) in your file (it does not
        ingest your file) so you don't need to re-register when you make
        changes to the file.
        """
        # Read in proposed config file
        docFac = self.get_object(session, 'defaultDocumentFactory')
        docFac.load(session, file_path)
        confdoc = docFac.get_document(session)
        try:
            confrec = BSLxmlParser.process_document(session, confdoc)
        except XMLSyntaxError as e:
            msg = ("Config file {0} is not well-formed and valid XML: "
                   "{1}".format(file_path, e.message))
            self.log_critical(session, msg)
            raise ConfigFileException(msg)
        # Extract the database identifier
        confdom = confrec.get_dom(session)
        dbid = self._get_newDatabaseId(session, confdom)
        if dbid is None:
            msg = ("Config file {0} must have an 'id' attribute at the "
                   "top-level".format(file_path))
            self.log_critical(session, msg)
            raise ConfigFileException(msg)
        # Generate plugin XML
        plugin = E.config(
            E.subConfigs(
                E.path(
                    {'type': "database", 'id': dbid},
                    file_path
                )
            )
        )
        # Try to do this by writing config plugin file if possible
        serverDefaultPath = self.get_path(session,
                                          'defaultPath',
                                          cheshire3Root)
        userSpecificPath = os.path.join(os.path.expanduser('~'),
                                        '.cheshire3-server')
        pluginPath = os.path.join('configs',
                                  'databases',
                                  '{0}.xml'.format(dbid))
        try:
            pluginfh = open(os.path.join(serverDefaultPath, pluginPath), 'w')
        except IOError:
            try:
                pluginfh = open(os.path.join(userSpecificPath, pluginPath),
                                'w')
            except IOError:
                msg = ("Database plugin directory {0} unavailable for writing"
                       "".format(os.path.join(userSpecificPath, pluginPath)))
                self.log_critical(session, msg)
                raise FileSystemException(msg)
        pluginfh.write(
            etree.tostring(
                plugin,
                pretty_print=True,
                encoding="utf-8"
            )
        )
        pluginfh.close()
        self.log_info(session,
                      "Database configured in {0} registered with Cheshire3 "
                      "Server {1}".format(file_path, self.id))

    def unregister_databaseConfig(self, session, identifier):
        """Unregister a Cheshire3 Database.

        Unregister a Cheshire3 :py:class:`~cheshire3.baseObjects.Database` from
        the :py:class:`~cheshire3.baseObjects.Server`.

        This process tells the :py:class:`~cheshire3.baseObjects.Server` that
        it should drop the configuration(s) for the specified
        `~cheshire3.baseObjects.Database`.
        """
        # Try to do this by writing config plugin file if possible
        serverDefaultPath = self.get_path(session,
                                          'defaultPath',
                                          cheshire3Root
                                          )
        userSpecificPath = os.path.join(os.path.expanduser('~'),
                                        '.cheshire3-server'
                                        )
        pluginPath = os.path.join('configs',
                                  'databases',
                                  '{0}.xml'.format(identifier)
                                  )
        try:
            os.remove(os.path.join(userSpecificPath, pluginPath))
        except OSError:
            # File doesn't exist
            # Try server plugin directory
            try:
                os.remove(os.path.join(serverDefaultPath, pluginPath))
            except OSError:
                # File doesn't exist, or insufficient permission
                if os.path.exists(os.path.join(serverDefaultPath, pluginPath)):
                    msg = ("Insufficient write permissions for Server's "
                           "Database plugin directory"
                           )
                else:
                    msg = ("No Database plugin file for Database {0}"
                           "".format(identifier)
                           )
                self.log_critical(session, msg)
                raise FileSystemException(msg)

        self.log_info(session,
                      "Database {0} unregistered from Cheshire3 "
                      "Server {1}".format(identifier, self.id)
                      )


# Set up ElementMaker for Cheshire3 config namespace
E = ElementMaker(namespace=CONFIG_NS, nsmap={None: CONFIG_NS})
