# Copyright (C) 2010 Canonical
#
# Authors:
#  Gary Lasker
#  Michael Vogt
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; version 3.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

import logging
import xapian

from gettext import gettext as _

import softwarecenter.distro

from softwarecenter.enums import (SortMethods,
                                  Icons,
                                  ViewPages,
                                 )

LOG = logging.getLogger(__name__)


class ChannelsManager(object):
    def __init__(self, db, **kwargs):
        self.distro = softwarecenter.distro.get_distro()
        self.db = db

    # public
    @property
    def channels(self):
        return self._get_channels_from_db()

    @property
    def channels_installed_only(self):
        return self._get_channels_from_db(True)

    @classmethod
    def channel_available(kls, channelname):
        pass

    # private
    def _get_channels_from_db(self, installed_only=False):
        """
        (internal) implements 'channels()' and 'channels_installed_only()'
        properties
        """
        distro_channel_origin = self.distro.get_distro_channel_name()

        # gather the set of software channels and order them
        other_channel_list = []
        cached_origins = []
        for channel_iter in self.db.xapiandb.allterms("XOL"):
            if len(channel_iter.term) == 3:
                continue
            channel_name = channel_iter.term[3:]
            channel_origin = ""

            # get origin information for this channel
            m = self.db.xapiandb.postlist_begin(channel_iter.term)
            doc = self.db.xapiandb.get_document(m.get_docid())
            for term_iter in doc.termlist():
                if (term_iter.term.startswith("XOO") and
                    len(term_iter.term) > 3):
                    channel_origin = term_iter.term[3:]
                    break
            LOG.debug("channel_name: %s" % channel_name)
            LOG.debug("channel_origin: %s" % channel_origin)
            if channel_origin not in cached_origins:
                other_channel_list.append((channel_name, channel_origin))
                cached_origins.append(channel_origin)

        dist_channel = None
        other_channels = []
        unknown_channel = []
        local_channel = None

        for (channel_name, channel_origin) in other_channel_list:
            if not channel_name:
                unknown_channel.append(SoftwareChannel(
                    channel_name,
                    channel_origin,
                    None,
                    installed_only=installed_only))
            elif channel_origin == distro_channel_origin:
                dist_channel = (SoftwareChannel(
                    channel_name,
                    channel_origin,
                    None,
                    installed_only=installed_only))
            elif channel_name == "notdownloadable":
                if installed_only:
                    local_channel = SoftwareChannel(
                        channel_name,
                        None,
                        None,
                        installed_only=installed_only)
            else:
                other_channels.append(SoftwareChannel(
                    channel_name,
                    channel_origin,
                    None,
                    installed_only=installed_only))

        # set them in order
        channels = []
        if dist_channel is not None:
            channels.append(dist_channel)
        channels.extend(other_channels)
        channels.extend(unknown_channel)
        if local_channel is not None:
            channels.append(local_channel)

        for channel in channels:
            if installed_only:
                channel._channel_view_id = ViewPages.INSTALLED
            else:
                channel._channel_view_id = ViewPages.AVAILABLE
        return channels


