package XXV::MODULES::AUTOTIMER;

use strict;

use Tools;
use Locale::gettext;


# ------------------
# Name:  module
# Descr: The standard routine to describe the Plugin
# Usage: my $modhash = $obj->module();
# ------------------
sub module {
    my $obj = shift || return error ('No Object!' );
    my $args = {
        Name => 'AUTOTIMER',
        Prereq => {
            # 'Perl::Module' => 'Description',
        },
        Description => gettext('This module search for epg entries with an user defined text and create new timer.'),
        Version => '0.45',
        Date => '12.09.2005',
        Author => 'xpix',
        Status => sub{ $obj->status(@_) },
        Preferences => {
            active => {
                description => gettext('Activate this service'),
                default     => 'y',
                type        => 'confirm',
                required    => gettext('This is required!'),
            },
            interval => {
                description => gettext('The interval in seconds to search for timers'),
                default     => 300,
                type        => 'integer',
                required    => gettext('This is required!'),
            },
            exclude => {
                description => gettext('Exclude channel list for autotimer.'),
                type        => 'string',
                default     => 'POS > 50',
                check   => sub{
                    my $value = shift;
                    if(index($value, ',') != -1) {
                        return 'POS > 50'; # Nur um sicher zu sein, das die alten Werte nicht übernommen werden.
                    } else {
                        return $value;
                    }
                },
            },
        },
        Commands => {
            astatus => {
                description => gettext('Status from autotimers'),
                short       => 'as',
                callback    => sub{ $obj->status(@_) },
                DenyClass   => 'alist',
            },
            anew => {
                description => gettext("Create a new autotimer"),
                short       => 'an',
                callback    => sub{ $obj->autotimerCreate(@_) },
                Level       => 'user',
                DenyClass   => 'aedit',
            },
            adelete => {
                description => gettext("Delete a autotimer 'aid'"),
                short       => 'ad',
                callback    => sub{ $obj->autotimerDelete(@_) },
                Level       => 'user',
                DenyClass   => 'aedit',
            },
            aedit => {
                description => gettext("Edit a autotimer 'aid'"),
                short       => 'ae',
                callback    => sub{ $obj->autotimerEdit(@_) },
                Level       => 'user',
                DenyClass   => 'aedit',
            },
            asearch => {
                description => gettext("Search for autotimer with text 'aid'"),
                short       => 'ase',
                callback    => sub{ $obj->list(@_) },
                DenyClass   => 'asearch',
            },
            alist => {
                description => gettext("List the autotimer 'aid'"),
                short       => 'al',
                callback    => sub{ $obj->list(@_) },
                DenyClass   => 'alist',
            },
            aupdate => {
                description => gettext("Start the autotimer process"),
                short       => 'au',
                callback    => sub{ $obj->autotimer(@_) },
                Level       => 'user',
                DenyClass   => 'aedit',
            },
            aimport => {
                description => gettext("Import autotimer from vdradmind.at"),
                short       => 'ai',
                callback    => sub{ $obj->autotimerimport(@_) },
                Level       => 'user',
                DenyClass   => 'aedit',
            },
            aexport => {
                description => gettext("Export autotimer to vdradmind.at"),
                short       => 'ax',
                callback    => sub{ $obj->autotimerexport(@_) },
                Level       => 'user',
                DenyClass   => 'aedit',
            },
            atoggle => {
                description => gettext("Toggle autotimer on or off 'aid'"),
                short       => 'at',
                callback    => sub{ $obj->autotimerToggle(@_) },
                Level       => 'user',
                DenyClass   => 'aedit',
            },
        },
        RegEvent    => {
            'newTimerfromAutotimer' => {
                Descr => gettext('Create event entries, if a new timer from autotimer created.'),

                # You have this choices (harmless is default):
                # 'harmless', 'intresting', 'veryintresting', 'important', 'veryimportant'
                Level => 'veryintresting',

                # Search for a spezial Event.
                # I.e.: Search for an LogEvent with match
                # "Sub=>text" = subroutine =~ /text/
                # "Msg=>text" = logmessage =~ /text/
                # "Mod=>text" = modname =~ /text/
                SearchForEvent => {
                    Sub => 'AUTOTIMER',
                    Msg => 'Save timer',
                },
                # Search for a Match and extract the information
                # of the TimerId
                # ...
                Match => {
                    TimerId => qr/TimerId\:\s+\"(\d+)\"/s,
                },
                Actions => [
                    q|sub{  my $args = shift;
                            my $event = shift;
                            my $timer  = getDataById($args->{TimerId}, 'TIMERS', 'Id');
                            my $autotimer = getDataById($timer->{AutotimerId}, 'AUTOTIMER', 'Id');
                            my $title = sprintf(gettext("Autotimer('%s') found: %s"),
                                                    $autotimer->{Search}, $timer->{File});
                            my $description = sprintf(gettext("Description: %s\nAt: %s to %s"),
                                $timer->{Summary},
                                $timer->{NextStartTime},
                                fmttime($timer->{Stop})
                                );

                            main::getModule('REPORT')->news($title, $description, "display", $timer->{NextEpgId}, "intresting");
                        }
                    |,
                ],

            },
        },
    };
    return $args;
}

# ------------------
# Name:  status
# Descr: Standardsubroutine to report statistical data for Report Plugin.
# Usage: my $report = $obj->status([$watcher, $console]);
# ------------------
sub status {
    my $obj = shift || return error ('No Object!' );
    my $watcher = shift;
    my $console = shift;
    my $lastReportTime = shift || 0;

    my $sql = qq|
SELECT
    t.Id as __Id,
    t.File,
    t.Status as __Status,
    c.Name as Channel,
    c.Pos as __Pos,
    DATE_FORMAT(t.Day, '%e.%c.%Y') as Day,
    t.Start,
    t.Stop,
    t.Priority,
    UNIX_TIMESTAMP(t.NextStartTime) as __Day,
    t.Collision as __Collision,
    t.NextEpgId as __NextEpgId,
    t.AutotimerId as __AutotimerId
FROM
    TIMERS as t,
    CHANNELS as c
WHERE
    t.ChannelID = c.Id
    and UNIX_TIMESTAMP(t.addtime) > $lastReportTime
    and t.AutotimerId > 0
ORDER BY
    t.NextStartTime|;

    my $fields = fields($obj->{dbh}, $sql);
    my $erg = $obj->{dbh}->selectall_arrayref($sql);
    for(@$erg) {
        $_->[6] = fmttime($_->[6]);
        $_->[7] = fmttime($_->[7]);
    }

    unshift(@$erg, $fields);
    return {
        message => sprintf(gettext('Autotimer has %d new timer programmed, since last report at %s'),
            (scalar @$erg - 1), scalar localtime($lastReportTime)),
        table   => $erg,
    };
}


