/* 
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
#include "turn.h"
#include "auth.h"

#define REALM           "pjsip.org"
//#define TURN_PORT     PJ_STUN_TURN_PORT
#define TURN_PORT       34780
#define LOG_LEVEL       4


static pj_caching_pool g_cp;

int err(const char *title, pj_status_t status)
{
    char errmsg[PJ_ERR_MSG_SIZE];
    pj_strerror(status, errmsg, sizeof(errmsg));

    printf("%s: %s\n", title, errmsg);
    return 1;
}

static void dump_status(pj_turn_srv *srv)
{
    char addr[80];
    pj_hash_iterator_t itbuf, *it;
    pj_time_val now;
    unsigned i;

    for (i=0; i<srv->core.lis_cnt; ++i) {
        pj_turn_listener *lis = srv->core.listener[i];
        printf("Server address : %s\n", lis->info);
    }

    printf("Worker threads : %d\n", srv->core.thread_cnt);
    /* Field used_size is deprecated by #3897 */
    //printf("Total mem usage: %u.%03uMB\n", (unsigned)(g_cp.used_size / 1000000),
    //       (unsigned)((g_cp.used_size % 1000000)/1000));
    printf("UDP port range : %u %u %u (next/min/max)\n", srv->ports.next_udp,
           srv->ports.min_udp, srv->ports.max_udp);
    printf("TCP port range : %u %u %u (next/min/max)\n", srv->ports.next_tcp,
           srv->ports.min_tcp, srv->ports.max_tcp);
    printf("Clients #      : %u\n", pj_hash_count(srv->tables.alloc));

    puts("");

    if (pj_hash_count(srv->tables.alloc)==0) {
        return;
    }

    puts("#    Client addr.          Alloc addr.            Username Lftm Expy #prm #chl");
    puts("------------------------------------------------------------------------------");

    pj_gettimeofday(&now);

    it = pj_hash_first(srv->tables.alloc, &itbuf);
    i=1;
    while (it) {
        pj_turn_allocation *alloc = (pj_turn_allocation*) 
                                    pj_hash_this(srv->tables.alloc, it);
        printf("%-3d %-22s %-22s %-8.*s %-4d %-4ld %-4d %-4d\n",
               i,
               alloc->info,
               pj_sockaddr_print(&alloc->relay.hkey.addr, addr, sizeof(addr), 3),
               (int)alloc->cred.data.static_cred.username.slen,
               alloc->cred.data.static_cred.username.ptr,
               alloc->relay.lifetime,
               alloc->relay.expiry.sec - now.sec,
               pj_hash_count(alloc->peer_table), 
               pj_hash_count(alloc->ch_table));

        it = pj_hash_next(srv->tables.alloc, it);
        ++i;
    }
}

static void menu(void)
{
    puts("");
    puts("Menu:");
    puts(" d   Dump status");
    puts(" q   Quit");
    printf(">> ");
}

static void console_main(pj_turn_srv *srv)
{
    pj_bool_t quit = PJ_FALSE;

    while (!quit) {
        char line[10];
        
        menu();
            
        if (fgets(line, sizeof(line), stdin) == NULL)
            break;

        switch (line[0]) {
        case 'd':
            dump_status(srv);
            break;
        case 'q':
            quit = PJ_TRUE;
            break;
        }
    }
}

int main()
{
    pj_turn_srv *srv;
    pj_turn_listener *listener;
    pj_status_t status;

    status = pj_init();
    if (status != PJ_SUCCESS)
        return err("pj_init() error", status);

    status = pjlib_util_init();
    if (status != PJ_SUCCESS)
        return err("pjlib_util_init() error", status);

    status = pjnath_init();
    if (status != PJ_SUCCESS)
        return err("pjnath_init() error", status);

    pj_caching_pool_init(&g_cp, NULL, 0);

    pj_turn_auth_init(REALM);

    status = pj_turn_srv_create(&g_cp.factory, &srv);
    if (status != PJ_SUCCESS)
        return err("Error creating server", status);

    status = pj_turn_listener_create_udp(srv, pj_AF_INET(), NULL, 
                                         TURN_PORT, 1, 0, &listener);
    if (status != PJ_SUCCESS)
        return err("Error creating UDP listener", status);

#if PJ_HAS_TCP
    status = pj_turn_listener_create_tcp(srv, pj_AF_INET(), NULL, 
                                         TURN_PORT, 1, 0, &listener);
    if (status != PJ_SUCCESS)
        return err("Error creating listener", status);
#endif

    status = pj_turn_srv_add_listener(srv, listener);
    if (status != PJ_SUCCESS)
        return err("Error adding listener", status);

    puts("Server is running");

    pj_log_set_level(LOG_LEVEL);

    console_main(srv);

    pj_turn_srv_destroy(srv);
    pj_caching_pool_destroy(&g_cp);
    pj_shutdown();

    return 0;
}

