package Twitch;
use Moose;
use POE qw(Component::IRC Kernel Session);
use Twitch::Channel;
use Twitch::User;
use Twitch::Chat;

has channel => (
   is      => 'rw', 
   isa     => 'Twitch::Channel',
   default => sub { Twitch::Channel->new; },
);

has user => (
   is      => 'rw', 
   isa     => 'Twitch::User',
   default => sub { Twitch::User->new; },
);

has chat => (
   is      => 'rw', 
   isa     => 'Twitch::Chat',
   default => sub { Twitch::Chat->new; },
);

has irc => (
   is      => 'rw',
   isa     => 'POE::Component::IRC',
   default => sub { POE::Component::IRC->spawn; },
);

has server => (
   is  => 'ro',
   isa => 'Str',
   default => 'irc.twitch.tv',
);

has port => (
   is  => 'ro',
   isa => 'Int',
   default => 6667,
);

sub init {
   my $self = shift;
   my ($user, $oauth, $channel) = @_;
   $self->channel->name($channel);
   $self->channel->fixup;
   $self->user->name($user);
   $self->user->oauth($oauth);

   # Startup POE session
   POE::Session->create(
      inline_states => {
         _start     => sub { $self->irc_start;   },
         irc_001    => sub { $self->irc_connect; },
         irc_public => sub { $self->irc_listen(@_[KERNEL, ARG0, ARG1, ARG2]); },
      },
      heap => { irc => $self->irc },
   );
}

sub start { POE::Kernel->run(); }

#---------------------
#-- Event listeners --
#---------------------
sub irc_start {
   my $self = shift;
   $self->irc->yield(register => "all");
   $self->irc->yield(
      connect => {
         Nick     => $self->user->stringify,
         Password => $self->user->oauth,
         Port     => $self->port,
         Server   => $self->server,
      }
   );
}

sub irc_connect {
  my $self = shift;
  $self->irc->yield(join => $self->channel->stringify);
}

sub irc_listen {
   my $self = shift;
   my ($kernel, $who, $where, $msg) = @_;
   my $nick    = (split /!/, $who)[0];
   my $channel = $where->[0];
   my $ts      = scalar localtime;

   # Parse the special command character '!'
   if ($msg =~ /^!(.+)/) {
      if (exists $self->chat->commands->{$1}) {
         print "command [" . $1 . "] exists.. "
             . "sending response " . $self->chat->commands->{$1}
             . "to channel " . $self->channel->stringify;

         # Send a response back to the server.
         my $resp = $self->chat->commands->{$1};
         $kernel->post( $self->irc->session_id() => privmsg => $channel => $resp );

      }
      else {
         print "!MyCmd [$ts] <$nick:$channel> $msg\n";
      }
   }  
   else {
      printf "!Handled [$ts] <%20s : %20s> %-20s\n", $nick, $channel, $msg;
   } 
}

1;

=head1 Twitch

Twitch - A module for creating Twitch chat applications.

=head1 SYNOPSIS

   A module/object to easily implement Twitch chat applications through the 
   use of OOP (Moose), and event-driven programming (POE::Component::IRC).
   Ex: A simple script for printing all chat messages to the terminal.
      
      use Twitch;
      my $twitch = Twitch->new;
      $twitch->init($nickname, $password, $channel);
      $twitch->start;

=head1 DESCRIPTION

   A module to kickstart Twitch chat applications.

=head2 Methods

=over 12

=item C<new>

   Returns a new Twitch object.

=item C<init>

   Takes three parameters (in this order):
   1) Twitch name - Str - Required
   2) Oauth       - Str - Required
   3) Channel     - Str - Required

   Ex: Logging in as 'Steel' with fake oauth of '11091jr309j', and connecting
       to 'summit2g'.
      
      use Twitch;
      my $twitch = Twitch->new;
      $twitch->init('Steel', '11091jr309j', 'summit2g');
      $twitch->start;

=item C<start>

   Calls POE::Kernel->run(), which will start all twitch irc instances 
   created in the application in which it was called.

=item C<irc_start>

   Not intended to be called on the object. 
   This method is called by POE::Session.

=item C<irc_connect>

   Not intended to be called on the object. 
   This method is called by POE::Session.

=item C<irc_listen>

   Not intended to be called on the object. 
   This method is called by POE::Session.
   This method parses the connected chat looking for '!' followed by a command.
   Ex:
      !hello
      !timeofday
      !uptime
   When a command is parsed, it is compared to the _commands hash in 
   Twitch::Chat. Thus, to add a command you'll append the !command and 
   subroutine instructions there.

=back

=head1 AUTHOR

   Steel - <http://www.github.com/shionhart/>

=head1 SEE ALSO

L<POE::Component::IRC>, 
L<Moose>, 
L<Twitch::Chat>, 
L<Twitch::User>, 
L<Twitch::Channel>

=cut