# ------------------
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->{$_} = $self->{MOD}->{Preferences}->{$_}->{default} unless($self->{$_});
    } keys %{$self->{MOD}->{Preferences}};

    # Try to use the Requirments
    map {
        eval "use $_";
        return panic("\nCan not load Module: $_\nPlease install this module on your System:\nperl -MCPAN -e 'install $_'") if($@);
    } keys %{$self->{MOD}->{Prereq}};

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

    # file
    $self->{file} = $self->{config}->{file};

    # The Initprocess
    my $erg = $self->_init or return error('Problem to initialze Module');

    # Interval to read timers and put to DB
    Event->timer(
        interval => $self->{interval},
        prio => 6,  # -1 very hard ... 6 very low
        cb => sub{
            lg 'Start interval autotimer to find new events!';
            $self->autotimer();
        },
    ) if($self->{active} eq 'y');

	return $self;
}

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

    # Old Version
    if(tableExists($obj->{dbh}, 'AUTOTIMER')) {
        # Nicht loeschen da das Userdata sind
		my $fields = fields($obj->{dbh}, 'select * from AUTOTIMER');

        panic "Upps, you have a incompatible database. Please use the script contrib/upgrade-xxv.sh to convert the database!"
            unless(scalar @$fields == 16);
    }

    # Look for table or create this table
    my $version = main::getVersion;
    $obj->{dbh}->do(qq|
      CREATE TABLE IF NOT EXISTS AUTOTIMER (
          Id int(11) unsigned auto_increment NOT NULL,
          Activ enum('y', 'n') default 'y',
          Done  enum('y', 'n') default 'y',
          Search text NOT NULL default '',
          InFields set('title', 'subtitle', 'description' ) NOT NULL,
          Channels text default '',
          Start char(4) default '0000',
          Stop  char(4) default '0000',
          MinLength tinyint default '0',
          Priority tinyint(2),
          Lifetime tinyint(2),
          Dir text,
          VPS enum('y', 'n') default 'n',
          prevminutes tinyint,
          afterminutes tinyint,
          Weekdays set('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'),
          PRIMARY KEY  (Id)
        ) COMMENT = '$version'
    |);
}

# ------------------
# Name:  autotimer
# Descr: Routine to parse the EPG Data for users Autotimer.
#        If Autotimerid given, then will this search only
#        for this Autotimer else for all.
# Usage: $obj->autotimer([$autotimerid]);
# ------------------
sub autotimer {
    my $obj = shift || return error ('No Object!' );
    my $watcher = shift;
    my $console = shift;
    my $autotimerid = shift;

    # Get Autotimer
    my $sql = sprintf('select * from AUTOTIMER where Activ = "y" %s order by Id', ($autotimerid ? "AND Id = $autotimerid" : ''));
    my $att = $obj->{dbh}->selectall_hashref($sql, 'Id');

    my $waiter;
    if(ref $console && !$autotimerid && $console->typ eq 'HTML') {
        my $zaehler = scalar keys %$att;
        $waiter = $console->wait(gettext("Look for autotimer ..."), 0, ++$zaehler, 'no');
    }

    my $l = 0; # Lines for Waiter
    my $C = 0; # Total of created and modifed timers
    my $M = 0;
    my $log;

    # Get Timersmodule
    my $timermod = main::getModule('TIMERS');
    foreach my $id (sort keys %$att) {
        my $a = $att->{$id};

        $waiter->next(++$l, undef, sprintf(gettext("Look for autotimer with ID(%d) with search '%s'"), $id, $a->{Search}))
          if(ref $waiter);

        if(ref $console && $autotimerid) {
            $console->message(' ') if($console->{TYP} eq 'HTML');
            $console->message(sprintf(gettext("Look for autotimer with ID(%d) with search '%s'"), $id, $a->{Search}));
        }

        # Build SQL Command and run it ....
    	my $events = $obj->_eventsearch($a, $timermod) || next;

        # Only search for one at?
        if(ref $console && $autotimerid) {
            $console->message(sprintf(gettext("Found %d entries for '%s' in EPG DB."), scalar keys %$events, $a->{Search}));
            map {
                $console->table($events->{$_});
            } sort keys %{$events}
        }

		my $vdrVersion = main::getVdrVersion();
        # Every found and save this as timer
        my $c = 0;
        my $m = 0;
        foreach my $Id (sort keys %$events) {
            $events->{$Id}->{Activ} = 'y';
			$events->{$Id}->{VPS} = $events->{$Id}->{VpsStart} && $a->{VPS} eq 'y';
            $events->{$Id}->{Priority} = $a->{Priority};
            $events->{$Id}->{Lifetime} = $a->{Lifetime};

            $events->{$Id}->{File} = $obj->_placeholder($events->{$Id}, $a);

            my $nexttime = $timermod->getNextTime( $events->{$Id}->{Day} , $events->{$Id}->{Start},$events->{$Id}->{Stop} )
                  or error(sprintf("Can't get the nexttime for this autotimer: %d", $events->{$Id}->{eventid}));

            # Add anchor for reidentify timer
            my $aidcomment = sprintf('#~AT[%d]', $id);

			if($vdrVersion >= 10344){
    	        $events->{$Id}->{Summary} = $aidcomment;
			} else {
	            $events->{$Id}->{Summary} .= $aidcomment;
			}

            my $file = $events->{$Id}->{File};
            $file =~ s/\'/\\\'/sg;
            $file =~ s/%/\\%/sg;

            my $summary = $events->{$Id}->{Summary};
            $summary =~ s/\'/\\\'/sg;
            $summary =~ s/%/\\%/sg;

            if($events->{$Id}->{VPS}) {
 	            $events->{$Id}->{Start} = $events->{$Id}->{VpsStart};
 	            $events->{$Id}->{Stop} = $events->{$Id}->{VpsStop};
            }

            my @parameters = ($events->{$Id}, $timermod, $nexttime, $aidcomment, $file, $summary);

            # Programmed timer exist with this Autotimerdata?
            next if($obj->_timerexists(@parameters));

            # Adjust timers set by the autotimer
            my $timerID = $obj->_timerexistsfuzzy(@parameters);

            if(!$timerID && $a->{Done} eq 'y') {
                # Ignore timer if it already with same title recorded
                next if($obj->_recordexists(@parameters));

                # Ignore timer if it already a timer with same title programmed, on other place
                next if($obj->_timerexiststitle(@parameters));
            }

            my $error = 0;
            my $erg = $timermod->saveTimer($events->{$Id}, $timerID ? $timerID : undef);
            foreach my $zeile (@$erg) {
                if($zeile =~ /^(\d{3})\s+(.+)/) {
                    $error = $2 if(int($1) >= 500);
                }
            }
            if($error) {
                $console->err(sprintf(gettext("Can't save timer for '%s' : %s"), $events->{$Id}->{File}, $error))
                    if(ref $console && $autotimerid);
            } else {
                if($timerID) {
                    ++$m;
                        $console->message(sprintf(gettext("Modify timer for '%s'."), $events->{$Id}->{File}))
                            if(ref $console && $autotimerid);
                } else {
                    ++$c;
                        $console->message(sprintf(gettext("Create timer for '%s'."), $events->{$Id}->{File}))
                            if(ref $console && $autotimerid);
                }
            }
        }
        $C += $c;
        $M += $m;
        if($c) {
            my $msg = sprintf(gettext("Create %d timer for '%s'."), $c, $a->{Search});
            if(ref $console && $autotimerid) {
                $console->message($msg);
            }
            else {
                push(@{$log},$msg);
            }
        }
        if($m) {
            my $msg = sprintf(gettext("Modify %d timer for '%s'."), $m, $a->{Search});
            if(ref $console && $autotimerid) {
                $console->message($msg);
            }
            else {
                push(@{$log},$msg);
            }
        }
    }

    $waiter->next(undef,undef,gettext('Read new timer in data base.'))
      if(ref $waiter);

    sleep 1;

    $timermod->readData();

    # last call of waiter
    $waiter->end() if(ref $waiter);

    if(ref $console) {
        $console->start() if(ref $waiter);
        unshift(@{$log},sprintf(gettext("Autotimer process has %d timers created and %d timers modified."), $C, $M));
        lg join("\n", @$log);
        $console->message($log);
        $console->link({
            text => gettext("Back to autotimer list"),
            url => "?cmd=alist",
        }) if($console->typ eq 'HTML');
    }
}

