#!/usr/bin/perl

use strict;
use warnings;
no warnings 'redefine';

use utf8;

use Data::Dumper;
use URI::Escape();
use DateTime();

use utf8();
use params();
use config();
use entry();
use log();
use entry();
use template();
use calendar();
use auth();
use uac();
use project();
use studios();
use events();
use series();
use series_dates();
use markup();
use localization();
use studio_timeslot_dates();
use work_dates();
use playout();
use user_settings();
use audio_recordings();
use audio();
use user_day_start();

binmode STDOUT, ":utf8";

my $r = shift;
( my $cgi, my $params, my $error ) = params::get($r);

my $config = config::get('../config/config.cgi');
my ( $user, $expires ) = auth::get_user( $config, $params, $cgi );
return if ( !defined $user ) || ( $user eq '' );
$config->{user} = $user;

my $user_presets = uac::get_user_presets(
    $config,
    {
        user       => $user,
        project_id => $params->{project_id},
        studio_id  => $params->{studio_id}
    }
);
$params->{default_studio_id} = $user_presets->{studio_id};
$params = uac::setDefaultStudio( $params, $user_presets );
$params->{expires} = $expires;

my $scriptName = 'calendar.cgi';

#add "all" studio to select box
unshift @{ $user_presets->{studios} },
  {
    id   => -1,
    name => '-all-'
  };

# select studios, TODO: do in JS
if ( $params->{studio_id} eq '-1' ) {
    for my $studio ( @{ $user_presets->{studios} } ) {
        delete $studio->{selected};
        $studio->{selected} = 1 if $params->{studio_id} eq $studio->{id};
    }
}

my $request = {
    url => $ENV{QUERY_STRING} || '',
    params => {
        original => $params,
        checked  => check_params( $config, $params ),
    },
};
$request = uac::prepare_request( $request, $user_presets );
$params = $request->{params}->{checked};

if (
    (
        ( defined $params->{action} ) && ( ( $params->{action} eq 'show' )
            || ( $params->{action} eq 'edit_event' ) )
    )
    || ( $params->{part} == 1 )
  )
{
    print "Content-type:text/html; charset=UTF-8;\n\n";
} else {

    #process header
    my $headerParams = uac::set_template_permissions( $request->{permissions}, $params );
    $headerParams->{loc} = localization::get( $config, { user => $user, file => 'menu' } );
    template::process( $config, 'print', template::check( $config, 'calendar-header.html' ),
        $headerParams );
    if ( $params->{list} eq '1' ) {
        print q{
            <script src="js/jquery.tablesorter.min.js"></script>
            <style>#content{ top:5rem; position:relative; }</style>
        };
    }
}

if ( defined $user_presets->{error} ) {
    print "<br><br>";
    uac::print_error( $user_presets->{error} );
    return;
}

$config->{access}->{write} = 0;
unless ( defined $params->{project_id} ) {
    uac::print_error("Please select a project");
    return;
}

if ( $params->{project_id} ne '-1' ) {
    if ( project::check( $config, { project_id => $params->{project_id} } ) ne '1' ) {
        uac::print_error("invalid project");
        return;
    }
}

unless ( defined $params->{studio_id} ) {
    uac::print_error("Please select a studio");
    return;
}
if ( $params->{studio_id} ne '-1' ) {
    if ( studios::check( $config, { studio_id => $params->{studio_id} } ) ne '1' ) {
        uac::print_error("invalid studio");
        return;
    }
}

my $start_of_day = $params->{day_start};
my $end_of_day   = $start_of_day;
$end_of_day += 24 if ( $end_of_day <= $start_of_day );
our $hour_height = 60;
our $yzoom       = 1.5;

showCalendar(
    $config, $request,
    {
        hour_height  => $hour_height,
        yzoom        => $yzoom,
        start_of_day => $start_of_day,
        end_of_day   => $end_of_day,
    }
);

