package XXV::MODULES::TELNET;

use strict;

use XXV::OUTPUT::Console;
use IO::Socket::INET;
use Module::Reload;
use Tools;
use Locale::gettext;

# This module method must exist for XXV
# ------------------
sub module {
# ------------------
    my $obj = shift || return error ('No Object!' );
    my $args = {
        Name => 'TELNET',
        Description => gettext('This module is a multisession telnet server.'),
        Version => '0.01',
        Date => '18.08.2004',
        Author => 'xpix',
        Preferences => {
            active => {
                description => gettext('Activate this service'),
                default     => 'y',
                type        => 'confirm',
                required    => gettext('This is required!'),
            },
            Clients => {
                description => gettext('Maximum number of allowed connections at a time'),
                default     => 5,
                type        => 'integer',
                required    => gettext('This is required!'),
            },
            Port => {
                description => gettext('Portnumber to connect the client'),
                default     => 8005,
                type        => 'integer',
                required    => gettext('This is required!'),
            },
        },
        Commands => {
            help => {
                description => gettext("This will display all commands or the helptext from the 'module name'"),
                short       => 'h',
                callback    => sub{
                    return $obj->usage(@_);
                },
            },
            quit => {
                description => gettext("This will exit the telnet session"),
                short       => 'q',
                callback    => sub{
                    my ($w, $c, $l) = @_;
                    warn   "[Server] Closed telnet session.\n";
                    $c->message(gettext("Closed session."));
                    $w->w->fd->close;
                    $w->w->cancel;
                },
            },
            bye => {
                description => gettext("This will exit the xxv system."),
                short       => 'x',
                callback    => sub{
                    my ($w, $c, $l) = @_;
        		    my $answer;
        		    my $questions = [
            		    'really' =>	{
                                    typ => 'confirm',
                                    msg => gettext("Are you sure to exit the XXV System?"),
                                    def => 'n'}
        		    ];
                    $answer = $c->question(gettext("This will exit the xxv system."),$questions,$answer);
                    if($answer->{really} eq 'y') {
                        $w->w->fd->close;
                        $w->w->cancel;
                        warn   "[Server] Closed session and exit.\n";
                        main::quit;
                    }
                },
        		Level   => 'admin'
            },
            reload => {
                description => gettext("This will reload all Modules."),
                short       => 'rel',
                callback    => sub{
                    my ($w, $c, $l) = @_;
                    $Module::Reload::Debug = 2;
                    Module::Reload->check;
                    $c->message(gettext("Modules reloaded."));
                },
		        Level   => 'admin'
            },
        },
    };
    return $args;
}

# ------------------
sub new {
# ------------------
	my($class, %attr) = @_;
	my $self = {};
	bless($self, $class);

    # paths
    $self->{paths} = delete $attr{'-paths'};

	# who am I
    $self->{MOD} = $self->module;

    # all configvalues to $self without parents (important for ConfigModule)
    map {
        $self->{$_} = $attr{'-config'}->{$self->{MOD}->{Name}}->{$_} || $self->{MOD}->{Preferences}->{$_}->{default}
    } keys %{$self->{MOD}->{Preferences}};

    # read the DB Handle
    $self->{dbh} = delete $attr{'-dbh'};

    # The Initprocess
    $self->init or return error('Problem to initialze Module');

	return $self;
}