# ------------------
# Name:  autotimerCreate
# Descr: Routine to display the create form for Autotimer.
# Usage: $obj->autotimerCreate($watcher, $console, [$userdata]);
# ------------------
sub autotimerCreate {
    my $obj = shift || return error ('No Object!' );
    my $watcher = shift || return error ('No Watcher!');
    my $console = shift || return error ('No Console');
    my $timerid = shift || 0;
    my $data    = shift || 0;

    $obj->autotimerEdit($watcher, $console, $timerid, $data);
}

# ------------------
# Name:  autotimerEdit
# Descr: Routine to display the edit form for Autotimer.
# Usage: $obj->autotimerEdit($watcher, $console, [$atid], [$userdata]);
# ------------------
sub autotimerEdit {
    my $obj = shift || return error ('No Object!' );
    my $watcher = shift || return error ('No Watcher!');
    my $console = shift || return error ('No Console');
    my $timerid = shift || 0;
    my $data    = shift || 0;

    my $mod = main::getModule('CHANNELS');
    my $modT = main::getModule('TIMERS');

    my $epg;
    if($timerid and not ref $data) {
        $epg = $obj->{dbh}->selectrow_hashref("select * from AUTOTIMER where Id = $timerid")
            or return $console->message(sprintf(gettext("Autotimer with ID '%s' does not exist in the database!"),$timerid));

            # Channels Ids in Namen umwandeln
            my @channels = map { $_ = $mod->ChannelToPos($_) } split(/[\s|,]+/, $epg->{Channels});
            $epg->{Channels} = \@channels;

            # question erwartet ein Array
            my @infields = split(/\s*,\s*/, $epg->{InFields});
            $epg->{InFields} = \@infields;

            # question erwartet ein Array
            my @weekdays = split(/\s*,\s*/, $epg->{Weekdays});
            $epg->{Weekdays} = \@weekdays;
    } elsif (ref $data eq 'HASH') {
        $epg = $data;
    }

    my %wd = (
        'Mon' => gettext('Mon'),
        'Tue' => gettext('Tue'),
        'Wed' => gettext('Wed'),
        'Thu' => gettext('Thu'),
        'Fri' => gettext('Fri'),
        'Sat' => gettext('Sat'),
        'Sun' => gettext('Sun')
    );

    my %in = (
        'title' => gettext('Title'),
        'subtitle' => gettext('Subtitle'),
        'description' => gettext('Description')
    );

    my $questions = [
        'Id' => {
            typ     => 'hidden',
            def     => $epg->{Id} || 0,
        },
        'Activ' => {
            typ     => 'confirm',
            def     => $epg->{Activ} || 'y',
            msg     => gettext('Switch this autotimer on?'),
        },
        'Done' => {
            typ     => 'confirm',
            def     => $epg->{Done} || 'y',
            msg     => gettext('Ignore retry with same broadcast title and already taked recordings?'),
        },
        'Search' => {
            msg   => gettext("Search (minus will search with NOT this txt)\ni.e.: Enterprise,-Voyager\n(ampersand will search AND this txt)\ni.e.:Spielfilm,&USA"),
            req   => gettext('This is required!'),
            def   => $epg->{Search} || '',
        },
        'InFields' => {
            msg   => gettext('Search in this EPG fields'),
            typ   => 'checkbox',
            choices   => [$in{'title'}, $in{'subtitle'}, $in{'description'}],
            req   => gettext('This is required!'),
            def   => sub {
                            my $value = $epg->{InFields} || ['title','subtitle'];
                            my @vals = (ref $value eq 'ARRAY') ? @$value : split(/\s*,\s*/, $value);
                            my @ret;
                            foreach my $v (@vals) {
                                push(@ret,$in{$v});
                            }
                            return @ret;
                          },
            check   => sub{
                my $value = shift || return;
                my $data = shift || return error('No Data in CB');
                my @vals = (ref $value eq 'ARRAY') ? @$value : split(/\s*,\s*/, $value);
                my @ret;
                foreach my $v (@vals) {
                    unless(grep($_ eq $v, @{$data->{choices}})) {
                        my $ch = join(' ', @{$data->{choices}});
                        return undef, sprintf(gettext("You can choose: %s!"),$ch);
                    }
                    foreach my $k (keys %in) {
                        push(@ret,$k)
                            if($v eq $in{$k});
                    }
                }
                return join(',', @ret);
            },
        },
        'Channels' => {
            typ     => 'list',
            def     => $epg->{Channels},
            choices => $mod->ChannelArray('Name', sprintf(' NOT (%s)', $obj->{exclude})),
            options => 'multi',
            msg     => gettext('Restrict search to this channels'),
            check   => sub{
                my $value = shift || return;
                my @vals;
                foreach my $chname ((ref $value eq 'ARRAY' ? @$value : split(/\s*,\s*/, $value))) {
                    if( my $chid = $mod->PosToChannel($chname) || $mod->NameToChannel($chname)) {
                        push(@vals, $chid);
                    } else {
                        return undef, sprintf(gettext("The channel '%s' does not exist!"),$chname);
                    }
                }
                return join(',', @vals);
            },
        },
         'Start' => {
             typ     => 'string',
             def     => sub{
		             my $value = $epg->{Start} || return "";
                     return fmttime($value);
                 },
             msg     => gettext("Starttime in format 'HH:MM'"),
             check   => sub{
                 my $value = shift || 0;
                 return undef, gettext('You set a start-time without a stop-time!')
                    if(not $data->{Stop} and $value);
        		 return "" if(not $value);
        		 $value = fmttime($value) if($value =~ /^\d+$/sig);
                 return undef, gettext('No right time!') if($value !~ /^\d+:\d+$/sig);
                 my @v = split(':', $value);
                 $value = sprintf('%02d%02d',$v[0],$v[1]);
                 if(int($value) < 2400 and int($value) >= 0) {
                     return sprintf('%04d',$value);
                 } else {
                     return undef, gettext('No right time!');
                 }
             },
         },
         'Stop' => {
             typ     => 'string',
             def     => sub{
    		         my $value = $epg->{Stop} || return "";
                     return fmttime($value);
                 },
             msg     => gettext("Endtime in format 'HH:MM'"),
             check   => sub{
                 my $value = shift || 0;
                 return undef, gettext('You set a stop-time without a start-time!')
                    if(not $data->{Start} and $value);
        		 return "" if(not $value);
        		 $value = fmttime($value) if($value =~ /^\d+$/sig);
                 return undef, gettext('No right time!') if($value !~ /^\d+:\d+$/sig);
                 my @v = split(':', $value);
                 $value = sprintf('%02d%02d',$v[0],$v[1]);
                 if(int($value) < 2400 and int($value) >= 0) {
                     return sprintf('%04d',$value);
                 } else {
                     return undef, gettext('No right time!');
                 }
             },
        },
        'Weekdays' => {
            msg   => gettext('Search only on this weekdays'),
            typ   => 'checkbox',
            choices   =>  [$wd{'Mon'}, $wd{'Tue'}, $wd{'Wed'}, $wd{'Thu'}, $wd{'Fri'}, $wd{'Sat'}, $wd{'Sun'}],
            def   => sub {
                            my $value = $epg->{Weekdays} || ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
                            my @vals = (ref $value eq 'ARRAY') ? @$value : split(/\s*,\s*/, $value);
                            my @ret;
                            foreach my $v (@vals) {
                                push(@ret,$wd{$v});
                            }
                            return @ret;
                          },
            check   => sub{
                my $value = shift || [$wd{'Mon'}, $wd{'Tue'}, $wd{'Wed'}, $wd{'Thu'}, $wd{'Fri'}, $wd{'Sat'}, $wd{'Sun'}];
                my $data = shift || return error('No Data in CB');
                my @vals = (ref $value eq 'ARRAY') ? @$value : split(/\s*,\s*/, $value);
                my @ret;
                foreach my $v (@vals) {
                    unless(grep($_ eq $v, @{$data->{choices}})) {
                        my $ch = join(' ', @{$data->{choices}});
                        return undef, sprintf(gettext("You can choose: %s!"),$ch);
                    }
                    foreach my $k (keys %wd) {
                        push(@ret,$k)
                            if($v eq $wd{$k});
                    }
                }
                return join(',', @ret);
            },
        },
        'VPS' => {
            typ     => 'confirm',
            def     => $epg->{VPS} || 'n',
            msg     => gettext('Activate VPS for new timer?'),
        },
        'prevminutes' => {
            typ     => 'integer',
            msg     => gettext('Buffer time in minutes before the scheduled end of the recorded program.'),
            def     => $epg->{prevminutes},
            check   => sub{
                my $value = shift || return;
                if($value =~ /^\d+$/sig and $value > 0) {
                    return int($value);
                } else {
                    return undef, gettext('No right Value!');
                }
            },
        },
        'afterminutes' => {
            typ     => 'integer',
            msg     => gettext('Buffer time in minutes after the scheduled end of the recorded program.'),
            def     => $epg->{afterminutes},
            check   => sub{
                my $value = shift || return;
                if($value =~ /^\d+$/sig and $value > 0) {
                    return int($value);
                } else {
                    return undef, gettext('No right Value!');
                }
            },
        },
        'MinLength' => {
            typ     => 'integer',
            msg     => gettext('Minimum length in minutes'),
            def     => $epg->{MinLength} || 0,
            check   => sub{
                my $value = shift || return;
                if($value =~ /^\d+$/sig and $value > 0) {
                    return int($value);
                } else {
                    return undef, gettext('No right Value!');
                }
            },
        },
        'Priority' => {
            typ     => 'integer',
            msg     => sprintf(gettext('Priority (0 .. %d)'),$console->{USER}->{MaxPriority} ? $console->{USER}->{MaxPriority} : 99 ),
            def     => (defined $epg->{Priority} ? $epg->{Priority} : $modT->{Priority}),
            check   => sub{
                my $value = shift || 0;
                if($value =~ /^\d+$/sig and $value >= 0 and $value < 100) {
                    if($console->{USER}->{MaxPriority} and $value > $console->{USER}->{MaxPriority}) {
                        return undef, sprintf(gettext('Sorry, but maximum priority is limited on %d!'), $console->{USER}->{MaxPriority});
                    }
                    return int($value);
                } else {
                    return undef, gettext('No right Value!');
                }
            },
        },
        'Lifetime' => {
            typ     => 'integer',
            msg     => sprintf(gettext('Lifetime (0 .. %d)'),$console->{USER}->{MaxLifeTime} ? $console->{USER}->{MaxLifeTime} : 99 ),
            def     => (defined $epg->{Lifetime} ? $epg->{Lifetime} : $modT->{Lifetime}),
            check   => sub{
                my $value = shift || 0;
                if($value =~ /^\d+$/sig and $value >= 0 and $value < 100) {
                    if($console->{USER}->{MaxLifeTime} and $value > $console->{USER}->{MaxLifeTime}) {
                        return undef, sprintf(gettext('Sorry, but maximum lifetime is limited on %d!'), $console->{USER}->{MaxLifeTime});
                    }
                    return int($value);
                } else {
                    return undef, gettext('No right Value!');
                }
            },
        },
        'Dir' => {
            msg     => gettext('Group all recordings on a directory'),
            def     => $epg->{Dir},
        },
    ];

    # Ask Questions
    $data = $console->question(($timerid ? gettext('Edit autotimer')
					 : gettext('Create a new autotimer')), $questions, $data);

    if(ref $data eq 'HASH') {
        delete $data->{Channel};

        # Last chance ;)
        return $console->message(gettext('Nothing to search defined!'))
            unless($data->{Search});

    	$obj->_insert($data);

    	$data->{Id} = $obj->{dbh}->selectall_arrayref('SELECT max(ID) FROM AUTOTIMER')->[0][0]
    		if(not $data->{Id});

        $console->message(gettext('Autotimer saved!'));
        lg sprintf('%s autotimer with search "%s" is saved from User: %s',
            ($timerid ? 'New' : 'Changed'),
            $data->{Search},
            $console->{USER}->{Name}
            );
        $obj->autotimer($watcher, $console, $data->{Id}, $data->{refe} );

        $console->link({
            text => gettext("Back to referred side"),
            url => $console->{browser}->{Referer},
        }) if($console->typ eq 'HTML');

    }
    return 1;
}

