# This code was forked from the LiveJournal project owned and operated
# by Live Journal, Inc. The code has been modified and expanded by
# Dreamwidth Studios, LLC. These files were originally licensed under
# the terms of the license supplied by Live Journal, Inc, which can
# currently be found at:
#
# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
#
# In accordance with the original license, this code and all its
# modifications are provided under the GNU General Public License.
# A copy of that license can be found in the LICENSE file included as
# part of this distribution.

# This is a class representing a notification that came out of an
# LJ::NotificationInbox. You can tell it to mark itself as
# read/unread, delete it, and get the event that it contains out of
# it.
# Mischa Spiegelmock, 05/2006

package LJ::NotificationItem;
use strict;
use warnings;
no warnings "redefine";

use LJ::NotificationInbox;
use LJ::Event;
use Carp qw(croak);

*new = \&instance;

# parameters: user, notification inbox id
sub instance {
    my ( $class, $u, $qid ) = @_;

    my $singletonkey = $qid;

    $u->{_inbox_items} ||= {};
    return $u->{_inbox_items}->{$singletonkey} if $u->{_inbox_items}->{$singletonkey};

    my $self = {
        userid  => $u->id,
        qid     => $qid,
        state   => undef,
        event   => undef,
        when    => undef,
        _loaded => 0,
    };

    $u->{_inbox_items}->{$singletonkey} = $self;

    return bless $self, $class;
}

# returns whose notification this is
*u = \&owner;
sub owner { LJ::load_userid( $_[0]->{userid} ) }

# returns this item's id in the notification queue
sub qid { $_[0]->{qid} }

# returns true if this item really exists
sub valid {
    my $self = shift;

    return undef unless $self->u && $self->qid;
    $self->_load unless $self->{_loaded};

    return $self->event;
}

# returns title of this item
sub title {
    my $self = shift;
    return "(Invalid event)" unless $self->event;

    my %opts = @_;
    my $mode = delete $opts{mode};
    croak "Too many args passed to NotificationItem->as_html" if %opts;

    $mode = "html" unless $mode;

    if ( $mode eq "html" ) {
        return eval { $self->event->as_html( $self->u ) } || $@;
    }
}

# returns contents of this item for user u
sub as_html {
    my $self = shift;
    croak "Too many args passed to NotificationItem->as_html" if scalar @_;
    return "(Invalid event)" unless $self->event;
    return eval { $self->event->content( $self->u ) } || $@;
}

sub as_html_summary {
    my $self = shift;
    croak "Too many args passed to NotificationItem->as_html_summary" if scalar @_;
    return "(Invalid event)" unless $self->event;
    return eval { $self->event->content_summary( $self->u ) } || $@;
}

# returns the event that this item refers to
sub event {
    my $self = shift;

    $self->_load unless $self->{_loaded};

    return $self->{event};
}

# loads this item
sub _load {
    my $self = shift;

    my $qid = $self->qid;
    my $u   = $self->owner;

    return if $self->{_loaded};

    # load info for all the currently instantiated singletons
    # get current singleton qids
    $u->{_inbox_items} ||= {};
    my @qids = map { $_->qid } values %{ $u->{_inbox_items} };

    my $bind = join( ',', map { '?' } @qids );

    my $sth = $u->prepare( "SELECT userid, qid, journalid, etypeid, arg1, arg2, state, createtime "
            . "FROM notifyqueue WHERE userid=? AND qid IN ($bind)" );
    $sth->execute( $u->id, @qids );
    die $sth->errstr if $sth->err;

    my @items;
    while ( my $row = $sth->fetchrow_hashref ) {
        my $qid = $row->{qid} or next;
        my $singleton = $u->{_inbox_items}->{$qid} or next;

        push @items, $singleton->absorb_row($row);
    }
}

# fills in a skeleton item from a database row hashref
sub absorb_row {
    my ( $self, $row ) = @_;

    $self->{_loaded} = 1;

    $self->{state} = $row->{state};
    $self->{when}  = $row->{createtime};

    my $evt = LJ::Event->new_from_raw_params( $row->{etypeid}, $row->{journalid}, $row->{arg1},
        $row->{arg2} );
    $self->{event} = $evt;

    return $self;
}

# returns when this event happened (or got put in the inbox)
sub when_unixtime {
    my $self = shift;

    $self->_load unless $self->{_loaded};

    return $self->{when};
}

# returns the state of this item
sub _state {
    my $self = shift;

    $self->_load unless $self->{_loaded};

    return $self->{state};
}

# returns if this event is marked as read
sub read {
    return 0 unless defined $_[0]->_state;
    return $_[0]->_state eq 'R';
}

# returns if this event is marked as unread
sub unread {
    return 0 unless defined $_[0]->_state;
    return $_[0]->_state eq 'N';
}

# delete this item from its inbox
sub delete {
    my $self = shift;
    return unless $self->owner;
    my $inbox = $self->owner->notification_inbox;

    # delete from the inbox so the inbox stays in sync
    my $ret = $inbox->delete_from_queue($self);
    %$self = ();
    return $ret;
}

# mark this item as read
sub mark_read {
    my $self = shift;

    # do nothing if it's already marked as read
    return if $self->read;
    $self->_set_state('R');
}

# mark this item as read
sub mark_unread {
    my $self = shift;

    # do nothing if it's already marked as unread
    return if $self->unread;
    $self->_set_state('N');
}

# sets the state of this item
sub _set_state {
    my ( $self, $state ) = @_;

    $self->owner->do( "UPDATE notifyqueue SET state=? WHERE userid=? AND qid=?",
        undef, $state, $self->owner->id, $self->qid )
        or die $self->owner->errstr;
    $self->{state} = $state;

    # expire unread cache
    my $userid = $self->u->id;
    my $memkey = [ $userid, "inbox:newct:${userid}" ];
    LJ::MemCache::delete($memkey);
}

# JSON output implementation for NotificationItem objects
sub TO_JSON {
    my $self = shift;
    my $json = {
        title     => $self->title,
        content   => $self->as_html,
        unread    => $self->unread,
        timestamp => $self->when_unixtime

    };
}