sub showCalendar {
    my $config      = shift;
    my $request     = shift;
    my $cal_options = shift;

    my $hour_height  = $cal_options->{hour_height};
    my $yzoom        = $cal_options->{yzoom};
    my $start_of_day = $cal_options->{start_of_day};
    my $end_of_day   = $cal_options->{end_of_day};

    my $params = $request->{params}->{checked};
    my $permissions = $request->{permissions} || {};
    unless ( $permissions->{read_series} == 1 ) {
        uac::permissions_denied('read_series');
        return;
    }

    #get range from user settings
    my $user_settings = user_settings::get( $config, { user => $params->{presets}->{user} } );
    $params->{range} = $user_settings->{range} unless defined $params->{range};
    $params->{range} = 28 unless defined $params->{range};

    #get colors from user settings
    print user_settings::getColorCss( $config, { user => $params->{presets}->{user} } )
      if $params->{part} == 0;

    $params->{loc} =
      localization::get( $config, { user => $params->{presets}->{user}, file => 'all,calendar' } );
    my $language = $user_settings->{language} || 'en';
    $params->{language} = $language;
    print localization::getJavascript( $params->{loc} ) if $params->{part} == 0;

    my $calendar = getCalendar( $config, $params, $language );
    my $options  = {};
    my $events   = [];

    if ( ( $params->{part} == 1 ) || ( $params->{list} == 1 ) ) {

        #set date range
        my $from = $calendar->{from_date};
        my $till = $calendar->{till_date};

        my $project_id = $params->{project_id};
        my $studio_id  = $params->{studio_id};

        #build event filter
        $options = {
            project_id         => $project_id,
            template           => 'html',
            limit              => 600,
            #get                => 'no_content',
            from_date          => $from,
            till_date          => $till,
            date_range_include => 1,
            archive            => 'all',
        };

        # set options depending on switches
        if ( $params->{studio_id} ne '-1' ) {
            $options->{studio_id} = $studio_id;
            my $location = $params->{presets}->{studio}->{location};
            $options->{location} = $location if $location =~ /\S/;
        }

        if ( $params->{project_id} ne '-1' ) {
            $options->{project_id} = $project_id;
            my $project = $params->{presets}->{project}->{name};
            $options->{project} = $project if $project =~ /\S/;
        }

        if ( defined $params->{series_id} ) {
            $options->{series_id} = $params->{series_id};
            delete $options->{from_date};
            delete $options->{till_date};
            delete $options->{date_range_include};
        }

        if ( $params->{search} =~ /\S/ ) {
            if ( $params->{list} == 1 ) {
                $options->{search} = $params->{search};
                delete $options->{from_date};
                delete $options->{till_date};
                delete $options->{date_range_include};
            }
        }
        $options->{from_time} = '00:00' if defined $options->{from_date};

        $options->{draft} = 0 unless $params->{list} == 1;

        #get events sorted by date
        $events = getSeriesEvents( $config, $request, $options, $params );
        unless ( $params->{list} == 1 ) {
            for my $event (@$events) {
                $event->{origStart} = $event->{start};
                $event->{origContent} = $event->{origContent};
            }
            $events = break_dates( $events, $start_of_day );
        }

        # recalc after break (for list only?)
        for my $event (@$events) {
            delete $event->{day};
            delete $event->{start_date};
            delete $event->{end_date};
            $event = events::calc_dates( $config, $event );
        }

        my $events_by_start = {};
        for my $event (@$events) {
            $events_by_start->{ $event->{start} } = $event;
        }

        #build series filter
        $options = {
            project_id         => $project_id,
            studio_id          => $studio_id,
            from               => $from,
            till               => $till,
            date_range_include => 1,
            exclude            => 0
        };

        if ( defined $params->{series_id} ) {
            $options->{series_id} = $params->{series_id};
            delete $options->{from};
            delete $options->{till};
            delete $options->{date_range_include};
        }

        if ( $params->{search} =~ /\S/ ) {
            $options->{search} = $params->{search};
            if ( $params->{list} == 1 ) {
                delete $options->{from};
                delete $options->{till};
                delete $options->{date_range_include};
            }
        }

        #get all series dates
        my $series_dates = series_dates::get_series( $config, $options );
        my $id = 0;
        for my $date (@$series_dates) {
            $date->{schedule} = 1;

            #$date->{event_id}=-1;
            $date->{event_id}  = $id;
            $date->{origStart} = $date->{start};
            delete $date->{day};
            delete $date->{start_date};
            delete $date->{end_date};
            $id++;
        }
        unless ( $params->{list} == 1 ) {
            $series_dates = break_dates( $series_dates, $start_of_day );
        }

        #merge series and events
        for my $date (@$series_dates) {
            $date = events::calc_dates( $config, $date );
            push @$events, $date;
        }

        #get timeslot_dates
        my $studio_dates = studio_timeslot_dates::get( $config, $options );

        $id = 0;
        for my $date (@$studio_dates) {
            $date->{grid}      = 1;
            $date->{series_id} = -1;

            #$date->{event_id}=-1;
            $date->{event_id}  = $id;
            $date->{origStart} = $date->{start};
            delete $date->{day};
            delete $date->{start_date};
            delete $date->{end_date};
            $id++;
        }
        unless ( $params->{list} == 1 ) {
            $studio_dates = break_dates( $studio_dates, $start_of_day );
        }

        for my $date (@$studio_dates) {
            $date = events::calc_dates( $config, $date );
            push @$events, $date;
        }

        #get work_dates
        my $work_dates = work_dates::get( $config, $options );
        for my $date (@$work_dates) {
            $date->{work}      = 1;
            $date->{series_id} = -1;
            $date->{event_id}  = -1;
            $date->{origStart} = $date->{start};
            delete $date->{day};
            delete $date->{start_date};
            delete $date->{end_date};
        }
        unless ( $params->{list} == 1 ) {
            $work_dates = break_dates( $work_dates, $start_of_day );
        }

        for my $date (@$work_dates) {
            $date = events::calc_dates( $config, $date );
            push @$events, $date;
        }

        #get playout
        delete $options->{exclude};
        my $playout_dates = playout::get_scheduled( $config, $options );
        $id = 0;
        for my $date (@$playout_dates) {
            my $format = undef;
            if ( defined $date->{'format'} ) {
                $format =
                    ( $date->{'format'}         || '' ) . " "
                  . ( $date->{'format_version'} || '' ) . " "
                  . ( $date->{'format_profile'} || '' );
                $format =~ s/MPEG Audio Version 1 Layer 3/MP3/g;
                $format .= ' ' . ( $date->{'format_settings'} || '' )
                  if defined $date->{'format_settings'};
                $format .= '<br>';
            }

            $date->{play}      = 1;
            $date->{series_id} = -1;
            $date->{event_id}  = $id;
            $date->{title}     = '';
            $date->{title} .= '<b>errors</b>: ' . $date->{errors} . '<br>'
              if defined $date->{errors};
            $date->{title} .= audio::formatDuration(
                $date->{duration},
                $date->{event_duration},
                sprintf( "duration: %.1g h", $date->{duration} / 3600 ) . "<br>",
                sprintf( "%d s",             $date->{duration} )
            ) if defined $date->{duration};
            $date->{title} .= audio::formatLoudness( $date->{rms_left}, 'L: ' ) . ', '
              if defined $date->{rms_left};
            $date->{title} .= audio::formatLoudness( $date->{rms_right}, 'R: ' ) . '<br>'
              if defined $date->{rms_right};
            $date->{title} .= audio::formatBitrate( $date->{bitrate} ) if defined $date->{bitrate};
            $date->{title} .= ' ' . audio::formatBitrateMode( $date->{bitrate_mode} ) . '<br>'
              if defined $date->{bitrate_mode};
            $date->{title} .=
              '<b>replay gain</b> ' . sprintf( "%.1f", $date->{replay_gain} ) . '<br>'
              if defined $date->{replay_gain};
            $date->{title} .= audio::formatSamplingRate( $date->{sampling_rate} )
              if defined $date->{sampling_rate};
            $date->{title} .= audio::formatChannels( $date->{channels} ) . '<br>'
              if defined $date->{channels};
            $date->{title} .= int( ( $date->{'stream_size'} || '0' ) / ( 1024 * 1024 ) ) . 'MB<br>'
              if defined $date->{'stream_size'};
            $date->{title} .= $format if defined $format;
            $date->{title} .= '<b>library</b>: ' . ( $date->{writing_library} || '' ) . '<br>'
              if defined $date->{'writing_library'};
            $date->{title} .= '<b>path</b>: ' . ( $date->{file} || '' ) . '<br>'
              if defined $date->{file};
            $date->{title} .= '<b>updated_at</b>: ' . ( $date->{updated_at} || '' ) . '<br>'
              if defined $date->{updated_at};
            $date->{title} .= '<b>modified_at</b>: ' . ( $date->{modified_at} || '' ) . '<br>'
              if defined $date->{modified_at};

            $date->{rms_image} = URI::Escape::uri_unescape( $date->{rms_image} )
              if defined $date->{rms_image};

            $date->{origStart} = $date->{start};

            # set end date seconds to 00 to handle error at break_dates/join_dates
            $date->{end} =~ s/(\d\d\:\d\d)\:\d\d/$1\:00/;
            delete $date->{day};
            delete $date->{start_date};
            delete $date->{end_date};
            $id++;
        }

        unless ( $params->{list} == 1 ) {
            $playout_dates = break_dates( $playout_dates, $start_of_day );
        }

        for my $date (@$playout_dates) {
            $date = events::calc_dates( $config, $date );
            if ( defined $events_by_start->{ $date->{start} } ) {
                $events_by_start->{ $date->{start} }->{duration} = $date->{duration} || 0;
                $events_by_start->{ $date->{start} }->{event_duration} =
                  $date->{event_duration} || 0;
                $events_by_start->{ $date->{start} }->{rms_left}  = $date->{rms_left}  || 0;
                $events_by_start->{ $date->{start} }->{rms_right} = $date->{rms_right} || 0;
                $events_by_start->{ $date->{start} }->{playout_modified_at} = $date->{modified_at};
                $events_by_start->{ $date->{start} }->{playout_updated_at}  = $date->{updated_at};
                $events_by_start->{ $date->{start} }->{file}  = $date->{file};
            }
            push @$events, $date;
        }
        
        # series dates
        if ($params->{list} == 1 and defined $options->{series_id}){
            my $series = series::get(
                $config,
                {
                    #project_id => $project_id,
                    #studio_id  => $studio_id,
                    series_id  => $options->{series_id}
                }
            );
            if ( defined $series->[0] and $series->[0]->{predecessor_id}
                and $series->[0]->{predecessor_id} ne $series->[0]->{id} ){
                my $events2 = getSeriesEvents( $config, $request, {
                    series_id => $series->[0]->{predecessor_id}
                }, $params );
                
                for my $event (@$events2) {
                    delete $event->{day};
                    delete $event->{start_date};
                    delete $event->{end_date};
                    push @$events, events::calc_dates( $config, $event );
                }
            }
        }
    }

    #output
    printToolbar( $config, $params, $calendar ) if $params->{part} == 0;

    #if($params->{part}==1){
    print qq{
        <script> 
            var current_date="$calendar->{month} $calendar->{year}";
            var previous_date="$calendar->{previous_date}";
            var next_date="$calendar->{next_date}";
        </script>
        };

    #}

    #filter events by time
    unless ( $params->{list} == 1 ) {
        $events = filterEvents( $events, $options, $start_of_day );
    }

    #sort events by start
    @$events = sort { $a->{start} cmp $b->{start} } @$events;

    #separate by day (e.g. to 6 pm)
    my $events_by_day = {};
    for my $event (@$events) {
        my $day =
          time::datetime_to_date( time::add_hours_to_datetime( $event->{start}, -$start_of_day ) );
        push @{ $events_by_day->{$day} }, $event;
    }

    #get min and max hour from all events
    unless ( $params->{list} == 1 ) {
        my $min_hour = 48;
        my $max_hour = 0;

        for my $event (@$events) {
            if ( $event->{start} =~ /(\d\d)\:\d\d\:\d\d$/ ) {
                my $hour = $1;
                $hour += 24 if $hour < $start_of_day;
                $min_hour = $hour if ( $hour < $min_hour ) && ( $hour >= $start_of_day );
            }
            if ( $event->{end} =~ /(\d\d)\:\d\d\:\d\d$/ ) {
                my $hour = $1;
                $hour += 24 if $hour <= $start_of_day;
                $max_hour = $hour if ( $hour > $max_hour ) && ( $hour <= $end_of_day );
            }
        }
        $cal_options->{min_hour} = $min_hour;
        $cal_options->{max_hour} = $max_hour;
    }

    # calculate positions and find schedule errors (depending on position)
    for my $date ( sort ( keys %$events_by_day ) ) {
        my $events = $events_by_day->{$date};
        calc_positions( $events, $cal_options );
        find_errors($events);
    }

    for my $event (@$events) {
        next unless defined $event->{uploaded_at};
        next
          if ( defined $event->{playout_updated_at} )
          && ( $event->{uploaded_at} lt $event->{playout_updated_at} );

    }

    if ( $params->{list} == 1 ) {
        showEventList( $config, $permissions, $params, $events_by_day );
    } else {
        if ( $params->{part} == 0 ) {
            print qq{<div id="calendarTable"> </div>};
        }
        if ( $params->{part} == 1 ) {
            calcCalendarTable( $config, $permissions, $params, $calendar, $events_by_day,
                $cal_options );
            printTableHeader( $config, $permissions, $params, $cal_options );
            printTableBody( $config, $permissions, $params, $cal_options );
        }
        if ( $params->{part} == 0 ) {
            printSeries( $config, $permissions, $params, $cal_options );
            print qq{
                    </main>
            };
        }

        # time has to be set when events come in
        printJavascript( $config, $permissions, $params, $cal_options );
        if ( $params->{part} == 0 ) {
            print qq{
                    </body>
                </html>
            };
        }
    }
}