# ------------------
# Name:  autotimerDelete
# Descr: Routine to display the delete form for Autotimer.
# Usage: $obj->autotimerDelete($watcher, $console, $atid);
# ------------------
sub autotimerDelete {
    my $obj = shift || return error ('No Object!' );
    my $watcher = shift || return error ('No Watcher!');
    my $console = shift || return error ('No Console');
    my $timerid = shift || return $console->message(gettext("No ID for autotimer to delete! Please use adelete 'aid'"));   # If timerid the edittimer

    my $erg = $obj->{dbh}->do("delete from AUTOTIMER where Id in ($timerid)")
        or return $console->message(sprintf gettext("Autotimer with ID '%s' does not exist in the database!"), $timerid);
    $console->message(sprintf gettext("Autotimer %s is deleted."), $timerid);
    lg sprintf('autotimer with id "%s" is deleted from User: %s',
        $timerid,
        $console->{USER}->{Name}
        );
    $console->redirect({url => $console->{browser}->{Referer}, wait => 1})
        if($console->typ eq 'HTML');
}

# ------------------
# Name:  autotimerToogle
# Descr: Switch the Autotimer on or off.
# Usage: $obj->autotimerToogle($watcher, $console, $atid);
# ------------------
sub autotimerToggle {
    my $obj = shift || return error ('No Object!' );
    my $watcher = shift || return error ('No Watcher!');
    my $console = shift || return error ('No Console');
    my $timerid = shift || return $console->message(gettext("No ID for autotimer to toggle! Please use atoggle 'aid'"));   # If timerid the edittimer

    my $status = $obj->{dbh}->selectrow_arrayref("select Activ from AUTOTIMER where Id = $timerid")->[0]
       or return $console->message(sprintf gettext("Autotimer with ID '%s' does not exist in the database!"), $timerid);

    my $newstatus = ($status eq 'n') ? 'y' : 'n';
    my $erg = $obj->{dbh}->do("UPDATE AUTOTIMER set Activ = '$newstatus' where Id = $timerid")
        or return $console->message(sprintf(gettext("Autotimer with ID '%s' does not exist in the database!"),$timerid));
    my $text = ($newstatus eq 'n') ? gettext('disabled')
                                   : gettext('activated');
    lg sprintf('autotimer with id "%s" is %s from User: %s',
        $timerid,
        ($newstatus eq 'n' ? 'disabled' : 'activated'),
        $console->{USER}->{Name}
        );

    $console->message(sprintf gettext("Autotimer %s is %s."), $timerid, $text);
    $console->redirect({url => $console->{browser}->{Referer}, wait => 2})
        if($console->typ eq 'HTML');
}

