<%pre>

#include <livefeatures.h>
#include <setup.h>
#include <tools.h>
#include <epg_events.h>
#include <epgsearch.h>

using namespace vdrlive;

static const size_t maximumTruncShortDescrLength = 100;   // characters for the short description in the "boxes"
static const size_t maximumTruncDescriptionLength = 150;  // characters for the description in the "boxes"

</%pre>
<%args>
type = "now";
std::string mode;
std::string attime;
std::string fixtime;
</%args>
<%session scope="global">
bool logged_in(false);
</%session>

<# scope="page" should be enough but does not work with tntnet 3.0 #>
<%request scope="global">
std::string current_type;
std::string current_mode;
std::string current_attime;
std::string current_fixtime;
std::string current_displaytime;
</%request>

<%include>page_init.eh</%include>
<%cpp>
reply.setHeader (tnt::httpheader::cacheControl, "no-store, no-cache, max-age=0, must-revalidate, proxy-revalidate", true);
if (!logged_in && LiveSetup().UseAuth()) {
  cToSvConcat<0> targetUrl = "/login.html?redirect=";
  targetUrl.appendUrlEscaped(request.getQuery());
  return reply.redirect(targetUrl.data());
}


std::string head;
time_t seektime = 0;
std::string displaytime;
std::string headTime;
std::string headDate;

if (mode.empty())
  mode = LiveSetup().GetLastWhatsOnListMode();
else
  LiveSetup().SetLastWhatsOnListMode(mode);

current_type = type;
current_mode = mode;
current_attime = attime;
current_fixtime = fixtime;

char const * timeFormat = tr("%I:%M %p");
char const * dateFormat = tr("%A, %x");
if (mode == "detail") {
  dateFormat = tr("%a, %x");
}

if (type == "now") {
  headTime = std::string(cToSvDateTime(timeFormat, time(0)));
  headDate = std::string(cToSvDateTime(dateFormat, time(0)));
  head = std::string(tr("What's running on")) + " " + headDate + " " + tr("at") + " " + headTime;
} else if (type == "next") {
  headTime = std::string(cToSvDateTime(timeFormat, time(0) + 3600));
  headDate = std::string(cToSvDateTime(dateFormat, time(0) + 3600));
  head = tr("What's on next?");
} else if (type == "at") {
  if (attime != "")
    displaytime = ExpandTimeString(attime);
  else if (fixtime != "")
    displaytime = ExpandTimeString(fixtime);
  current_displaytime = displaytime;

  seektime = GetTimeT(displaytime);
  if (seektime - time(0) + 3600 < 0) // if wanted time is past more then 1h, then use tomorrow
    seektime += SECSINDAY;
  headTime = std::string(cToSvDateTime(timeFormat, seektime));
  headDate = std::string(cToSvDateTime(dateFormat, seektime));
  head = std::string(tr("What's running on")) + " " + headDate + " " + tr("at") + std::string(" ") + headTime;
} else if (type == "favs") {
  head = tr("Favorites");
}

</%cpp>
<& pageelems.doc_type &>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>VDR-Live - <$ head $></title>
    <& pageelems.stylesheets &>
    <& pageelems.ajax_js &>
    <& pageelems.create_html_js &>
    <script type="text/javascript"><!--
    function showtime(selection)
    {
      if (selection.options[selection.selectedIndex].value != "")
        window.location.href = "whats_on.html?type=at&mode=<$ current_mode $>&fixtime=" + selection.options[selection.selectedIndex].value;
    }
    function showspectime(selection)
    {
      if (selection.value != "")
        window.location.href = "whats_on.html?type=at&mode=<$ current_mode $>&attime=" + selection.value;
    }
    //--></script>
  </head>
  <body onpagehide="saveScrollPosition('content')" onpageshow="restoreScrollPosition()">
    <& pageelems.logo &>
    <& menu active=("whats_on") component=("whats_on.whats_on_actions")>
    <div id="content">
      <div class="spacebar"><# spacer with fade-out effect #></div>
