package DJabberd::Subscription;
use strict;
use Carp qw(croak);
use DJabberd::Util qw(exml);
use overload
    '""' => \&as_string;

use fields (
            'to',        # all bools
            'from',
            'pendin',
            'pendout',
            );

# The nine valid subscription states from the spec:
#                         TO  FROM  PIN  POUT
#   None                  -   -     -    -
#   None+PendOut          -   -     -    X
#   None+PendIn           -   -     X    -
#   None+PendIn+PendOut   -   -     X    X
#   To                    X   -     -    -
#   To+PendIn             X   -     X    -
#   From                  -   X     -    -
#   From+PendOut          -   X     -    X
#   Both                  X   X     -    -

# Invalid conditions:
#    TO && PendOut
#    From && PendIn


sub new {
    my $self = shift;
    $self = fields::new($self) unless ref $self;
    return $self;
}

sub none {
    my $class = shift;
    return $class->new;
}

sub new_from_name {
    my ($class, $name) = @_;
    my $sb = {
        'none' => sub { $class->none },
        # ...
    }->{$name};
    croak "Unknown subscription state name '$name'" unless $sb;
    return $sb->();
}

sub is_none_pending_in {
    my $self = shift;
    return $self->{pendin} &&
        ! $self->{pendout} &&
        ! $self->{to} &&
        ! $self->{from};
}

sub pending_out {
    my $self = shift;
    return $self->{pendout};
}

sub pending_in {
    my $self = shift;
    return $self->{pendin};
}

sub sub_from {
    my $self = shift;
    return $self->{from};
}

sub sub_to {
    my $self = shift;
    return $self->{to};
}

sub set_from {
    my ($self, $val) = @_;
    $val = 1 unless defined $val;
    $self->{from} = $val;
    return $self;
}

sub set_to {
    my ($self, $val) = @_;
    $val = 1 unless defined $val;
    $self->{to} = $val;
    return $self;
}

sub set_pending_out {
    my ($self, $val) = @_;
    $val = 1 unless defined $val;
    $self->{pendout} = $val;
    return $self;
}

sub set_pending_in {
    my ($self, $val) = @_;
    $val = 1 unless defined $val;
    $self->{pendin} = $val;
    return $self;
}

sub got_inbound_subscribed {
    my $self = shift;
    $self->{to}      = 1;
    $self->{pendout} = 0;
}

sub got_outbound_unsubscribe {
    my $self = shift;
    $self->{to}      = 0;
    $self->{pendout} = 0;
}

sub got_outbound_unsubscribed {
    my $self = shift;
    $self->{pendin}  = 0;
    $self->{from}    = 0;
}

sub got_inbound_unsubscribe {
    my $self = shift;
    $self->{from}    = 0;
    $self->{pendin}  = 0;
}

sub got_outbound_subscribed {
    my $self = shift;
    $self->{from}    = 1;
    $self->{pendin}  = 0;
}

# returns 1 if any action was taken, 0 if it was no-op
sub got_outbound_subscribe {
    my $self = shift;
    # the no-op case
    return 0 if $self->{to} && ! $self->{pendout};

    # for some reason, user's pendout bit is set even though
    # it shouldn't be.  fix up.
    if ($self->{to} && $self->{pendout}) {
        $self->{pendout} = 0;
        return 1;
    }

    $self->{pendout} = 1;
    return 1;
}

sub as_bitmask {
    my $self = shift;
    my $ret = 0;
    $ret = $ret | 1   if $self->{to};
    $ret = $ret | 2   if $self->{from};
    $ret = $ret | 4   if $self->{pendin};
    $ret = $ret | 8   if $self->{pendout};
    return $ret;
}

sub from_bitmask {
    my ($class, $mask) = @_;
    $mask += 0;  # force to numeric context
    my $new = $class->new;
    $new->{to}      = 1 if $mask & 1;
    $new->{from}    = 1 if $mask & 2;
    $new->{pendin}  = 1 if $mask & 4;
    $new->{pendout} = 1 if $mask & 8;
    return $new;
}

sub as_string {
    my $self = shift;
    my @fields = grep { $self->{$_} } qw(to from pendin pendout);
    return "[@fields]";
}

sub as_attributes {
    my $self = shift;
    my $state;
    if ($self->{to} && $self->{from}) {
        $state = "both";
    } elsif ($self->{to}) {
        $state = "to";
    } elsif ($self->{from}) {
        $state = "from";
    } else {
        $state = "none";
    }

    my $ret = "subscription='$state'";
    if ($self->{pendout}) {
        $ret .= " ask='subscribe'";
    }
    return $ret;
}

1;