# ------------------
# Name:  list
# Descr: List Autotimers in a table display.
# Usage: $obj->list($watcher, $console, [$atid], [$params]);
# ------------------
sub list {
    my $obj = shift || return error ('No Object!' );
    my $watcher = shift || return error ('No Watcher!');
    my $console = shift || return error ('No Console');
    my $id      = shift || '';
    my $params  = shift;

    my $where;
    if($id =~ /^.*?/) {
        if($id =~ /^[0-9]+?/) {
            $where = "WHERE Id = '$id'";
        } else {
            $where = "WHERE Search LIKE '%$id%' or Dir LIKE '%$id%'";
        }
    }

    my %f = (
        'Id' => umlaute(gettext('Service')),
        'Act' => umlaute(gettext('Act')),
        'Search' => umlaute(gettext('Search')),
        'Channels' => umlaute(gettext('Channels')),
        'Start' => umlaute(gettext('Start')),
        'Stop' => umlaute(gettext('Stop')),
        'Dir' => umlaute(gettext('Dir')),
        'Min' => umlaute(gettext('Min')),
    );

    my $sql = qq|
    select
      Id as $f{'Id'},
      Activ as $f{'Act'},
      Search as $f{'Search'},
      Channels as $f{'Channels'},
      Dir as $f{'Dir'},
      Start as $f{'Start'},
      Stop as $f{'Stop'},
      MinLength as $f{'Min'}
    FROM
      AUTOTIMER
    $where
    |;

    my $fields = fields($obj->{dbh}, $sql);

    my $sortby = gettext("Search");
    $sortby = $params->{sortby}
        if(exists $params->{sortby} && grep(/^$params->{sortby}$/i,@{$fields}));
    $sql .= " order by $sortby";
    if(exists $params->{desc} && $params->{desc} == 1) {
        $sql .= " desc"; }
    else {
        $sql .= " asc"; }

    my $erg = $obj->{dbh}->selectall_arrayref($sql);
    unshift(@$erg, $fields);

    my $channels = main::getModule('CHANNELS')->ChannelHash('Id');
    my $timers = main::getModule('TIMERS')->getTimersByAutotimer();

    $console->table($erg,
        {
            sortable => 1,
            channels => $channels,
            timers => $timers,
        }
    );
}


