#!/usr/bin/env perl
use strict;
use warnings;
use 5.008001;
use File::Spec;
use File::Basename;
use lib File::Spec->catdir(dirname(__FILE__), '..', 'lib');

use App::Tacochan;
use Skype::Any;
use Encode ();
use JSON ();
use Getopt::Long ();
use Pod::Usage;
use Plack::Builder;
use Plack::Builder::Conditionals;
use Plack::Request;
use Plack::Response;
use Twiggy::Server;

my $parser = Getopt::Long::Parser->new(
    config => [ "no_ignore_case", "pass_through" ],
);

my %options;
my($http_host, $http_port) = ('127.0.0.1', 4969);
my @reverse_proxy;
$parser->getoptions(
    'o|host=s'  => \$http_host,
    'p|port=i'  => \$http_port,
    'r|reverse-proxy=s' => \@reverse_proxy,
    'h|help'    => \$options{help},
    'v|version' => \$options{version},
);
pod2usage(1) if $options{help};
if ($options{version}) {
    die "tacochan $App::Tacochan::VERSION\n";
}

my $skype = Skype::Any->new(name => 'tacochan');
my $json = JSON->new->utf8->pretty;

sub render {
    my($code, $msg) = @_;
    $msg = Encode::encode_utf8($msg);
    my $res = Plack::Response->new($code);
    $res->content_type('text/plain; charset=utf-8');
    $res->content_length(length $msg);
    $res->body($msg);
    $res->finalize;
}

my $recent_chats = {};

my $code = sub {
    my $req = Plack::Request->new(shift);
    my $method = $req->method;
    my $path = $req->path;

    if ($method eq 'GET') {
        if ($path eq '/chat_list' || $path eq '/channel_list') {
            my $reply = $skype->api->send_command('SEARCH RECENTCHATS')->reply;
            $reply =~ s/^CHATS\s+//;
            for my $chatname (split /,\s+/, $reply) {
                unless (exists $recent_chats->{$chatname}) {
                    my $chat = $skype->chat($chatname);
                    my $topic = $chat->topic;
                    $recent_chats->{$chatname} = {
                        status  => $chat->status,
                        members => [split /\s+/, $chat->members],
                        $topic ? (topic => $chat->topic) : (),
                    };
                }
            }
            my $json_text = $json->encode($recent_chats);
            my $res = Plack::Response->new(200);
            $res->content_type('application/json; charset=utf-8');
            $res->content_length(length $json_text);
            $res->body($json_text);
            return $res->finalize;
        } elsif ($path eq '/') {
            my $base = $req->base;
            my $html =<<HTML;
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>tacochan</title>
    </head>
    <body>
        <h1>tacochan</h1>

        <h2>recent chats</h2>
        <table border="1">
            <tr><td rowspan="5">status</td><td colspan="2">chat status</td></tr>
            <tr><td>LEGACY_DIALOG</td><td>old style IM</td></tr>
            <tr><td>DIALOG</td><td>1:1 chat</td></tr>
            <tr><td>MULTI_SUBSCRIBED</td><td>participant in chat</td></tr>
            <tr><td>UNSUBSCRIBED</td><td>left chat</td></tr>
            <tr><td>members</td><td colspan="2">all users who have been these</td></tr>
            <tr><td>topic</td><td colspan="2">chat topic</td></tr>
        </table>
        <iframe src="/chat_list" width="500"></iframe>

        <h2>API usage</h2>

        <section>
            <h3>chat leave</h3>
            <table border="1">
                <tr><td>method</td><td>POST</td></tr>
                <tr><td>url</td><td>${base}leave</td></tr>
                <tr><td>form params</td><td>chat=#chat</td></tr>
            </table>
            <form action="/leave" method="post">
                leave chat: <input name="chat" /><input type="submit" value="leave" />
            </form>
        <section>


        <section>
            <h3>sent message to chat</h3>
            <table border="1">
                <tr><td>method</td><td>POST</td></tr>
                <tr><td>url</td><td>${base}send</td></tr>
                <tr><td>form params</td><td>chat=#chat|user[, user, ...]&amp;message=your_message</td></tr>
            </table>
            <form action="/send" method="post">
                chat: <input name="chat" /><br />
                message: <input name="message" /><input type="submit" value="post" />
            </form>
        </section>
    </body>
</html>
HTML

            my $res = Plack::Response->new(200);
            $res->content_type('text/html; charset=utf-8');
            $res->content_length(length $html);
            $res->body($html);
            return $res->finalize;
        }
    } elsif ($method eq 'POST') {
        my $chatname = $req->param('chat') || $req->param('channel');
        if ($path eq '/join') {
            eval {
                $skype->chat($chatname)->alter('join');
            };
            if ($@) {
                return render(403, "join failure chat: $chatname");
            }
            return render(200, "join success chat: $chatname");
        } elsif ($path eq '/leave' || $path eq '/part') {
            eval {
                $skype->chat($chatname)->alter('leave');
            };
            if ($@) {
                return render(403, "leave failure chat: $chatname");
            }
            delete $recent_chats->{$chatname};
            return render(200, "leave success chat: $chatname");
        } elsif ($path eq '/send' || $path eq '/notice' || $path eq '/privmsg') {
            my $chat = do {
                if ($chatname =~ /^#/) {
                    $skype->chat($chatname);
                } else {
                    $skype->user($chatname)->chat();
                }
            };
            my $message = Encode::decode_utf8($req->param('message'));
            eval {
                $chat->send_message($message);
            };
            if ($@) {
                return render(403, "message sent failure chat: $chatname $message");
            }
            return render(200, "message sent chat: $chat->{id} $message");
        }
    }

    return render(404, 'not found');
};

my $app = builder {
    if ( @reverse_proxy ) {
        enable match_if addr(\@reverse_proxy), 'Plack::Middleware::ReverseProxy';
    }
    enable 'Plack::Middleware::AccessLog', format => 'combined';
    $code;
};

warn "starting httpd: http://$http_host:$http_port/";
my $twiggy = Twiggy::Server->new(
    host => $http_host,
    port => $http_port,
);
$twiggy->register_service($app);

$skype->run;

__END__

=head1 NAME

tacochan - Skype message delivery by HTTP

=head1 SYNOPSIS

    % tacochan

=head1 OPTIONS

=over 4

=item -o, --host

The interface a TCP based server daemon binds to. Defauts to undef,
which lets most server backends bind the any (*) interface. This
option doesn't mean anything if the server does not support TCP
socket.

=item -p, --port (default: 4969)

The port number a TCP based server daemon listens on. Defaults to
4969. This option doesn't mean anything if the server does not support
TCP socket.

=item -r, --reverse-proxy

treat X-Forwarded-For as REMOTE_ADDR if REMOTE_ADDR match this argument.

see L<Plack::Middleware::ReverseProxy>.

=item -h, --help

Show help for this command.

=item -v, --version

Show version.

=back

=cut