<%cpp>
  std::vector<EpgInfo> eventList;

  // collect the broadcasts
  if (type != "favs")
  {
    {
      LOCK_CHANNELS_READ;
      LOCK_SCHEDULES_READ;
      for (const cChannel *Channel = Channels->First(); Channel && Channel->Number() <= LiveSetup().GetLastChannel(); Channel = Channels->Next(Channel)) {
        if (Channel->GroupSep()) {
          continue;
        }
        const cSchedule *Schedule = Schedules->GetSchedule(Channel);
        if (!Schedule) {
          continue;
        }
        const cEvent *Event = nullptr;
        if (type == "now")
          Event = Schedule->GetPresentEvent();
        else if (type == "next")
          Event = Schedule->GetFollowingEvent();
        else if (type == "at")
          Event = Schedule->GetEventAround(seektime);

        if (!Event && !LiveSetup().GetShowChannelsWithoutEPG()) continue;

        eventList.emplace_back(Channel, Event);
      }
    }
  }
  else // get favorite broadcasts from epgsearch
  {
    SearchResults results;
    SearchTimers timers;  // this class "automatically" receives the data from epgsearch
    for (SearchTimers::iterator timer = timers.begin(); timer != timers.end(); ++timer) {
      if (!timer->UseInFavorites()) continue;
      SearchResults curresults;
      curresults.GetByID(timer->Id());
      results.merge(curresults);
    }
    time_t now = time(NULL);
    for (SearchResults::iterator result = results.begin(); result != results.end(); ++result) {
      long diff = result->StartTime() - now;
      if (labs(diff) >= 24*60*60) continue; // skip broadcasts more than a day away
      LOCK_CHANNELS_READ;
      LOCK_SCHEDULES_READ;
      const cChannel* Channel = result->GetChannel(Channels);
      if (!Channel) continue;
      eventList.emplace_back(Channel, result->GetEvent(Channel, Schedules) );
    }
  }

  // display broadcasts
  if (eventList.size() > 0 && mode == "detail") {
</%cpp>
      <# we use a flexbox, because the first row of floats have corrupt box headers #>
      <div class="event_collection">
% } else {
      <table class="listing whats_on" cellspacing="0" cellpadding="0">
        <tr class="head">
          <td colspan="4"><div class="boxheader"><div class="caption"><$ head $></div></div></td>
        </tr>
<%cpp>
  }
  for(std::vector<EpgInfo>::size_type i = 0; i < eventList.size(); ++i) {
    EpgInfoPtr epgEvent = &(eventList[i]);

    bool shortDescrTruncated = false;
    bool descriptionTruncated = false;
    cToSvConcat longDescription;
    if (!epgEvent->LongDescr().empty()) {
      longDescription << "<p>";
      AppendHtmlEscapedAndCorrectNonUTF8(longDescription, StringWordTruncate(epgEvent->LongDescr(), LiveSetup().GetMaxTooltipChars(), descriptionTruncated), "</p><p>");
      if (descriptionTruncated) longDescription << " ...";
      longDescription << "</p>";
    }
    longDescription << "<p class=\"click-details\">" << tr("Click to view details") << "</p>";

    std::string truncShortDescr(StringWordTruncate(epgEvent->ShortDescr(), maximumTruncShortDescrLength, shortDescrTruncated));
    std::string truncDescription(StringWordTruncate(epgEvent->LongDescr(), maximumTruncDescriptionLength, descriptionTruncated));
    int chNumber = epgEvent->ChannelNumber();

    std::string startTime(epgEvent->StartTime(tr("%I:%M %p")));
    std::string endTime(epgEvent->EndTime(tr("%I:%M %p")));
    std::string startDate(epgEvent->StartTime(tr("%a, %x")));

    std::string timeSpan(startTime + tr(" - ") + endTime);

    if (startTime.empty() && endTime.empty()) {
      timeSpan = mode=="detail" ? headTime : std::string();
      startDate = headDate;
    }
    if (eventList.size() > 0 && mode == "detail") {
</%cpp>
      <div class="event">
        <div class="station">
          <div class="boxheader"><div class="caption"><a href="schedule.html?channel=<$ cSv(cToSvInt(chNumber)) $>" <& tooltip.hint text=(tr("View the schedule of this channel")) &>><$ cSv(cToSvInt(chNumber)) $><$ tr(" - ") $><$ (epgEvent->Caption()) $></a></div></div>
        </div>
        <div class="content">
          <div class="tools">
            <& pageelems.epg_tool_box detail=(1) epgid=(epgEvent->Id()) title=(epgEvent->Title()) startTime=(epgEvent->GetStartTime()) endTime=(epgEvent->GetEndTime()) &>
          </div>
          <div class="whatson_info">
            <div class="epg_details">
              <div class="info nowrap">
                <div class="date margin_right"><$ (startDate) $></div>
                <div class="time"><$ (timeSpan) $></div>
               </div>
              <div class="duration">
                <div class="progress"><& pageelems.progressbar progress=(epgEvent->Elapsed()) duration=(epgEvent->Duration()) &></div>
              </div>
            </div>
            <div class="title withmargin"><$ (epgEvent->Title()) $></div>
            <div class="short withmargin"><$ truncShortDescr $><? shortDescrTruncated ? " ..." ?></div>
            <div class="description withmargin"><$ truncDescription $><? descriptionTruncated ? " ..." ?></div>
%           if (shortDescrTruncated || descriptionTruncated) {
              <div class="more withmargin"><a <& tooltip.hint text=(longDescription) &><& tooltip.display domId=(epgEvent->Id()) &>><$ tr("more") $></a></div>
%           }
          </div>
        </div>
      </div>
<%cpp>
    } else { // mode == "list"
      bool lastCurrentChanel = i == (eventList.size() - 1);
      tChannelID chanId;
      tEventID eventId;
      EpgEvents::DecodeDomId(epgEvent->Id(), chanId, eventId);

      // Searching for channel logos
      cToSvConcat chanlogoimg;
      if (!LiveSetup().GetChanLogoDir().empty() ) {
        std::string chanlogofile = std::string(cToSvReplace(epgEvent->Caption(), "/", "_").concat(".png"));
        std::string chanlogopath = LiveSetup().GetChanLogoDir() + chanlogofile;
        struct stat chanfilebuf;
        bool logo_available = stat (chanlogopath.c_str(), &chanfilebuf) == 0;
        if (!logo_available) {
          chanlogofile = std::string(cToSvToLower(chanlogofile));
          chanlogopath = LiveSetup().GetChanLogoDir() + chanlogofile;
          logo_available = stat (chanlogopath.c_str(), &chanfilebuf) == 0;
        }
        if (!logo_available) {
          chanlogofile = cToSvReplace(epgEvent->Caption(), "/", "~").concat(".png");
          chanlogopath = LiveSetup().GetChanLogoDir() + chanlogofile;
          logo_available = stat (chanlogopath.c_str(), &chanfilebuf) == 0;
        }
        if (!logo_available) {
          chanlogofile = std::string(cToSvToLower(chanlogofile));
          chanlogopath = LiveSetup().GetChanLogoDir() + chanlogofile;
          logo_available = stat (chanlogopath.c_str(), &chanfilebuf) == 0;
        }
        if (!logo_available) {
          chanlogofile = cToSvReplace(epgEvent->Caption(), "/", "_").concat(".svg");
          chanlogopath = LiveSetup().GetChanLogoDir() + chanlogofile;
          logo_available = stat (chanlogopath.c_str(), &chanfilebuf) == 0;
        }
        if (!logo_available) {
          chanlogofile = std::string(cToSvToLower(chanlogofile));
          chanlogopath = LiveSetup().GetChanLogoDir() + chanlogofile;
          logo_available = stat (chanlogopath.c_str(), &chanfilebuf) == 0;
        }
        if (!logo_available) {
          chanlogofile = cToSvReplace(epgEvent->Caption(), "/", "~").concat(".svg");
          chanlogopath = LiveSetup().GetChanLogoDir() + chanlogofile;
          logo_available = stat (chanlogopath.c_str(), &chanfilebuf) == 0;
        }
        if (!logo_available) {
          chanlogofile = std::string(cToSvToLower(chanlogofile));
          chanlogopath = LiveSetup().GetChanLogoDir() + chanlogofile;
          logo_available = stat (chanlogopath.c_str(), &chanfilebuf) == 0;
        }
        if (logo_available) {
          chanlogoimg.append("<img src=\"chanlogos/");
          AppendHtmlEscapedAndCorrectNonUTF8(chanlogoimg, chanlogofile);
          chanlogoimg.append("\" alt=\"");
          AppendHtmlEscapedAndCorrectNonUTF8(chanlogoimg, epgEvent->Caption());
          chanlogoimg.append("\" loading=\"lazy\" class=\"channel_logo\">");
        }
      }
</%cpp>
      <tr>
        <td class="action leftcol <? lastCurrentChanel ? "bottomrow"?>"><div class="event_actions"><& pageelems.epg_tool_box detail=(0) epgid=(epgEvent->Id()) title=(epgEvent->Title()) startTime=(epgEvent->GetStartTime()) endTime=(epgEvent->GetEndTime()) &></div></td>
        <td class="topaligned <? lastCurrentChanel ? "bottomrow"?>">
          <div class="withmargin">
            <div class="nomargin nowrap"><$ (timeSpan) $></div>
            <div class="duration"><div class="progress"><& pageelems.progressbar progress=(epgEvent->Elapsed()) duration=(epgEvent->Duration()) &></div></div>
          </div>
        </td>
        <td class="topaligned <? lastCurrentChanel ? "bottomrow"?>">
%          if (eventId != 0) {
            <div class="more withmargin">
            <a
%            if (!longDescription.empty()) {
            <& tooltip.hint text=(longDescription) &><& tooltip.display domId=(epgEvent->Id()) &>
%            }
            ><span class="title"><$ (epgEvent->Title()) $></span><br/><span class="short"><$ (epgEvent->ShortDescr()) $></span></a>
%          } else {
            <div class="withmargin">
            <$ (epgEvent->Title()) $>
%          }
          </div>
        </td>
        <td class="topaligned rightcol <? lastCurrentChanel ? "bottomrow"?>"><div class="station withmargin"><span class="channel_id"><$ cToSvInt(chNumber) $><$ tr(" - ") $></span> <a href="schedule.html?channel=<$ cToSvInt(chNumber) $>" <& tooltip.hint label=(epgEvent->Caption().data()) className=("click-action") text=(tr("View the schedule of this channel")) &>><$$ chanlogoimg $><$ chanlogoimg.empty()?epgEvent->Caption():"" $></a></div>
        </td>
      </tr>
%  }
<%cpp>
  }