# ------------------
sub _eventsearch {
# ------------------
    my $obj = shift  || return error ('No Object!' );
    my $a   = shift  || return error ('No Data from Autotimer!' );
    my $timermod = shift  || main::getModule('TIMERS') || return error ('No Timermod!' );

    my $search = buildsearch($a->{InFields}, $a->{Search});

    # Start and Stop
    if($a->{Start} and $a->{Stop}) {
        if($a->{Start} > $a->{Stop}) {
            $search .= "\n AND ((DATE_FORMAT(e.starttime, '%H%i') > $a->{Start} AND DATE_FORMAT(e.starttime, '%H%i') < 2359) OR (DATE_FORMAT(e.starttime, '%H%i') >= 0 and DATE_FORMAT(e.starttime, '%H%i') < $a->{Stop}))";
        } else {
            $search .= "\n AND (DATE_FORMAT(e.starttime, '%H%i') > $a->{Start} AND DATE_FORMAT(e.starttime, '%H%i') < $a->{Stop})";
        }
    }

    # Min Length
    if(exists $a->{MinLength} and $a->{MinLength}) {
        $search .= sprintf(" AND e.duration >= %d ", $a->{MinLength} * 60);
    }

    # Channels
    if($a->{Channels} and my @channelids = split(',', $a->{Channels})) {
        @channelids = map {$_ = "'$_'"} @channelids;
        $search = sprintf(' %s  AND channel_id in (%s)', $search, join(',', @channelids));
    }

    # Weekdays
    if($a->{Weekdays} and my @weekdays = split(',', $a->{Weekdays})) {
        @weekdays = map {$_ = "'$_'"} @weekdays;
        $search = sprintf(' %s  AND DATE_FORMAT(e.starttime, \'%a\') in (%s)', $search, join(',', @weekdays));
    }

    # Exclude Channels
    if($obj->{exclude}) {
        $search = sprintf(' %s  AND NOT (c.%s)', $search, $obj->{exclude});
    }

	# Custom time range
	my $after = 0;
	my $prev = 0;
	if($a->{VPS} ne 'y') {
		if(defined $a->{prevminutes}) {
			$prev = $a->{prevminutes} * 60;
		} else {
			$prev = $timermod->{prevminutes} * 60;
		}
		if(defined $a->{afterminutes}) {
			$after = $a->{afterminutes} * 60;
		} else {
			$after = $timermod->{afterminutes} * 60;
		}
	}

    # Search for timer
    my $sql = qq|
SELECT
    e.eventid as eventid,
    e.channel_id as ChannelID,
    c.Name as Channel,
    c.POS as POS,
    e.title as Title,
    e.subtitle as Subtitle,
    e.description as Summary,
    DATE_FORMAT(FROM_UNIXTIME(UNIX_TIMESTAMP(e.starttime) - $prev ), '%d') as Day,
    DATE_FORMAT(FROM_UNIXTIME(UNIX_TIMESTAMP(e.starttime) - $prev ), '%H%i') as Start,
    DATE_FORMAT(FROM_UNIXTIME(UNIX_TIMESTAMP(e.starttime) + e.duration + $after ), '%H%i') as Stop,
    DATE_FORMAT(FROM_UNIXTIME(UNIX_TIMESTAMP(e.vpstime)), '%H%i') as VpsStart,
    DATE_FORMAT(FROM_UNIXTIME(UNIX_TIMESTAMP(e.vpstime) + e.duration), '%H%i') as VpsStop
FROM
    EPG as e,
    CHANNELS as c
WHERE
    ( $search )
    AND ( e.channel_id = c.Id )|;

#dumper $sql;
    my $data = $obj->{dbh}->selectall_hashref($sql, 'eventid');

    return $data;
}

# ------------------
sub _timerexists {
# ------------------
    my $obj = shift  || return error ('No Object!' );
    my $eventdata = shift  || return error ('No Data from Autotimer!' );
    my $timermod  = shift  || main::getModule('TIMERS') || return error ('No Timermod!' );
    my ($nexttime, $aidcomment, $file, $summary) = @_;

    # Avoid Timer already defined (the timer with the same data again do not put on)
    my $sql = "select count(*) as cc from TIMERS where
                ChannelID = '$eventdata->{ChannelID}'
                and UNIX_TIMESTAMP(NextStartTime) = '$nexttime->{start}'
                and UNIX_TIMESTAMP(NextStopTime)  = '$nexttime->{stop}'
                and Priority = '$eventdata->{Priority}'
                and Lifetime = '$eventdata->{Lifetime}'
                and (( Status & 1 = '0' )
                or  (File = '$file'
                and Summary = '$summary'))";
    my $again = $obj->{dbh}->selectrow_hashref($sql) || return 0;

    return $again->{cc};
}