# ------------------
sub init {
# ------------------
    my $obj = shift || return error ('No Object!' );

    # globals
    my $channels;

    my $prompt = "XXV> ";

    # make socket
	my $socket = IO::Socket::INET->new(
		Listen		=> $obj->{Clients} || 5,
		LocalPort	=> $obj->{Port}    || 8001,
		Reuse		=> 1);
	warn sprintf("[Fatal] Cannot build initial socket for %s on port %d. [%s]\n",
	        $obj->{Name}, $obj->{Port}, $!) unless $socket;

    # install an initial watcher
    Event->io(
        fd => $socket,
        prio => -1,  # -1 very hard ... 6 very low
        cb => sub {
                my $watch = shift;

                # make "channel" number
                my $channel=++$channels;

                # accept client
                my $client=$socket->accept;
                warn "[Error] Cannot connect telnet to new client.\n" and return unless $client;
                $client->autoflush;

                my $console = XXV::OUTPUT::Console->new(
                    -handle => $client,
                    -dbh    => $obj->{dbh},
                    -paths=> $obj->{paths},
                );

                # install a communicator
                Event->io(
                    fd => $client,
                    prio => -1,  # -1 very hard ... 6 very low
                    cb => sub {
                            my $watcher = shift;

                            # read new line and report it
                            my $handle=$watcher->w->fd;

                            my $line=<$handle>;
                            unless($line) {
                                $watcher->w->cancel;
                                $handle->close();
                                undef $watcher;
                                return 1;
                            }
                            $line =~ s/[\r|\n]//sig
                                if(defined $line);

                            $obj->handleInput($watcher, $console, $line);
                            # Prompt
                            $client->print($prompt) if($client->opened);
                        },
                );

                # welcome
                $client->print(sprintf(gettext("Welcome to XXV System Ver.: %s.\nThis is session %s.\n"),$obj->{MOD}->{Version},$channel));

                my $userMod = main::getModule('USER');
                unless(exists $console->{USER} or $userMod->{active} ne 'y') {
                    # Login
                    my $data;
                    if($userMod->checkIp($client)) {
                        $console->message(gettext("Welcome to xxv System."));
                        $data->{Name} = 'no';
                        $data->{Password} = 'no';
                    } else {
                        $data = $console->login(gettext("Welcome to xxv System. Please Login:"));
                    }
                    my $user = $userMod->check($client, $data->{Name}, $data->{Password});
                    if(exists $user->{Name}) {
                        $console->{USER} = $user;
                    } else {
                        $console->message(gettext("Sorry, but permission denied!"));
                        $client->close;
                        return 1;
                    }
                }

                $client->print($prompt);

            },
    ) if($obj->{active} eq 'y');

    return 1;

}

# ------------------
sub usage {
# ------------------
    my $obj = shift || return error ('No Object!' );
    my $watcher = shift || return error ('No Watcher!');
    my $console = shift || return error ('No Console');
    my $modulename = shift || 0;
    my $hint = shift || '';
    my $user = shift || $console->{USER};

    my $u = main::getModule('USER');
    unless($user) {
        my $loginObj = $obj;
        $loginObj = main::getModule('HTTPD')
                if ($console->{TYP} eq 'HTML') ;
        $loginObj = main::getModule('WAPD')
                if ($console->{TYP} eq 'WML') ;
        $user = $loginObj->{USER};
    }

    my $ret;
    push(@$ret, sprintf(gettext("%sThis is the XXV-%s-Server.\nPlease use the following commands:\n"),
        ($hint ? "$hint\n\n" : ''), $console->typ));

    my $mods = main::getModules();
    my @realModName;

    # Search for command and display the Description
    foreach my $modName (sort keys %{$mods}) {
        my $modCfg = $mods->{$modName}->{MOD};
        push(@realModName, $mods->{$modName}->{MOD}->{Name});
        next if($modulename and uc($modulename) ne $modCfg->{Name});
        foreach my $cmdName (sort keys %{$modCfg->{Commands}}) {
            push(@$ret,
                [
                    (split('::', $modName))[-1],
                    $modCfg->{Commands}->{$cmdName}->{short},
                    $cmdName,
                    $modCfg->{Commands}->{$cmdName}->{description},
                ]
            ) if(! $modCfg->{Commands}->{$cmdName}->{hidden} and ($u->{active} ne 'y') || $u->allowCommand($modCfg, $cmdName, $user, "1"));
        }
    }

    $console->menu(
        $ret,
        {
            periods  => $mods->{'XXV::MODULES::EPG'}->{periods},
            CHANNELS => $mods->{'XXV::MODULES::CHANNELS'}->ChannelArray('Name'),
            CONFIGS  => [ sort @realModName ],
        },
    );
}

# ------------------
sub handleInput {
# ------------------
    my $obj     = shift || return error ('No Object!' );
    my $watcher = shift || return error ('No Watcher!');
    my $console = shift || return error ('No Console');
    my $line    = shift || return;
    my $user    = shift || $console->{USER};

    my ($ucmd, $udata) = ($1, $2) if($line =~ /(\S+)\s*(.*)/sig);

    # Test the command on exists, permissions and so on
    my $u = main::getModule('USER');
    my ($cmdobj, $cmdname, $shorterr, $err) = $u->checkCommand($console, $ucmd);
    $console->{call} = $cmdname;
    if($cmdobj and not $shorterr) {
        $cmdobj->{callback}($watcher, $console, $udata);
    } elsif($shorterr eq 'noperm' or $shorterr eq 'noactive') {
        return $console->err($err);
    } else {
        return $obj->usage($watcher, $console, undef, $err);
    }
}

1;
