# This file is part of PRAW.
#
# PRAW 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, either version 3 of the License, or (at your option) any later
# version.
#
# PRAW 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
# PRAW.  If not, see <http://www.gnu.org/licenses/>.

"""
Contains code about objects such as Submissions, Redditors or Commments.

There are two main groups of objects in this file. The first are objects that
correspond to a Thing or part of a Thing as specified in reddit's API overview,
https://github.com/reddit/reddit/wiki/API. The second gives functionality that
extends over multiple Things. An object that extends from Saveable indicates
that it can be saved and unsaved in the context of a logged in user.
"""

from __future__ import print_function, unicode_literals
import six
from six.moves.urllib.parse import (  # pylint: disable=F0401
    parse_qs, urlparse, urlunparse)
from heapq import heappop, heappush
from json import dumps
from requests.compat import urljoin
from warnings import warn_explicit
from praw import (AuthenticatedReddit as AR, ModConfigMixin as MCMix,
                  ModFlairMixin as MFMix, ModLogMixin as MLMix,
                  ModOnlyMixin as MOMix, ModSelfMixin as MSMix,
                  MultiredditMixin as MultiMix, PrivateMessagesMixin as PMMix,
                  SubmitMixin, SubscribeMixin, UnauthenticatedReddit as UR)
from praw.decorators import (alias_function, limit_chars, restrict_access,
                             deprecated)
from praw.errors import ClientException, InvalidComment
from praw.internal import (_get_redditor_listing, _get_sorter,
                           _modify_relationship)


REDDITOR_KEYS = ('approved_by', 'author', 'banned_by', 'redditor',
                 'revision_by')


class RedditContentObject(object):

    """Base class that represents actual reddit objects."""

    @classmethod
    def from_api_response(cls, reddit_session, json_dict):
        """Return an instance of the appropriate class from the json_dict."""
        return cls(reddit_session, json_dict=json_dict)

    def __init__(self, reddit_session, json_dict=None, fetch=True,
                 info_url=None, underscore_names=None, uniq=None):
        """Create a new object from the dict of attributes returned by the API.

        The fetch parameter specifies whether to retrieve the object's
        information from the API (only matters when it isn't provided using
        json_dict).

        """
        self._info_url = info_url or reddit_session.config['info']
        self.reddit_session = reddit_session
        self._underscore_names = underscore_names
        self._uniq = uniq
        self._has_fetched = self._populate(json_dict, fetch)

    def __eq__(self, other):
        """Return whether the other instance equals the current."""
        return (isinstance(other, RedditContentObject) and
                self.fullname == other.fullname)

    def __getattr__(self, attr):
        """Return the value of the `attr` attribute."""
        if attr != '__setstate__' and not self._has_fetched:
            self._has_fetched = self._populate(None, True)
            return getattr(self, attr)
        raise AttributeError('\'%s\' has no attribute \'%s\'' % (type(self),
                                                                 attr))

    def __getstate__(self):
        """Needed for `pickle`.

        Without this, pickle protocol version 0 will make HTTP requests
        upon serialization, hence slowing it down significantly.
        """
        return self.__dict__

    def __ne__(self, other):
        """Return whether the other instance differs from the current."""
        return not self == other

    def __reduce_ex__(self, _):
        """Needed for `pickle`.

        Without this, `pickle` protocol version 2 will make HTTP requests
        upon serialization, hence slowing it down significantly.
        """
        return self.__reduce__()

    def __setattr__(self, name, value):
        """Set the `name` attribute to `value."""
        if value and name == 'subreddit':
            value = Subreddit(self.reddit_session, value, fetch=False)
        elif value and name in REDDITOR_KEYS:
            if isinstance(value, bool):
                pass
            elif isinstance(value, dict):
                value = Redditor(self.reddit_session, json
_dict=value['data'])
            elif not value or value == '[deleted]':
                value = None
            else:
                value = Redditor(self.reddit_session, value, fetch=False)
      
  object.__setattr__(self, name, value)

    def __str__(self):
        """Return a string representation of the RedditContentObject."""
        retval = self.__unicode__()
        if not six.PY3:
            retval = retval.encode('utf-8')
        return retval

    def _get_json_dict(self):
        # (disabled for entire function) pylint: disable=W0212

        # OAuth handling needs to be special cased here. For instance, the user
        # might be calling a method on a Subreddit object that requires first
        # loading the information about the subreddit. This method should try
        # to obtain the information in a scope-less manner unless either:
        # a) The object is a WikiPage and the reddit_session has the `wikiread`
        #    scope.
        # b) The object is not a WikiPage and the reddit_session has the
        #    `read` scope.
        prev_use_oauth = self.reddit_session._use_oauth
        self.reddit_session._use_oauth = (
            isinstance(self, WikiPage) and
            self.reddit_session.has_scope('wikiread')) or \
            (not isinstance(self, WikiPage) and
                self.reddit_session.has_scope('read'))
        try:
            params = {'uniq': self._uniq} if self._uniq else {}
            response = self.reddit_session.request_json(
                self._info_url, params=params, as_objects=False)
        finally:
            self.reddit_session._use_oauth = prev_use_oauth
        return response['data']

    def _populate(self, json_dict, fetch):
        if json_dict is None:
            json_dict = self._get_json_dict() if fetch else {}

        if self.reddit_session.config.store_json_result is True:
            self.json_dict = json_dict
        else:
            self.json_dict = None

        # TODO: Remove this wikipagelisting hack
        if isinstance(json_dict, list):
            json_dict = {'_tmp': json_dict}

        for name, value in six.iteritems(json_dict):
            if self._underscore_names and name in self._underscore_names:
                name = '_' + name
            setattr(self, name, value)

        self._post_populate(fetch)
        return bool(json_dict) or fetch

    def _post_populate(self, fetch):
        """Called after populating the attributes of the instance."""

    @property
    def fullname(self):
        """Return the object's fullname.

        A fullname is an object's kind mapping like `t3` followed by an
        underscore and the object's base36 id, e.g., `t1_c5s96e0`.

        """
        by_object = self.reddit_session.config.by_object
        return '%s_%s' % (by_object[self.__class__], self.id)

    @property
    @deprecated('``has_fetched`` will not be a public attribute in PRAW4.')
    def has_fetched(self):
        """Return whether the object has been fully fetched from reddit."""
        return self._has_fetched


class Moderatable(RedditContentObject):

    """Interface for Reddit content objects that have can be moderated."""

    @restrict_access(scope='modposts')
    def approve(self):
        """Approve object.

        This reverts a removal, resets the report counter, marks it with a
        green check mark (only visible to other moderators) on the website view
        and 