# ------------------
sub _timerexistsfuzzy {
# ------------------
    my $obj = shift  || return error ('No Object!' );
    my $eventdata = shift  || return error ('No Data from Autotimer!' );
    my $timermod  = shift  || main::getModule('TIMERS') || return error ('No Timermod!' );
    my ($nexttime, $aidcomment, $file, $summary) = @_;

    # Adjust timers set by the autotimer
    my $timerID = 0;
    my $sql = "select ID from TIMERS where
                ChannelID = '$eventdata->{ChannelID}'
                and UNIX_TIMESTAMP(NextStartTime) = '$nexttime->{start}'
                and UNIX_TIMESTAMP(NextStopTime)  = '$nexttime->{stop}'
                and Summary like '%$aidcomment'
                order by length(Summary) desc;";
    # dumper($sql);
    my $tresult = $obj->{dbh}->selectrow_hashref($sql);
    return $tresult->{ID}
        if( ref $tresult and $tresult->{ID} );
    return 0;
}

# ------------------
sub _recordexists {
# ------------------
    my $obj = shift  || return error ('No Object!' );
    my $eventdata = shift  || return error ('No Data from Autotimer!' );
    my $timermod  = shift  || main::getModule('TIMERS') || return error ('No Timermod!' );
    my ($nexttime, $aidcomment, $file, $summary) = @_;

    # Ignore timer if it already with same title recorded
    my $sql = "SELECT count(*) as cc
                FROM RECORDS as r, OLDEPG as e
                WHERE e.eventid = r.EventId
                    AND CONCAT_WS('~',e.title,IF(e.subtitle<>'',e.subtitle,NULL)) = '$file'";
    my $recording = $obj->{dbh}->selectrow_hashref($sql);
    return $recording->{cc}
        if(ref $recording and $recording->{cc});
    return 0;
}

# ------------------
sub _timerexiststitle {
# ------------------
    my $obj = shift  || return error ('No Object!' );
    my $eventdata = shift  || return error ('No Data from Autotimer!' );
    my $timermod  = shift  || main::getModule('TIMERS') || return error ('No Timermod!' );
    my ($nexttime, $aidcomment, $file, $summary) = @_;


    my $sql = "select count(*) as cc from TIMERS where File = '$file'";
#                and
#                (
#                    ChannelID != '$eventdata->{ChannelID}' or
#                    UNIX_TIMESTAMP(NextStartTime) != '$nexttime->{start}'
#                    or UNIX_TIMESTAMP(NextStopTime)  != '$nexttime->{stop}'
#                )";
    my $done = $obj->{dbh}->selectrow_hashref($sql);
    return $done->{cc}
        if(ref $done and $done->{cc});
    return 0;
}


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

    if(ref $data eq 'HASH') {
        my ($names, $vals, $kenn);
        map {
            push(@$names, $_);
            push(@$vals, $data->{$_}),
            push(@$kenn, '?'),
        } sort keys %$data;

        my $sql = sprintf("REPLACE INTO AUTOTIMER (%s) VALUES (%s)",
                join(', ', @$names),
                join(', ', @$kenn),
        );
        my $sth = $obj->{dbh}->prepare( $sql );
        $sth->execute( @$vals );
    } else {
        my $sth = $obj->{dbh}->prepare('REPLACE INTO AUTOTIMER VALUES (?,?,?,?,?,?,?,?,?)');
        $sth->execute( @$data );
    }
}

# ------------------
# Name:  autotimerimport
# Descr: Read a file for import autotimer export list.
#        This is usefull to backup own Autotimers.
# Usage: $obj->autotimerimport($watcher, $console, $filename);
# ------------------
sub autotimerimport {
    my $obj = shift || return error ('No Object!' );
    my $watcher = shift || return error ('No Watcher!');
    my $console = shift || return error ('No Console');
    my $AT_FILENAME = shift || return $console->message(gettext("No filename to import! Please use aimport 'vdradmind.at'"));
    my $mod = main::getModule ('CHANNELS');
    my $cnt = "0";
    open(AT_FILE, "$AT_FILENAME") || return $console->message(sprintf(gettext("can't open file '%s' : %s"),$AT_FILENAME,$!));
    while(<AT_FILE>) {
        chomp;
        next if ($_ eq "");

        my($active, $pattern, $section, $start, $stop, $episode, $prio,
            $lft, $channel, $directoy) = split(/\:/, $_);

        my $data;
        $data->{Id} = 0;
        if($active eq "1") { $data->{Activ} = 'y'; }
	    else               { $data->{Activ} = 'n'; }

        my @vals;
        $data->{Done} = 'y';
        $data->{Search} = $pattern;
        push(@vals, "title")
            if($section & 1);
        push(@vals, "subtitle")
            if($section & 2);
        push(@vals, "description")
            if($section & 4);
        $data->{InFields} = join(',', @vals);
        if( $channel ne "") {
            if (my $chid = $mod->PosToChannel($channel)) {
                $data->{Channels} = $chid;
            }
        }
        $data->{Priority} = $prio;
        $data->{Lifetime} = $lft;
        $data->{Dir} = $directoy;
        $data->{Start} = $start;
        $data->{Stop} = $stop;
#       $data->{Episode} = $episode; # autotimerimport don't support episode, i belief "1" means File = title~subtitle / "0" means File = title

        $obj->_insert($data);
        ++$cnt;
    }
    close (AT_FILE);
    $console->message(sprintf(gettext("%d Autotimer read from file %s."),$cnt,$AT_FILENAME));
    lg sprintf('%d autotimers imported from file "%s" as User: %s',
        $cnt,
        $AT_FILENAME,
        $console->{USER}->{Name}
        );

    $console->redirect({url => "?cmd=alist", wait => 2})
        if($console->typ eq 'HTML');
    return 1;
}