</%cpp>
%   if (eventList.size() > 0 && mode == "detail") {
      </div>
% } else {
%   if (eventList.size() == 0) {
        <tr>
          <td class="leftcol rightcol bottomrow message"><$ tr("No events found") $></td>
        </tr>
%   }
      </table>
% }
      <div class="spacebar"><# spacer with fade-out effect #></div>
    </div>
  </body>
</html>
<%include>page_exit.eh</%include>

<# ------------------------------------------------------------------------- #>

<%def whats_on_actions>
<a class="<? current_type == "now" ? "active" ?>" href="whats_on.html?type=now&amp;mode=<$ current_mode $>" id="nowhref"><$ tr("Now") $></a>
<span class="sep">|</span>
<a class="<? current_type == "next" ? "active" ?>" href="whats_on.html?type=next&amp;mode=<$ current_mode $>" id="nexthref"><$ tr("Next") $></a>
<span class="sep">|</span>
<span class="<? current_type == "at" && current_fixtime != "" ? "active" ?>"><$ tr("What's on") $></span>
<select name="userdeftimes" size="1" id="userdeftimes" onchange="showtime(this)">
  <option value="0"/>
<%cpp>
  std::string string_times = LiveSetup().GetTimes();
  cSplit parts(string_times, ';');
  auto part = parts.begin();
  for (int i = 0; part != parts.end(); ++i, ++part) {
</%cpp>
  <option <%cpp> reply.out() << ( (current_fixtime != "" && *part == current_displaytime) ? "selected=\"selected\"" : "" ); </%cpp>value="<$ *part $>"> <$ cToSvConcat(tr("at"), ' ', *part) $></option>
<%cpp>
  }
</%cpp>
</select>
<span class="sep">|</span>
<span class="<? current_type == "at" && current_fixtime == "" ? "active" ?>"><$ tr("at") $></span>
<input type="text" size="6" name="spectime" id="spectime" value = "<? current_attime != "" ? current_displaytime ?>" onchange="showspectime(this)"/>
<span class="sep">|</span>
% if ( LiveFeatures< features::epgsearch >().Recent() ) {
<a class="<? current_type == "favs" ? "active" ?>" href="whats_on.html?type=favs&amp;mode=<$ current_mode $>"><$ tr("Favorites") $></a>
<span class="sep">|</span>
% }
% if ( current_mode == "list" ) {
<a href="whats_on.html?mode=detail&amp;type=<$ current_type $>&amp;attime=<$ current_attime $>&amp;fixtime=<$ current_fixtime $>"><$ tr("Details view") $></a>
% } else {
<a href="whats_on.html?mode=list&amp;type=<$ current_type $>&amp;attime=<$ current_attime $>&amp;fixtime=<$ current_fixtime $>"><$ tr("List view") $></a>
% }
</%def>