sub debugDate {
    my $date = shift;
    $date->{program}     = '' unless defined $date->{program};
    $date->{series_name} = '' unless defined $date->{series_name};
    $date->{title}       = '' unless defined $date->{title};
    $date->{splitCount}  = 0  unless defined $date->{splitCount};
    my $dt = ( $date->{start} || '' ) . "   " . ( $date->{end} | '' );
    my $da = ( $date->{start_date} || '' ) . "    " . ( $date->{end_date} || '' );
    my $type = "schedule:" . ( $date->{schedule} || "" ) . " grid:" . ( $date->{grid} || "" );
}

# break dates at start_of_day

sub break_dates {
    my $dates        = shift;
    my $start_of_day = shift;

    #return $dates if $start_of_day eq '0';

    for my $date (@$dates) {
        next unless defined $date;

        $date->{splitCount} = 0 unless defined $date->{splitCount};

        #debugDate($date);

        next if $date->{splitCount} > 6;
        my $nextDayStart = breaks_day( $date->{start}, $date->{end}, $start_of_day );
        next if $nextDayStart eq '0';

        # add new entry
        my $entry = {};
        for my $key ( keys %$date ) {
            $entry->{$key} = $date->{$key};
        }
        $entry->{start} = $nextDayStart;
        $entry->{splitCount}++;
        push @$dates, $entry;

#        print STDERR "add $entry->{start}   $entry->{end}   count:$entry->{splitCount}  $entry->{program}-$entry->{series_name}-$entry->{title}\n";

        #modify existing entry
        my $start_date = time::datetime_to_date( $date->{start} );
        $date->{end} = $nextDayStart;
        $date->{splitCount}++;

#        print STDERR "set $date->{start}   $date->{end}   count:$date->{splitCount}  $date->{program}-$date->{series_name}-$date->{title}\n";
    }

    return join_dates( $dates, $start_of_day );
}

# check if event breaks the start of day (e.g. 06:00)
sub breaks_day {
    my $start        = shift;
    my $end          = shift;
    my $start_of_day = shift;

    my $starts    = time::datetime_to_array($start);
    my $startDate = time::array_to_date($starts);
    my $startTime = time::array_to_time($starts);
    $start = $startDate . ' ' . $startTime;

    my $ends    = time::datetime_to_array($end);
    my $endDate = time::array_to_date($ends);
    my $endTime = time::array_to_time($ends);
    $end = $endDate . ' ' . $endTime;

    my $dayStartTime = time::array_to_time($start_of_day);
    my $dayStart     = $startDate . ' ' . $dayStartTime;

    # start before 6:00 of same day
    return $dayStart if ( $start lt $dayStart ) && ( $end gt $dayStart );

    # start before 6:00 of next day
    my $nextDayStart = time::add_days_to_datetime( $dayStart, 1 );

    #$nextDayStart=~s/:00$//;
    return $nextDayStart if ( $start lt $nextDayStart ) && ( $end gt $nextDayStart );

    return 0;
}

# merge events with same seriesId and eventId at 00:00
sub join_dates {
    my $dates        = shift;
    my $start_of_day = shift;

    return $dates if $start_of_day == 0;
    @$dates = sort { $a->{start} cmp $b->{start} } @$dates;

    my $prev_date = undef;
    for my $date (@$dates) {
        next unless defined $date;
        unless ( defined $prev_date ) {
            $prev_date = $date;
            next;
        }
        if (   ( $date->{event_id} == $prev_date->{event_id} )
            && ( $date->{series_id} == $prev_date->{series_id} )
            && ( $date->{start} eq $prev_date->{end} )
            && ( $date->{start} =~ /00\:00\:\d\d/ ) )
        {
            $prev_date->{end} = $date->{end};
            $date = undef;
            next;
        }
        $prev_date = $date;
    }

    my $results = [];
    for my $date (@$dates) {
        next unless defined $date;
        push @$results, $date;
    }

    return $results;
}

sub filterEvents {
    my $events       = shift;
    my $options      = shift;
    my $start_of_day = shift;

    return [] unless defined $options->{from};
    return [] unless defined $options->{till};

    my $dayStartTime  = time::array_to_time($start_of_day);
    my $startDatetime = $options->{from} . ' ' . $dayStartTime;
    my $endDatetime   = $options->{till} . ' ' . $dayStartTime;

    my $results = [];
    for my $date (@$events) {
        next if ( ( $date->{start} ge $endDatetime ) || ( $date->{end} le $startDatetime ) );
        push @$results, $date;
    }
    return $results;
}