# ------------------
# Name:  autotimerexport
# Descr: Read autotimer from database and save this to a export file.
#        This is usefull to backup own Autotimers.
# Usage: $obj->autotimerexport($watcher, $console, $filename);
# ------------------
sub autotimerexport {
    my $obj = shift || return error ('No Object!' );
    my $watcher = shift || return error ('No Watcher!');
    my $console = shift || return error ('No Console');
    my $AT_FILENAME = shift || return $console->err(gettext("No filename to export! Please use aexport 'vdradmind.at'"));
    my $id      = shift || '';

    return $console->err(sprintf(gettext("File to store autotimer '%s' already exist! The file can't be overwritten. Please remove the file manually."),$AT_FILENAME))
        if(-e $AT_FILENAME);

    my $mod = main::getModule ('CHANNELS');

    my $sql = "select Activ, Search, InFields, Channels, Dir, Start, Stop, Priority, Lifetime from AUTOTIMER where AUTOTIMER.Id like '%$id%' order by AUTOTIMER.Id";
    my $atlist = $obj->{dbh}->selectall_arrayref($sql);

    return $console->err(gettext("No autotimer data are to export available!"))
        if(scalar $atlist <= 0);

    my $cnt = "0";
    open(AT_FILE, ">$AT_FILENAME") || return $console->err(sprintf(gettext("can't open file '%s' : %s"),$AT_FILENAME,$!));
    foreach my $data (@$atlist) {
        if($data->[1] ne "" and $data->[2] ne "") {

        my $active = "0";
        my $pattern = $data->[1];
        my $section = "0";
        my $start = "";
        my $stop = "";
        my $episode = "0"; # autotimerexport don't support episode, i belief "1" means File = title~subtitle / "0" means File = title
        my $prio = "";
        my $lft = "";
        my $channel = "";
        my $directoy = "";

        $active = "1" if($data->[0] eq "y");

        $section |= 1 if ($data->[2]=~/^.*title.*$/);
        $section |= 2 if ($data->[2]=~/^.*subtitle.*$/);
        $section |= 4 if ($data->[2]=~/^.*description.*$/);

        if( $data->[3]) {
            foreach my $ch (@{$mod->ChannelArray ('Id')}) {
                if ($data->[3] eq $ch->[0]) { # vdradmin suppport only one Channel
                    $channel = $ch->[1];
                    last;
                }
            }
        }

        $directoy = $data->[4] if($data->[4]);
        $start = $data->[5] if($data->[5] and $data->[5] ne $data->[6]); # Exclude if start and stop if same value e.g. 0000
        $stop = $data->[6] if($data->[6] and $data->[5] ne $data->[6]);
        $prio = $data->[7] if($data->[7]);
        $lft = $data->[8] if($data->[8]);

        print AT_FILE sprintf("%s:%s:%s:%s:%s:%s:%s:%s:%s:%s\n",
            $active,$pattern,$section,$start,$stop,$episode,$prio,$lft,$channel,$directoy)
            || return return $console->message(sprintf(gettext("can't write file '%s' : %s"),$AT_FILENAME,$!));
        ++$cnt;
        }
    }
    close (AT_FILE);
    $console->message(sprintf(gettext("%d Autotimer write to file %s"),$cnt,$AT_FILENAME));
    lg sprintf('%d autotimers exported to file "%s" from User: %s',
        $cnt,
        $AT_FILENAME,
        $console->{USER}->{Name}
        );
    $console->redirect({url => "?cmd=alist", wait => 2})
        if($console->typ eq 'HTML');
    return 1;
}

# ------------------
# Name:  _placeholder
# Descr: Replace the placeholder with extendet EPG
# Usage: my $text = $obj->_placeholder($epgdata, $autotimerdata);
# ------------------
sub _placeholder {
    my $obj  = shift  || return error ('No Object!' );
    my $data = shift  || return error ('No Data!' );
    my $at   = shift  || return error ('No AtData!' );

    my $file;

    if ($at->{Dir}) {
    	my $title = $at->{Dir};
        if($title =~ /.*%.*%.*/sig) {
	 	   my %at_details;
                $at_details{'title'}            = $data->{Title};
                $at_details{'subtitle'}         = $data->{Subtitle} ? $data->{Subtitle} : $data->{Start};
                $at_details{'date'}             = $data->{Day};
                $at_details{'regie'}            = $1 if $data->{Summary} =~ m/\|Director: (.*?)\|/;
                $at_details{'category'}         = $1 if $data->{Summary} =~ m/\|Category: (.*?)\|/;
                $at_details{'genre'}            = $1 if $data->{Summary} =~ m/\|Genre: (.*?)\|/;
                $at_details{'year'}             = $1 if $data->{Summary} =~ m/\|Year: (.*?)\|/;
                $at_details{'country'}          = $1 if $data->{Summary} =~ m/\|Country: (.*?)\|/;
                $at_details{'originaltitle'}    = $1 if $data->{Summary} =~ m/\|Originaltitle: (.*?)\|/;
                $at_details{'fsk'}              = $1 if $data->{Summary} =~ m/\|FSK: (.*?)\|/;
                $at_details{'episode'}          = $1 if $data->{Summary} =~ m/\|Episode: (.*?)\|/;
                $at_details{'rating'}           = $1 if $data->{Summary} =~ m/\|Rating: (.*?)\|/;
                $title =~ s/%([\w_-]+)%/$at_details{lc($1)}/sieg;
				$file = $title;
        } else { # Classic mode DIR~TITLE~SUBTILE
			$file = sprintf('%s~%s~%s', $at->{Dir}, $data->{Title},$data->{Subtitle});
        }
	} else {
		$file = sprintf('%s~%s', $data->{Title},$data->{Subtitle});
    }

    # sind irgendweche Tags verwendet worden, die leer waren und die doppelte Verzeichnisse erzeugten?
    $file =~s#~+#~#g;
    $file =~s#^~##g;
    $file =~s#~$##g;

    return $file;
}

1;