class SoftwareChannel(object):
    """
    class to represent a software channel
    """

    ICON_SIZE = 24

    def __init__(self, channel_name, channel_origin, channel_component,
                 source_entry=None, installed_only=False,
                 channel_icon=None, channel_query=None,
                 channel_sort_mode=SortMethods.BY_ALPHABET):
        """
        configure the software channel object based on channel name,
        origin, and component (the latter for detecting the partner
        channel)
        """
        self._channel_name = channel_name
        self._channel_origin = channel_origin
        self._channel_component = channel_component
        self._channel_color = None
        self._channel_view_id = None
        self.installed_only = installed_only
        self._channel_sort_mode = channel_sort_mode
        # distro specific stuff
        self.distro = softwarecenter.distro.get_distro()
        # configure the channel
        self._channel_display_name = self._get_display_name_for_channel(
            channel_name, channel_origin, channel_component)
        if channel_icon is None:
            self._channel_icon = self._get_icon_for_channel(
                channel_name, channel_origin, channel_component)
        else:
            self._channel_icon = channel_icon
        if channel_query is None:
            self._channel_query = self._get_channel_query_for_channel(
                channel_name, channel_origin, channel_component)
        else:
            self._channel_query = channel_query
        # a sources.list entry attached to the channel (this is currently
        # only used for not-yet-enabled channels)
        self._source_entry = source_entry
        # when the channel needs to be added to the systems sources.list
        self.needs_adding = False

    @property
    def name(self):
        """
        return the channel name as represented in the xapian database
        """
        return self._channel_name

    @property
    def origin(self):
        """
        return the channel origin as represented in the xapian database
        """
        return self._channel_origin

    @property
    def component(self):
        """
        return the channel component as represented in the xapian database
        """
        return self._channel_component

    @property
    def display_name(self):
        """
        return the display name for the corresponding channel for use in the UI
        """
        return self._channel_display_name

    @property
    def icon(self):
        """
        return the icon that corresponds to each channel based
        on the channel name, its origin string or its component
        """
        return self._channel_icon

    @property
    def query(self):
        """
        return the xapian query to be used with this software channel
        """
        return self._channel_query

    @property
    def sort_mode(self):
        """
        return the sort mode for this software channel
        """
        return self._channel_sort_mode

    # TODO:  implement __cmp__ so that sort for channels is encapsulated
    #        here as well

    def _get_display_name_for_channel(self, channel_name, channel_origin,
        channel_component):
        if channel_component == "partner":
            channel_display_name = _("Canonical Partners")
        elif not channel_origin:
            channel_display_name = _("Unknown")
        elif channel_origin == self.distro.get_distro_channel_name():
            channel_display_name = self.distro.get_distro_channel_description()
        elif channel_name == "For Purchase":
            channel_display_name = _("For Purchase")
        elif channel_name == "Previous Purchases":
            channel_display_name = _("Previous Purchases")
        elif channel_name == "Application Review Board PPA":
            channel_display_name = _("Independent")
        elif channel_name == "notdownloadable":
            channel_display_name = _("Other")
        else:
            return channel_name
        return channel_display_name

    def _get_icon_for_channel(self, channel_name, channel_origin,
        channel_component):
        if channel_component == "partner":
            channel_icon = "partner"
        elif not channel_name:
            channel_icon = "unknown-channel"
        elif channel_origin == self.distro.get_distro_channel_name():
            channel_icon = "distributor-logo"
        elif channel_name == "Application Review Board PPA":
            channel_icon = "system-users"
        elif channel_name == "For Purchase":
            channel_icon = "emblem-money"
        elif channel_origin and channel_origin.startswith("LP-PPA"):
            channel_icon = "ppa"
        elif channel_name == "notdownloadable":
            channel_icon = "application-default-icon"
        # TODO: add check for generic repository source (e.g., Google, Inc.)
        #       channel_icon = "generic-repository"
        else:
            channel_icon = "unknown-channel"
        return channel_icon

    def _get_channel_query_for_channel(self, channel_name, channel_origin,
        channel_component):

        if channel_component == "partner":
            q1 = xapian.Query("XOCpartner")
            q2 = xapian.Query("AH%s-partner" % self.distro.get_codename())
            channel_query = xapian.Query(xapian.Query.OP_OR, q1, q2)
        # show only apps when displaying the new apps archive
        elif channel_name == "Application Review Board PPA":
            channel_query = xapian.Query(xapian.Query.OP_AND,
                                         xapian.Query("XOL" + channel_name),
                                         xapian.Query("ATapplication"))
        elif channel_origin:
            channel_query = xapian.Query("XOO" + channel_origin)
        else:
            channel_query = xapian.Query("XOL" + channel_name)
        return channel_query

    def __str__(self):
        details = []
        details.append("* SoftwareChannel")
        details.append("  name: %s" % self.name)
        details.append("  origin: %s" % self.origin)
        details.append("  component: %s" % self.component)
        details.append("  display_name: %s" % self.display_name)
        details.append("  iconname: %s" % self.icon)
        details.append("  query: %s" % self.query)
        details.append("  sort_mode: %s" % self.sort_mode)
        details.append("  installed_only: %s" % self.installed_only)
        return unicode('\n'.join(details), 'utf8').encode('utf8')


class AllChannel(SoftwareChannel):

    def __init__(self, channel_name, installed_only):
        SoftwareChannel.__init__(
            self, channel_name, "all", None,
            installed_only=installed_only,
            channel_icon=Icons.FALLBACK)

    # overrides
    def _get_display_name_for_channel(self, channel_name, channel_origin,
        channel_component):
        return channel_name

    def _get_channel_query_for_channel(self, *args):
        pass


class AllAvailableChannel(AllChannel):

    def __init__(self):
        AllChannel.__init__(self, _("All Software"), False)


class AllInstalledChannel(AllChannel):

    def __init__(self):
        AllChannel.__init__(self, _("All Installed"), True)


# singleton
channels_manager = None


def get_channels_manager(db):
    global channels_manager
    if channels_manager is None:
        from softwarecenter.enums import USE_PACKAGEKIT_BACKEND
        if not USE_PACKAGEKIT_BACKEND:
            from softwarecenter.backend.channel_impl.aptchannels import (
                AptChannelsManager)
            channels_manager = AptChannelsManager(db)
        else:
            channels_manager = ChannelsManager(db)
    return channels_manager


def is_channel_available(channelname):
    from softwarecenter.backend.channel_impl.aptchannels import (
        AptChannelsManager)
    return AptChannelsManager.channel_available(channelname)

if __name__ == "__main__":
    distro = softwarecenter.distro.get_distro()
    channel = SoftwareChannel(distro.get_distro_channel_name(),
                              None, None)
    print(channel)
    channel = SoftwareChannel(distro.get_distro_channel_name(), None,
        "partner")
    print(channel)