sub showEventList {
    my $config        = shift;
    my $permissions   = shift;
    my $params        = shift;
    my $events_by_day = shift;
    my $language      = $params->{language};

    my $rerunIcon   = qq{<img src="image/replay.svg" title="$params->{loc}->{label_rerun}">};
    my $liveIcon    = qq{<img src="image/mic.svg" title="$params->{loc}->{label_live}">};
    my $draftIcon   = qq{<img src="image/draft.svg" title="$params->{loc}->{label_draft}">};
    my $archiveIcon = qq{<img src="image/archive.svg" title="$params->{loc}->{label_archived}">};
    my $playoutIcon    = qq{<img src="image/play.svg">};
    my $processingIcon = qq{<img src="image/processsing.svg">};
    my $preparedIcon   = qq{<img src="image/prepared.svg>};
    my $creoleIcon     = qq{<img src="image/creole.svg>};

    my $out = '';
    $out = qq{
        <div id="event_list">
        <table>
            <thead>
                <tr>
                    <th class="day_of_year">$params->{loc}->{label_day_of_year}</th>
                    <th class="weekday">$params->{loc}->{label_weekday}</th>
                    <th class="start_date">$params->{loc}->{label_start}</th>
                    <th class="start_time">$params->{loc}->{label_end}</th>
                    <th class="series_name">$params->{loc}->{label_series}</th>
                    <th class="series_id">sid</th>
                    <th class="title">$params->{loc}->{label_title}</th>
                    <th class="episode">$params->{loc}->{label_episode}</th>
                    <th class="rerun">$rerunIcon</th>
                    <th class="draft">$draftIcon</th>
                    <th class="live">$liveIcon</th>
                    <th class="playout" title="$params->{loc}->{label_playout}">$playoutIcon</th>
                    <th class="archive">$archiveIcon</th>
                    <th class="project_id">project</th>
                    <th class="studio">studio</th>
                    <th class="creole">wiki format</th>
                 </tr>
            </thead>
            <tbody>
    } if $params->{part} == 0;
    my $i = 1;

    my $scheduled_events = {};
    for my $date ( reverse sort ( keys %$events_by_day ) ) {
        for my $event ( reverse @{ $events_by_day->{$date} } ) {
            next unless defined $event;
            next if defined $event->{grid};
            next if defined $event->{work};
            next if defined $event->{play};

            #schedules with matching date are marked to be hidden in find_errors
            next if defined $event->{hide};
            $event->{project_id} //= $params->{project_id};
            $event->{studio_id}  //= $params->{studio_id};
            $event->{series_id}  = '-1' unless defined $event->{series_id};
            $event->{event_id}   = '-1' unless defined $event->{event_id};
            my $id =
                'event_'
              . $event->{project_id} . '_'
              . $event->{studio_id} . '_'
              . $event->{series_id} . '_'
              . $event->{event_id};

            my $class = 'event';
            $class = $event->{class} if defined $event->{class};
            $class = 'schedule'      if defined $event->{schedule};
            if ( $class =~ /(event|schedule)/ ) {
                $class .= ' scheduled' if defined $event->{scheduled};
                $class .= ' error'     if defined $event->{error};
                $class .= ' no_series'
                  if ( ( $class eq 'event' ) && ( $event->{series_id} eq '-1' ) );

                for my $filter (
                    'rerun', 'archived',           'playout', 'published',
                    'live',  'disable_event_sync', 'draft'
                  )
                {
                    $class .= ' ' . $filter
                      if ( ( defined $event->{$filter} ) && ( $event->{$filter} eq '1' ) );
                }
                $class .= ' preproduced'
                  unless ( ( defined $event->{'live'} ) && ( $event->{'live'} eq '1' ) );
                $class .= ' no_playout'
                  unless ( ( defined $event->{'playout'} ) && ( defined $event->{'playout'} and $event->{'playout'} eq '1' ) );
                $class .= ' no_rerun'
                  unless ( ( defined $event->{'rerun'} ) && ( $event->{'rerun'} eq '1' ) );
            }

            $event->{start}              ||= '';
            $event->{weekday_short_name} ||= '';
            $event->{start_date_name}    ||= '';
            $event->{start_time_name}    ||= '';
            $event->{end_time}           ||= '';
            $event->{series_name}        ||= '';
            $event->{title}              ||= '';
            $event->{user_title}         ||= '';
            $event->{episode}            ||= '';
            $event->{rerun}              ||= '';
            $event->{draft}              ||= '';
            $event->{playout}            ||= '';
            $id                          ||= '';
            $class                       ||= '';

            my $archived = $event->{archived} || '-';
            $archived = '-'          if $archived eq '0';
            $archived = $archiveIcon if $archived eq '1';

            my $live = $event->{live} || '-';
            $live = '-'       if $live eq '0';
            $live = $liveIcon if $live eq '1';

            my $rerun = $event->{rerun} || '-';

            $rerun = " [" . markup::base26( $event->{recurrence_count} + 1 ) . "]"
              if ( defined $event->{recurrence_count} )
              && ( $event->{recurrence_count} ne '' )
              && ( $event->{recurrence_count} > 0 );

            my $draft = $event->{draft} || '0';
            $draft = '-'        if $draft eq '0';
            $draft = $draftIcon if $draft eq '1';

            my $playout = '-';
            if (defined $event->{upload_status}){
                $playout = $processingIcon if $event->{upload_status} ne '';
                $playout = $preparedIcon   if $event->{upload_status} eq 'done';
            }
            $playout = $playoutIcon if $event->{playout} eq '1';

            my $title = $event->{title};
            $title .= ': ' . $event->{user_title} if $event->{user_title} ne '';

            my $other_studio  = $params->{studio_id}  ne $event->{studio_id};
            my $other_project = $params->{project_id} ne $event->{project_id};
            $class.=' predecessor' if $other_project or $other_studio;
            $other_studio  = '<img src="image/globe.svg">' if $other_studio;
            $other_project = '<img src="image/globe.svg">' if $other_project;

            my $file = $event->{file} 
                ? 'playout: ' . $event->{file} =~ s/\'/\&apos;/gr 
                : 'playout';
            my $playout_info = $file // $event->{upload_status} // '';

            my $studio_name = $event->{studio_name} // '-';
            
            my $format = {"markdown" => "-", "creole" => "Lang Belta" }->{$event->{content_format}//''} // 'Lang Belta';
            $out .=
                qq!<tr id="$id" class="$class" start="$event->{start}" >!
              . qq!<td class="day_of_year">!
              . (defined $event->{start} ? time::dayOfYear( $event->{start} ) :'')
              . q!</td>!
              . qq!<td class="weekday">$event->{weekday_short_name},</td>!
              . qq!<td class="start_date" data-text="$event->{start_datetime}">$event->{start_date_name}</td>!
              . qq!<td class="start_time">$event->{start_time_name} - $event->{end_time}</td>!
              . qq!<td class="series_name">$event->{series_name}</td>!
              . qq!<td class="series_id">$event->{series_id}</td>!
              . qq!<td class="title">$title</td>!
              . qq!<td class="episode">$event->{episode}</td>!
              . qq!<td class="rerun">$rerun</td>!
              . qq!<td class="draft">$draft</td>!
              . qq!<td class="live">$live</td>!
              . qq!<td class="playout" title="$playout_info">$playout</td>!
              . qq!<td class="archived">$archived</td>!
              . qq!<td>$event->{project_name} $other_studio</td>!
              . qq!<td>$studio_name $other_studio</td>!
              . qq!<td>$format</td>!
              . qq!</tr>! . "\n";
        }
        $i++;
        if ( $i % 100 == 0 ) {
            print $out;
            $out = '';
        }
    }
    $out .= qq{
                </tbody>
            </table>
        </div>
    } if $params->{part} == 0;

    my $project_id = $params->{project_id};
    my $studio_id  = $params->{studio_id};

    #add handler for events not assigned to series
    if ( ( $params->{studio_id} ne '' ) && ( $params->{studio_id} ne '-1' ) ) {
        my $series = series::get(
            $config,
            {
                project_id => $project_id,
                studio_id  => $studio_id
            }
        );
        $out .= q{<div id="event_no_series" style="display:none">};
        $out .= addEventsToSeries( $series, $params )
          if ( defined $permissions->{assign_series_events} )
          && ( $permissions->{assign_series_events} eq '1' );
        $out .= createSeries($params)
          if ( defined $permissions->{create_series} ) && ( $permissions->{create_series} eq '1' );
        $out .= q{</div>};
    }

    $out .= qq{
            </main>
            <script>
                var region='} . $params->{loc}->{region} . q{';
                var calendarTable=0;
                var label_events='} . $params->{loc}->{label_events} . q{';
                var label_schedule='} . $params->{loc}->{label_schedule} . q{';
                var label_worktime='} . $params->{loc}->{label_worktime} . q{';
                var label_playout='} . $params->{loc}->{label_playout} . q{';
                var label_descriptions='} . $params->{loc}->{label_descriptions} . q{';
                var label_pin='} . $params->{loc}->{label_pin} . q{';
            </script>
        </body>
    </html>
    } if $params->{part} == 0;

    print $out;

}

sub calcCalendarTable {
    my $config        = shift;
    my $permissions   = shift;
    my $params        = shift;
    my $calendar      = shift;
    my $events_by_day = shift;
    my $cal_options   = shift;

    my $start_of_day = $cal_options->{start_of_day};
    my $end_of_day   = $cal_options->{end_of_day};
    my $min_hour     = $cal_options->{min_hour};
    my $max_hour     = $cal_options->{max_hour};
    my $project_id   = $params->{project_id};
    my $studio_id    = $params->{studio_id};
    my $language     = $params->{language};

    #insert time column
    for my $hour ( $min_hour .. $max_hour ) {
        push @{ $events_by_day->{0} },
          {
            start      => sprintf( '%02d:00', $hour % 24 ),
            start_time => sprintf( '%02d:00', $hour ),
            end_time   => sprintf( '%02d:00', $hour + 1 ),
            series_id  => -1,
            event_id   => -1,
            project_id => $project_id,
            studio_id  => $studio_id,
            class      => 'time',
            'time'     => sprintf( '%02d',    $hour % 24 )
          };
    }

    #insert current time
    my $time = '00:00';
    my $date = '';
    if ( time::get_datetime(
        time::time_to_datetime(time()), 
        $config->{date}->{time_zone}
    ) =~ /(\d\d\d\d\-\d\d\-\d\d)[ T](\d\d\:\d\d)/ ) {
        $date = $1;
        $time = $2;
    }

    my $next_time = '00:00';
    if ( time::get_datetime(
        time::time_to_datetime(time()+60),
        $config->{date}->{time_zone}
    ) =~ /(\d\d\d\d\-\d\d\-\d\d)[ T](\d\d\:\d\d)/ ) {
        $next_time = $2;
    }

    unshift @{ $events_by_day->{0} },
      {
        #start      => $time,
        start_time => $time,
        end_time   => $next_time,
        series_id  => -1,
        event_id   => -1,
        project_id => -1,
        studio_id  => -1,
        class      => 'time now',
        'time'     => $time,
      };
    calc_positions( $events_by_day->{0}, $cal_options );

    my $yoffset = $min_hour * $hour_height;
    my @days    = sort keys %$events_by_day;

    $cal_options->{days}          = \@days;
    $cal_options->{yoffset}       = $yoffset;
    $cal_options->{events_by_day} = $events_by_day;
    $cal_options->{date}          = $date;

}

sub printTableHeader {
    my $config      = shift;
    my $permissions = shift;
    my $params      = shift;
    my $cal_options = shift;

    my $days          = $cal_options->{days};
    my $events_by_day = $cal_options->{events_by_day};
    my $yoffset       = $cal_options->{yoffset};
    my $date          = $cal_options->{date};
    my $min_hour      = $cal_options->{min_hour};

    my $project_id = $params->{project_id};
    my $studio_id  = $params->{studio_id};
    my $language   = $params->{language};

    #print row with weekday and date
    my $out = '';

    my $numberOfDays = scalar(@$days);
    my $width        = int( 85 / $numberOfDays );
    $out .= qq!
        <script>
            var days=$numberOfDays;
        </script>
        <style>
            #calendar div.time, 
            #calendar_weekdays div.date, 
            #calendar div.event, 
            #calendar div.schedule, 
            #calendar div.work, 
            #calendar div.play,
            #calendar div.grid {
                width: $width%
            }        
        </style>
    !;

    $out .= q{
        <div id="calendar_weekdays" style="visibility:hidden">
            <table>
                <tbody>
                    <tr>
    };

    my $next_day_found = 0;

    #print navigation and weekday
    my $ypos     = 0;
    my $old_week = undef;
    my $dt       = undef;
    for my $day (@$days) {
        my $events = $events_by_day->{$day};

        if ( $day ne '0' ) {
            $dt = time::get_datetime( $day . 'T00:00:00', $config->{date}->{time_zone} );
            my $week = $dt->week_number();
            if ( ( defined $old_week ) && ( $week ne $old_week ) ) {
                $out .= qq{<td class="week"><div class="week"></div></td>};
            }
            $old_week = $week;
        }

        #header
        $out .= qq{<td>};
        my $event   = $events->[0];
        my $content = '';
        my $class   = 'date';
        if ( $day eq '0' ) {
            $out .= qq{<div id="position"></div></td>};
            next;
        } else {

            #print weekday
            $dt->set_locale($language);
            $content = $dt->day_name() . '<br>';
            $content .= $dt->strftime('%d. %b %Y') . '<br>';
            $content .= time::dayOfYear( $event->{start} ) . '<br>';

            #$class="date";
            if ( ( $day ge $date ) && ( $next_day_found == 0 ) ) {
                $class          = "date today";
                $next_day_found = 1;
            }
        }

        #insert date name
        my $hour = $min_hour;
        my $date = $day;
        $event = {
            start      => sprintf( '%02d:00', $hour % 24 ),
            start_time => sprintf( '%02d:00', $hour ),
            end_time   => sprintf( '%02d:30', $hour + 1 ),
            project_id => $project_id,
            studio_id  => $studio_id,
            content    => $content,
            class      => $class,
            date       => $date
        };

        calc_positions( [$event], $cal_options );
        $out .= print_event( $params, $event, $ypos, $yoffset, $yzoom );

        $out .= '</td>';
    }
    $out .= q{
                    </tr>
                </tbody>
            </table>
        </div>
    };
    print $out;
}

sub printTableBody {
    my $config      = shift;
    my $permissions = shift;
    my $params      = shift;
    my $cal_options = shift;

    my $days          = $cal_options->{days};
    my $events_by_day = $cal_options->{events_by_day};
    my $yoffset       = $cal_options->{yoffset};

    my $project_id = $params->{project_id};
    my $studio_id  = $params->{studio_id};

    if ( scalar( @{$days} ) == 0 ) {
        uac::print_info("no dates found at the selected time span");
    }

    my $out = q{
        <div id="calendar" style="display:none">
            <table>
                <tbody>
                    <tr>
    };

    #print events with weekday and date
    my $ypos     = 1;
    my $dt       = undef;
    my $old_week = undef;

    for my $day (@$days) {
        my $events = $events_by_day->{$day};

        if ( $day ne '0' ) {
            $dt = time::get_datetime( $day . 'T00:00:00', $config->{date}->{time_zone} );
            my $week = $dt->week_number();
            if ( ( defined $old_week ) && ( $week ne $old_week ) ) {
                $out .= qq{<td class="week"><div class="week"></div></td>};
            }
            $old_week = $week;
        }

        $out .= qq{<td>};    # width="$width">};

        for my $event (@$events) {
            my $content = '';
            if ( ( defined $event->{series_name} ) && ( $event->{series_name} ne '' ) ) {
                $event->{series_name} = $params->{loc}->{single_event}
                  if $event->{series_name} eq '' || $event->{series_name} eq '_single_';
                $content = '<b>' . $event->{series_name} . '</b><br>';
            }

            if ( ( defined $event->{title} ) && ( defined $event->{title} ne '' ) ) {
                $content .= $event->{title};
                unless ( $event->{title} =~ /\#\d+/ ) {
                    $content .= ' #' . $event->{episode}
                      if ( ( defined $event->{episode} ) && ( $event->{episode} ne '' ) );
                }
            }
            $content = $event->{start} if $day eq '0';
            $event->{project_id} = $project_id unless defined $event->{project_id};
            $event->{studio_id}  = $studio_id  unless defined $event->{studio_id};
            $event->{content}    = $content
              unless ( ( defined $event->{class} ) && ( $event->{class} eq 'time now' ) );
            $event->{class} = 'event' if $day ne '0';
            $event->{class} = 'grid' if ( ( defined $event->{grid} ) && ( $event->{grid} == 1 ) );
            $event->{class} = 'schedule'
              if ( ( defined $event->{schedule} ) && ( $event->{schedule} == 1 ) );
            $event->{class} = 'work' if ( ( defined $event->{work} ) && ( $event->{work} == 1 ) );
            $event->{class} = 'play' if ( ( defined $event->{play} ) && ( $event->{play} == 1 ) );

            if ( $event->{class} eq 'event' ) {
                $event->{content} .= '<br><span class="weak">';
                $event->{content} .= audio::formatFile($event->{file}, $event->{event_id});
                $event->{content} .= audio::formatDuration(
                    $event->{duration},
                    $event->{event_duration},
                    sprintf( "%d min", ( $event->{duration} + 30 ) / 60 ),
                    sprintf( "%d s", $event->{duration} )
                  )
                  . ' '
                  if defined $event->{duration};
                $event->{content} .= audio::formatLoudness( $event->{rms_left}, 'L: ' ,'round') . ' '
                  if defined $event->{rms_left};
                $event->{content} .= audio::formatLoudness( $event->{rms_right}, 'R: ','round' )
                  if defined $event->{rms_right};
                #$event->{content} .= formatBitrate( $event->{bitrate} ) if defined $event->{bitrate};
                $event->{content} .= '</span>';
            }

            $out .= print_event( $params, $event, $ypos, $yoffset, $yzoom );

            $ypos++;
        }
        $out .= '</td>';
    }
    $out .= q{
                                </tr>
                            </tbody>
                        </table>
                       </div><!--table-->
    };

    print $out;
}

sub printSeries {
    my $config      = shift;
    my $permissions = shift;
    my $params      = shift;
    my $cal_options = shift;

    my $project_id = $params->{project_id};
    my $studio_id  = $params->{studio_id};

    my $series = series::get(
        $config,
        {
            project_id => $project_id,
            studio_id  => $studio_id
        }
    );

    my $out = '';

    #add schedule entry for series
    if (   ( defined $permissions->{update_schedule} )
        && ( $permissions->{update_schedule} eq '1' )
        && ( scalar(@$series) > 0 ) )
    {
        $out .= q{<div id="series" style="display:none">};
        $out .= addSeries( $series, $params );
        $out .= q{</div>};
    }

    if ( ( $params->{studio_id} ne '' ) && ( $params->{studio_id} ne '-1' ) ) {
        $out .= q{<div id="event_no_series" style="display:none">};
        $out .= addEventsToSeries( $series, $params )
          if ( ( defined $permissions->{assign_series_events} )
            && ( $permissions->{assign_series_events} eq '1' ) );
        $out .= createSeries($params)
          if ( ( defined $permissions->{create_series} )
            && ( $permissions->{create_series} eq '1' ) );
        $out .= q{</div>};
    }

    $out .= q{
        <div id="no_studio_selected" style="display:none">
            } . $params->{loc}->{label_no_studio_selected} . q{
        </div>
    };
    print $out;
}

sub printJavascript {
    my $config      = shift;
    my $permissions = shift;
    my $params      = shift;
    my $cal_options = shift;

    my $startOfDay = $cal_options->{min_hour} % 24;

    #print STDERR "js: ".$cal_options->{min_hour}." ".$startOfDay."\n";
    my $out = q{
        <script>
            var region='} . $params->{loc}->{region} . q{';
            var calendarTable=1;
            var startOfDay=} . $startOfDay . q{;
            var label_events='} . $params->{loc}->{label_events} . q{';
            var label_schedule='} . $params->{loc}->{label_schedule} . q{';
            var label_worktime='} . $params->{loc}->{label_worktime} . q{';
            var label_descriptions='} . $params->{loc}->{label_descriptions} . q{';
            var label_playout='} . $params->{loc}->{label_playout} . q{';
            var label_pin='} . $params->{loc}->{label_pin} . q{';
        </script>
    };
    print $out;
}

#TODO: Javascript

sub addCalendarButton {
    my $params   = shift;
    my $calendar = shift;

    #add calendar button
    my $content = qq{
        <div id="previous_month"><a id="previous">&laquo;</a></div>
        <div id="selectDate" data-toggle>
            <input id="start_date" data-input/>
            <div id="current_date">$calendar->{month} $calendar->{year}</div>
        </div>
        <div id="next_month"><a id="next">&raquo;</a></div>
    };
    return $content;
}

sub addSeries {
    my $series = shift;
    my $params = shift;

    return unless defined $series;
    return unless scalar @$series > 0;

    my $out = '';
    $out .= q{
        <table>
          <tr>
                <td>} . $params->{loc}->{label_series} . q{</td>
                <td><select id="series_select" name="series_id">
    };

    for my $serie (@$series) {
        my $id       = $serie->{series_id}   || -1;
        my $duration = $serie->{duration}    || 0;
        my $name     = $serie->{series_name} || '';
        my $title    = $serie->{title}       || '';
        $name = $params->{loc}->{single_events} if $serie->{has_single_events} eq '1';
        $title = ' - ' . $title if $title ne '';

        $out .=
            '<option value="'
          . $id
          . '" duration="'
          . $duration . '">'
          . $name
          . $title
          . '</option>' . "\n";
    }

    $out .= q{
                    </select>
                    </td>
                </tr>                
            <tr>
                <td>} . $params->{loc}->{label_date} . q{</td>
                <td><input id="series_date" name="start_date" value=""></td>
            </tr>
            <tr>
                <td>} . $params->{loc}->{label_duration} . q{</td>
                <td><input id="series_duration" value="60"></td>
            </tr>
            </table>
        </div>
    };
    return $out;

}

# create form to add events to series (that are not assigned to series, yet)
sub addEventsToSeries {
    my $series = shift;
    my $params = shift;

    return unless defined $series;
    return unless scalar @$series > 0;
    my $project_id = $params->{project_id};
    my $studio_id  = $params->{studio_id};

    my $out = '';
    $out .= qq{
        <div>
            <b>} . $params->{loc}->{label_assign_event_series} . qq{</b>
            <form id="assign_series_events" method="post" action="series.cgi">
                <input type="hidden" name="project_id" value="$project_id">
                <input type="hidden" name="studio_id" value="$studio_id">
                <input type="hidden" name="event_id">
                <table>
                    <tr>
                        <td>} . $params->{loc}->{label_series} . qq{</td>
                        <td><select id="select_series" name="series_id">
    };

    for my $serie (@$series) {
        my $id       = $serie->{series_id}   || -1;
        my $duration = $serie->{duration}    || '';
        my $name     = $serie->{series_name} || '';
        my $title    = $serie->{title}       || '';
        $name = $params->{loc}->{single_events} if $serie->{has_single_events} == 1;
        $title = ' - ' . $title if $title ne '';
        $out .=
            '<option value="'
          . $id
          . '" duration="'
          . $duration . '">'
          . $name
          . $title
          . '</option>' . "\n";
    }

    $out .= q{
                        </select>
                        </td>
                    </tr>                
                    <tr><td></td>
                        <td>
                            <button type="submit" name="action" value="assign_event">}
      . $params->{loc}->{button_assign_event_series} . q{</button>
                        </td>
                    </tr>
                </table>
            </form>
        </div>
    };
    return $out;
}

# insert form to create series on not assigned events
sub createSeries {
    my $params = shift;

    my $project_id = $params->{project_id};
    my $studio_id  = $params->{studio_id};
    return qq{
        <div>
            <b>} . $params->{loc}->{label_create_series} . qq{</b>
            <form method="post" action="series.cgi">
                <input type="hidden" name="project_id" value="$project_id">
                <input type="hidden" name="studio_id"  value="$studio_id">
                <table>
                    <tr><td class="label">}
      . $params->{loc}->{label_name} . qq{</td>     <td><input name="series_name"></td></tr>
                    <tr><td class="label">}
      . $params->{loc}->{label_title} . qq{</td>     <td><input name="title"></td></tr>
                    <tr><td></td>
                        <td>
                            <button type="submit" name="action" value="create">}
      . $params->{loc}->{button_create_series} . qq{</button>
                        </td>
                    </tr>
                </table>
            </form>
        </div>
    };
}

sub print_event {
    my $params  = shift;
    my $event   = shift;
    my $ypos    = shift;
    my $yoffset = shift;
    my $yzoom   = shift;

    $event->{project_id} = '-1' unless defined $event->{project_id};
    $event->{studio_id}  = '-1' unless defined $event->{studio_id};
    $event->{series_id}  = '-1' unless defined $event->{series_id};
    $event->{event_id}   = '-1' unless defined $event->{event_id};

    my $id =
        'event_'
      . $event->{project_id} . '_'
      . $event->{studio_id} . '_'
      . $event->{series_id} . '_'
      . $event->{event_id};
    $id = 'grid_' . $event->{project_id} . '_' . $event->{studio_id} . '_' . $event->{series_id}
      if defined $event->{grid};
    $id = 'work_' . $event->{project_id} . '_' . $event->{studio_id} . '_' . $event->{schedule_id}
      if defined $event->{work};
    $id = 'play_' . $event->{project_id} . '_' . $event->{studio_id} if defined $event->{play};

    my $class = $event->{class} || '';
    my $showIcons = 0;
    if ( $class =~ /(event|schedule)/ ) {
        $class .= ' scheduled' if defined $event->{scheduled};
        $class .= ' no_series' if ( ( $class eq 'event' ) && ( $event->{series_id} eq '-1' ) );
        $class .= " error x$event->{error}" if defined $event->{error};

        for my $filter ( 'rerun', 'archived', 'playout', 'published', 'live', 'disable_event_sync',
            'draft' )
        {
            $class .= ' ' . $filter
              if ( ( defined $event->{$filter} ) && ( $event->{$filter} eq '1' ) );
        }
        $class .= ' preproduced'
          unless ( ( defined $event->{'live'} ) && ( $event->{'live'} eq '1' ) );
        $class .= ' no_playout'
          unless ( ( defined $event->{'playout'} ) && ( $event->{'playout'} eq '1' ) );
        $class .= ' no_rerun'
          unless ( ( defined $event->{'rerun'} ) && ( $event->{'rerun'} eq '1' ) );
        $showIcons = 1;
    }

    my $ystart = $event->{ystart} - $yoffset;
    my $yend   = $event->{yend} - $yoffset - 10;

    $ystart = int( $ystart * $yzoom );
    $yend   = int( $yend * $yzoom );
    my $height = $yend - $ystart + 1;

    if ( $ypos > 0 ) {
        $height = q{height:} . ($height) . 'px;';
    } else {
        $height = '';
    }

    my $content = '<div class="header">';
    $content .=  qq!<img class="icon" src="!.($event->{series_icon_url}).q!">! if $class=~/event/;
    $content .= $event->{content} || '';
    $content .= '</div>';

    if ( $class =~ /schedule/ ) {
        my $frequency = getFrequency($event);
        $content .= "<br>($frequency)" if defined $frequency;
    }

    my $attr = '';
    if ( $class =~ /play/ ) {
        $attr .= ' rms="' . $event->{rms_image} . '"' if defined $event->{rms_image};
        $attr .= ' start="' . $event->{start} . '"'   if defined $event->{start};
    }

    if ( defined $event->{upload} ) {
        $content .= '<br>uploading <progress max="10" ></progress> ';
    }

    $content .= q{<div class="scrollable">};
    $content .= q{<div class="excerpt">}.$event->{excerpt}.q{</div>} if defined $event->{excerpt};
    $content .= q{<div class="excerpt">}.$event->{html_topic}.q{</div>}   if defined $event->{topic};
    $content .= q{</div>};

    if ($showIcons) {
        my $attr =  { map { $_ => undef } split( /\s+/, $class) };
        
        my $file = $event->{file} 
            ? 'playout: ' . $event->{file} =~ s/\'/\&apos;/gr 
            : 'playout';

        my $playoutClass    = qq{<img src="image/play.svg">};
        my $processingClass = qq{<img src="image/processing.svg">};
        my $preparedClass   = qq{<img src="image/prepare.svg">};
        my $icons = '';
        
        if ( exists $attr->{event} ){
            my $playout = '';
            if (exists $attr->{upload_status}){
                $playout = $processingClass if $attr->{upload_status} ne '';
                $playout = $preparedClass   if $attr->{upload_status} eq 'done';
            }
            $playout = $playoutClass    if exists $attr->{playout};
            $icons.='<img src="image/mic.svg" title="live"/>'
                if exists($attr->{live}) && exists($attr->{no_rerun});
            $icons.='<img src="image/mic_off.svg" title="preproduced"/>'
                if exists($attr->{preproduced}) && exists($attr->{no_rerun});
            $icons.='<img src="image/replay.svg" title="rerun"/>'
                if exists $attr->{rerun};
            $icons.=qq{<img src="image/play.svg" title="$file" onmouseenter="console.log('$file');"/>}
                if $playout;
            $icons.='<img src="image/archive.svg" title="archived"/>'
                if exists $attr->{archived};
        }

        $content = qq{<div class="text" style="$height">$content</div><div class="icons">$icons</div>};
    }

    my $time = '';
    $time = qq{ time="$event->{time}"} if $class =~ m/time/;

    my $date = '';
    $date = qq{ date="$event->{date}"} if $class =~ m/date/;    

    my $line = q{<div } . qq{class="$class" id="$id"};
    $line .= qq{ style="} . $height . q{top:} . $ystart . q{px;"};
    $line .= $time . $date . qq{ $attr};
    $line .= qq{>$content</div>};
    $line .= "\n";
    return $line;
}

sub getFrequency {
    my $event = shift;

    my $period_type = $event->{period_type};
    return undef unless defined $period_type;
    return undef if $period_type ne 'days';

    my $frequency = $event->{frequency};
    return undef unless defined $frequency;
    return undef unless $frequency > 0;

    if ( ( $frequency >= 7 ) && ( ( $frequency % 7 ) == 0 ) ) {
        $frequency /= 7;
        return '1 week' if $frequency == 1;
        return $frequency .= ' weeks';
    }

    return '1 day' if $frequency == 1;
    return $frequency .= ' days';
}

sub calc_positions {
    my $events      = $_[0];
    my $cal_options = $_[1];

    my $start_of_day = $cal_options->{start_of_day};

    for my $event (@$events) {
        my ( $start_hour, $start_min ) = getTime( $event->{start_time} );
        my ( $end_hour,   $end_min )   = getTime( $event->{end_time} );

        $start_hour += 24 if $start_hour < $start_of_day;
        $end_hour   += 24 if $end_hour < $start_of_day;
        $end_hour   += 24 if $start_hour > $end_hour;
        $end_hour   += 24 if ( $start_hour == $end_hour ) && ( $start_min == $end_min );

        $event->{ystart} = $start_hour * 60 + $start_min;
        $event->{yend}   = $end_hour * 60 + $end_min;
    }
}

sub find_errors {
    my $events = $_[0];

    for my $event (@$events) {
        next if defined $event->{grid};
        next if defined $event->{work};
        next if defined $event->{play};
        next if ( defined $event->{draft} ) && ( $event->{draft} == 1 );
        next unless defined $event->{ystart};
        next unless defined $event->{yend};
        $event->{check_errors} = 1;
    }

    #check next events
    for my $i ( 0 .. scalar(@$events) - 1 ) {
        my $event = $events->[$i];
        next unless defined $event->{check_errors};

        #look for conflicts with next 5 events of day
        my $min_index = $i + 1;
        next if $min_index >= scalar @$events;
        my $max_index = $i + 8;
        $max_index = scalar(@$events) - 1 if $max_index >= (@$events);
        for my $j ( $min_index .. $max_index ) {
            my $event2 = $events->[$j];
            next unless defined $event2->{check_errors};

            #mark events if same start,stop,series_id, one is schedule one is event
            if (   ( defined $event->{series_id} )
                && ( defined $event2->{series_id} )
                && ( $event->{series_id} == $event2->{series_id} ) )
            {
                if (   ( $event->{ystart} eq $event2->{ystart} )
                    && ( $event->{yend} eq $event2->{yend} ) )
                {
                    if ( ( defined $event->{schedule} ) && ( !( defined $event2->{schedule} ) ) ) {
                        $event->{hide}       = 1;
                        $event2->{scheduled} = 1;
                        next;
                    }
                    if ( ( !( defined $event->{schedule} ) ) && ( defined $event2->{schedule} ) ) {
                        $event->{scheduled} = 1;
                        $event2->{hide}     = 1;
                        next;
                    }
                } elsif ( ( $event->{ystart} >= $event2->{ystart} )
                    && ( $event->{scheduled} == 1 )
                    && ( $event2->{scheduled} == 1 ) )
                {
                    #subsequent schedules
                    $event->{error}++;
                    $event2->{error} = 1 unless defined $event2->{error};
                    $event2->{error}++;
                    next;
                }
            } elsif ( $event->{ystart} >= $event2->{ystart} ) {

                #errors on multiple schedules or events
                $event->{error}++;
                $event2->{error} = 1 unless defined $event2->{error};
                $event2->{error}++;
            }
        }
    }

    #remove error tags from correctly scheduled entries (subsequent entries with same series id)
    for my $event (@$events) {
        delete $event->{error}
          if (
            ( defined $event->{error} )
            && (   ( ( defined $event->{scheduled} ) && ( $event->{scheduled} == 1 ) )
                || ( ( defined $event->{hide} ) && ( $event->{hide} == 1 ) ) )
          );
    }
}

sub printToolbar {
    my $config   = shift;
    my $params   = shift;
    my $calendar = shift;

    my $today = time::time_to_date();

    my $toolbar = '<div id="toolbar">';

    $toolbar .= addCalendarButton( $params, $calendar );
    $toolbar .= qq{<button id="setToday">} . $params->{loc}->{button_today} . qq{</button>};

    #ranges
    my $ranges = {
        $params->{loc}->{label_month}   => 'month',
        $params->{loc}->{label_4_weeks} => '28',
        $params->{loc}->{label_2_weeks} => '14',
        $params->{loc}->{label_1_week}  => '7',
        $params->{loc}->{label_day}     => '1',
    };
    $toolbar .= qq{
        <select id="range" name="range" onchange="reloadCalendar()" value="$params->{range}">
    };

    #    my $options=[];
    for my $range (
        $params->{loc}->{label_month},   $params->{loc}->{label_4_weeks},
        $params->{loc}->{label_2_weeks}, $params->{loc}->{label_1_week},
        $params->{loc}->{label_day}
      )
    {
        my $value = $ranges->{$range} || '';
        $toolbar .= qq{<option name="$range" value="$value">} . $range . '</option>';
    }
    $toolbar .= q{
        </select>
    };

    # start of day
    my $day_start = $params->{day_start} || '';
    $toolbar .= qq{
        <select id="day_start" name="day_start" onchange="updateDayStart();reloadCalendar()" value="$day_start">
    };
    for my $hour ( 0 .. 24 ) {
        my $selected = '';
        $selected = 'selected="selected"' if $hour eq $day_start;
        $toolbar .= qq{<option value="$hour">} . sprintf( "%02d:00", $hour ) . '</option>';
    }
    $toolbar .= q{
        </select>
    };

    #filter
    my $filter = $params->{filter} || '';
    $toolbar .= qq{
        <select id="filter" name="filter" onchange="reloadCalendar()">
    };

    for my $filter (
        'no markup', 'conflicts', 'rerun', 'archived',
        'playout',   'published', 'live',  'disable_event_sync',
        'draft'
      )
    {
        my $key = $filter;
        $key =~ s/ /_/g;

        $toolbar .=
          qq{<option value="$filter">} . $params->{loc}->{ 'label_' . $key } . '</option>';
    }

    $toolbar .= q{
        </select>
    };

    #search
    $toolbar .= qq{
        <form class="search">
            <input type="hidden" name="project_id" value="$params->{project_id}">
            <input type="hidden" name="studio_id" value="$params->{studio_id}">
            <input type="hidden" name="date"      value="$params->{date}">
            <input type="hidden" name="list"      value="1">
            <input class="search" name="search" value="$params->{search}" placeholder="}
      . $params->{loc}->{button_search} . qq{">
            <button type="submit" name="action" value="search">}
      . $params->{loc}->{button_search} . qq{</button>
        </form>
    };

    #
    $toolbar .= qq{
        <button id="editSeries">} . $params->{loc}->{button_edit_series} . qq{</button>
    } if $params->{list} == 1;

    $toolbar .= qq{
        </div>
    };

    print $toolbar;
}

sub getTime {
    my $time = shift;
    if ( $time =~ /^(\d\d)\:(\d\d)/ ) {
        return ( $1, $2 );
    }
    return ( -1, -1 );
}

sub getCalendar {
    my $config   = shift;
    my $params   = shift;
    my $language = shift;

    my $from_date = getFromDate( $config, $params );
    my $till_date = getTillDate( $config, $params );
    my $range = $params->{range};

    my $previous = '';
    my $next     = '';
    if ( $range eq 'month' ) {
        $previous =
          time::get_datetime( $from_date, $config->{date}->{time_zone} )->subtract( months => 1 )
          ->set_day(1)->date();
        $next = time::get_datetime( $from_date, $config->{date}->{time_zone} )->add( months => 1 )
          ->set_day(1)->date();
    } else {
        $previous = time::get_datetime( $from_date, $config->{date}->{time_zone} )
          ->subtract( days => $range )->date();
        $next =
          time::get_datetime( $from_date, $config->{date}->{time_zone} )->add( days => $range )
          ->date();
    }
    my ( $year, $month, $day ) = split( /\-/, $from_date );
    my $monthName = time::getMonthNamesShort($language)->[ $month - 1 ] || '';

    return {
        from_date     => $from_date,
        till_date     => $till_date,
        next_date     => $next,
        previous_date => $previous,
        month         => $monthName,
        year          => $year
    };

}

sub getFromDate {
    my $config = shift;
    my $params = shift;

    if ( $params->{from_date} ne '' ) {
        return $params->{from_date};
    }
    my $date = $params->{date};
    if ( $date eq '' ) {
        $date = DateTime->now( time_zone => $config->{date}->{time_zone} )->date();
    }

    if ( $params->{range} eq '28' ) {

        #get start of 4 week period
        $date = time::get_datetime( $date, $config->{date}->{time_zone} )->truncate( to => 'week' )
          ->ymd();
    }
    if ( $params->{range} eq 'month' ) {

        #get first day of month
        return time::get_datetime( $date, $config->{date}->{time_zone} )->set_day(1)->date();
    }

    #get date
    return time::get_datetime( $date, $config->{date}->{time_zone} )->date();
}

sub getTillDate {
    my $config = shift;
    my $params = shift;
    if ( $params->{till_date} ne '' ) {
        return $params->{till_date};
    }
    my $date = $params->{date} || '';
    if ( $date eq '' ) {
        $date = DateTime->now( time_zone => $config->{date}->{time_zone} )->date();
    }
    if ( $params->{range} eq '28' ) {
        $date = time::get_datetime( $date, $config->{date}->{time_zone} )->truncate( to => 'week' )
          ->ymd();
    }
    if ( $params->{range} eq 'month' ) {

        #get last day of month
        return time::get_datetime( $date, $config->{date}->{time_zone} )->set_day(1)
          ->add( months => 1 )->subtract( days => 1 )->date();
    }

    #add range to date
    return time::get_datetime( $date, $config->{date}->{time_zone} )
      ->add( days => $params->{range} )->date();
}

sub getSeriesEvents {
    my $config  = shift;
    my $request = shift;
    my $options = shift;
    my $params  = shift;

    #get events by series id
    my $series_id = $request->{params}->{checked}->{series_id};
    if ( defined $series_id ) {
        my $events = series::get_events( $request->{config}, $options );
        return $events;
    }

    #get events (directly from database to get the ones, not assigned, yet)
    delete $options->{studio_id};
    delete $options->{project_id};
    $options->{recordings} = 1;

    my $request2 = {
        params => {
            checked => events::check_params( $config, $options )
        },
        config      => $request->{config},
        permissions => $request->{permissions}
    };
    $request2->{params}->{checked}->{published} = 'all';
    $request2->{params}->{checked}->{draft} = '1' if $params->{list} == 1;

    my $events = events::get( $config, $request2 );

    series::add_series_ids_to_events( $request->{config}, $events );

    my $studios = studios::get(
        $request->{config},
        {
            project_id => $options->{project_id}
        }
    );
    my $studio_id_by_location = {};
    for my $studio (@$studios) {
        $studio_id_by_location->{ $studio->{location} } = $studio->{id};
    }

    for my $event (@$events) {
        $event->{project_id} = $options->{project_id} unless defined $event->{project_id};
        $event->{studio_id} = $studio_id_by_location->{ $event->{location} }
          unless defined $event->{studio_id};
    }

    return $events;
}

sub check_params {
    my $config = shift;
    my $params = shift;

    my $checked  = {
        user => $config->{user}
    };
    my $template = '';
    $checked->{template} = template::check( $config, $params->{template}, 'series' );

    #numeric values
    $checked->{part}     = 0;
    $checked->{list}     = 0;
    $checked->{open_end} = 1;
    entry::set_numbers( $checked, $params, [
        'id',      'project_id', 'studio_id', 'default_studio_id',
        'user_id', 'series_id',  'event_id',  'part',
        'list',    'day_start',  'open_end'
      ]);

    my $start = user_day_start::get( $config, {
        user       => $checked->{user},
        project_id => $checked->{project_id},
        studio_id  => $checked->{studio_id}
    });
    print  STDERR "read start $start\n";
    $checked->{day_start} = $start->{day_start} if $start;
    $checked->{day_start} = $config->{date}->{day_starting_hour}
      unless defined $checked->{day_start};
    $checked->{day_start} %= 24;

    if ( defined $checked->{studio_id} ) {

        # a studio is selected, use the studio from parameter
        $checked->{default_studio_id} = $checked->{studio_id};
    } elsif ( ( defined $params->{studio_id} ) && ( $params->{studio_id} eq '-1' ) ) {

        # all studios selected, use -1
        $checked->{studio_id} = -1;
    } else {

        # no studio given, use default studio
        $checked->{studio_id} = $checked->{default_studio_id};
    }

    for my $param ('expires') {
        $checked->{$param} = time::check_datetime( $params->{$param} );
    }

    #scalars
    $checked->{search} = '';
    $checked->{filter} = '';

    for my $param ( 'date', 'from_date', 'till_date' ) {
        $checked->{$param} = time::check_date( $params->{$param} );
    }

    entry::set_strings( $checked, $params, [
        'search', 'filter', 'range',
        'series_name', 'title',    'excerpt', 'content',
        'program',     'image',   'user_content'
      ]);

    $checked->{action} = entry::element_of( $params->{action}, 
        [ 'add_user', 'remove_user', 'delete', 'save', 'details', 'show', 'edit_event', 'save_event' ]
    );

    return $checked;
}

