##############################################
# $Id$
#

package FHEM::attrT_z2m_thermostat_Utils;    ## no critic 'Package declaration'

use strict;
use warnings;
use JSON qw(decode_json);
use Carp qw(carp);
use Scalar::Util qw(looks_like_number);

#use POSIX qw(strftime);
#use List::Util qw( min max );
#use Scalar::Util qw(looks_like_number);
#use Time::HiRes qw( gettimeofday );

use GPUtils qw(GP_Import);

## Import der FHEM Funktionen
#-- Run before package compilation
BEGIN {
    # Import from main context
    GP_Import(
        qw(
          AttrVal
          InternalVal
          ReadingsVal
          ReadingsNum
          ReadingsAge
          CommandGet
          CommandSet
          readingsSingleUpdate
          json2nameValue
          defs
          Log3
          IsWe
          )
    );
}

sub ::attrT_z2m_thermostat_Utils_Initialize { goto &Initialize }

# initialize ##################################################################
sub Initialize {
  my $hash = shift;
  return;
}

# Enter you functions below _this_ line.

my %jsonmap = ( 

);

sub z2t_send_weekprofile {
  my $name       = shift // carp q[No device name provided!]              && return;
  my $wp_name    = shift // carp q[No weekprofile device name provided!]  && return;
  my $wp_profile = shift // AttrVal($name, 'weekprofile', undef) // carp q[No weekprofile profile name provided!] && return;
  my $model      = shift // ReadingsVal($name,'week','5+2');
  my $topic      = shift // AttrVal($name,'devicetopic','') . '/set';

  my $hash = $defs{$name} // return;
  $topic   .= ' ';

  my $wp_profile_data = CommandGet(undef,"$wp_name profile_data $wp_profile 0");
  if ($wp_profile_data =~ m{(profile.*not.found|usage..profile_data..name)}xms ) {
    Log3( $hash, 3, "[$name] weekprofile $wp_name: no profile named \"$wp_profile\" available" );
    return;
  }

  my @D = qw(Sun Mon Tue Wed Thu Fri Sat); # eqals to my @D = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat");
  my $payload;
  my @days = (0..6);
  my $decoded;
  if ( !eval { $decoded  = decode_json($wp_profile_data) ; 1 } ) {
    Log3($name, 1, "JSON decoding error in $wp_profile provided by $wp_name: $@");
    return;
  }

  if ( $model eq '5+2' || $model eq '6+1') {
    @days = (0,1);
  } elsif ($model eq '7') {
    @days = (1);
  }

  for my $i (@days) {
      $payload = '{';

      for my $j (0..7) {
        if (defined $decoded->{$D[$i]}{'time'}[$j]) {
          my $time = $decoded->{$D[$i]}{'time'}[$j-1] // "00:00";
          my ($hour,$minute) = split m{:}xms, $time;
          $hour = 0 if $hour == 24;
          $payload .= '"hour":' . abs($hour) .',"minute":'. abs($minute) .',"temperature":'.$decoded->{$D[$i]}{'temp'}[$j];
          $payload .= '},{' if defined $decoded->{$D[$i]}{'time'}[$j+1];
        }
      }
      $payload .='}';
      if ( $i == 0 && ( $model eq '5+2' || $model eq '6+1') ) {
        CommandSet($hash,"$name holidays $payload");
        $payload = '{';
      }
      CommandSet($hash,"$name workdays $payload") if $model eq '5+2' || $model eq '6+1' || $model eq '7';
  }
  readingsSingleUpdate( $hash, 'weekprofile', "$wp_name $wp_profile",1);
  return;
}


sub z2t_send_Beca_weekprofile {
  my $name       = shift // carp q[No device name provided!]              && return;
  my $wp_name    = shift // carp q[No weekprofile device name provided!]  && return;
  my $wp_profile = shift // AttrVal($name, 'weekprofile', undef) // carp q[No weekprofile profile name provided!] && return;
  my $topic      = shift // carp q[No topic to send to provided!]         && return;

  my $hash = $defs{$name} // return;

  my $wp_profile_data = CommandGet(undef,"$wp_name profile_data $wp_profile 0");
  if ($wp_profile_data =~ m{(profile.*not.found|usage..profile_data..name)}xms ) {
    Log3( $hash, 3, "[$name] weekprofile $wp_name: no profile named \"$wp_profile\" available" );
    return;
  }

  my @D = qw(Sun Mon Tue Wed Thu Fri Sat); # eqals to my @D = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat");
  my $payload;
  my @days = (0,1,6);
  my $decoded;
  if ( !eval { $decoded  = decode_json($wp_profile_data) ; 1 } ) {
    Log3($name, 1, "JSON decoding error in $wp_profile provided by $wp_name: $@");
    return;
  }

  for my $i (@days) {
      my $sd = $i == 0 ? 'u' : $i == 6 ? 'a' : 'w';

      for my $j (0..5) {
        my $time = $decoded->{$D[$i]}{'time'}[$j];
        last if !defined $time;
        my $tmp  = $decoded->{$D[$i]}{'temp'}[$j];
        my $k = $j+1;
        next if !looks_like_number($tmp);
        $payload .= defined $payload ? ',' : '{';
        $payload .= qq("${sd}${k}h":"$time","${sd}${k}t":$tmp);
      }
  }
  #Log3($hash,3,"$payload");
  return if !defined $payload;
  $payload .='}';
  readingsSingleUpdate( $hash, 'weekprofile', "$wp_name $wp_profile",1);
  return qq{$topic $payload};
}


sub z2t_send_BHT {
  my $name       = shift // carp q[No device name provided!]              && return;
  my $wp_name    = shift // carp q[No weekprofile device name provided!]  && return;
  my $wp_profile = shift // AttrVal($name, 'weekprofile', undef) // carp q[No weekprofile profile name provided!] && return;
  my $topic      = shift // AttrVal($name,'devicetopic','') . '/set';

  my $hash = $defs{$name} // return;

  #Log3($hash, 3, "Fetching weekprofile for ${name} day ${today} / $D[${today}] from ${wp_name}/${wp_profile}");
  my $wp_profile_data = CommandGet(undef,"$wp_name profile_data $wp_profile 0");
  if ($wp_profile_data =~ m{(profile.*not.found|usage..profile_data..name)}xms ) {
    Log3( $hash, 3, "[$name] weekprofile $wp_name: no profile named \"$wp_profile\" available" );
    return;
  }

  my @D = qw(Sun Mon Tue Wed Thu Fri Sat); # eqals to my @D = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat");
  my $today = (localtime(time))[6];
  #  if ( !($today ~~ [1..5]) ) {$today = 1};
  if ( !(0 < $today < 6) ) {$today = 1};
  my @days = ($today,6,0);
  my $payload;
  my $decoded;
  if ( !eval { $decoded  = decode_json($wp_profile_data) ; 1 } ) {
    Log3($name, 1, "JSON decoding error in $wp_profile provided by $wp_name: $@");
    return;
  }

  for my $i (@days) {
      my $sd = $i == 0 ? 'sunday' : $i == 6 ? 'saturday' : 'weekdays';

      for my $j (0..3) {
        my $time = $decoded->{$D[$i]}{'time'}[$j];
        last if !defined $time;
        my ($hour,$minute) = split m{:}xms, $time;
        $hour += 0;
        $minute += 0;
        my $tmp  = $decoded->{$D[$i]}{'temp'}[$j]+0;
        my $k = $j+1;
        next if !looks_like_number($tmp);
        $payload .= defined $payload ? ',' : '{';
        $payload .= qq("${sd}_p${k}_hour":$hour,"${sd}_p${k}_minute":$minute,"${sd}_p${k}_temperature":$tmp);
      }
  }
  return if !defined $payload;
  $payload = '{"program":'.$payload;
  $payload .='}}';
  #Log3($hash,3,"Setting $name to new weekprofile: $payload");
  readingsSingleUpdate( $hash, 'weekprofile', "$wp_name $wp_profile",1);
  return qq{$topic $payload};
}

sub z2t_send_TRV {
  my $name       = shift // carp q[No device name provided!]              && return;
  my $wp_name    = shift // carp q[No weekprofile device name provided!]  && return;
  my $wp_profile = shift // AttrVal($name, 'weekprofile', undef) // carp q[No weekprofile profile name provided!] && return;
  my $topic      = shift // AttrVal($name,'devicetopic','') . '/set';

  my $hash = $defs{$name} // return;

  my $wp_profile_data = CommandGet(undef,"$wp_name profile_data $wp_profile 0");
  if ($wp_profile_data =~ m{(profile.*not.found|usage..profile_data..name)}xms ) {
    Log3( $hash, 3, "[$name] weekprofile $wp_name: no profile named \"$wp_profile\" available" );
    return;
  }

  my @D = qw(Sun Mon Tue Wed Thu Fri Sat); # eqals to my @D = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat");
  my $today = (localtime(time))[6];
  #  if ( !($today ~~ [1..5]) ) {$today = 1};
  if ( !(0 < $today < 6) ) {$today = 1};
  #Log3($hash, 3, "Fetching weekprofile for ${name} day ${today} / $D[${today}] from ${wp_name}/${wp_profile}");
  my @days = ($today,6,0);
  my $payload;
  my $decoded;
  if ( !eval { $decoded  = decode_json($wp_profile_data) ; 1 } ) {
    Log3($name, 1, "JSON decoding error in $wp_profile provided by $wp_name: $@");
    return;
  }

  for my $i (@days) {

      for my $j (0..3) {
        my $time = $decoded->{$D[$i]}{'time'}[$j];
        my ($hour,$minute) = split m{:}xms, $time;
        $hour += 0;
        $minute += 0;
        last if !defined $time;
        my $tmp  = $decoded->{$D[$i]}{'temp'}[$j]+0;
        my $k = $j+1;
        next if !looks_like_number($tmp);
        $payload .= defined $payload ? '  ' : '"';
        $payload .= sprintf("%02d:%02d/%d", $hour, $minute, $tmp);
      }
  }
  #  Log3($hash,3,"$payload");
  return if !defined $payload;
  $payload = '{"programming_mode":'.$payload;
  $payload .='"}';
  #Log3($hash,3,"Setting ${name} to new weekprofile: ${payload}");
  readingsSingleUpdate( $hash, 'weekprofile', "$wp_name $wp_profile",1);
  return qq{$topic $payload};
}

sub z2t_send_ME168 {
  my $name       = shift // carp q[No device name provided!]              && return;
  my $wp_name    = shift // carp q[No weekprofile device name provided!]  && return;
  my $wp_profile = shift // AttrVal($name, 'weekprofile', undef) // carp q[No weekprofile profile name provided!] && return;
  my $topic      = shift // AttrVal($name,'devicetopic','') . '/set';

  my $hash = $defs{$name} // return;

  my $wp_profile_data = CommandGet(undef,"$wp_name profile_data $wp_profile 0");
  if ($wp_profile_data =~ m{(profile.*not.found|usage..profile_data..name)}xms ) {
    Log3( $hash, 3, "[$name] weekprofile $wp_name: no profile named \"$wp_profile\" available" );
    return;
  }

  my @D = qw(Sun Mon Tue Wed Thu Fri Sat); # eqals to my @D = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat");
  my @DD = qw(sunday monday tuesday wednesday thursday friday saturday); # eqals to my @D = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat");
  my $today = (localtime(time))[6];
  #  if ( !($today ~~ [1..5]) ) {$today = 1};
  # if ( !(0 < $today < 6) ) {$today = 1};
  # Log3($hash, 3, "Fetching weekprofile for ${name} day ${today} / $D[${today}] from ${wp_name}/${wp_profile}");
  # real dyrty hack, s it seems only the first 6 days are transmitted....
  my @days = ($today, ($today+1)%7, ($today+2)%7, ($today+3)%7, ($today+4)%7 ,($today+5)%7 ,($today+6)%7);
  #my @days = (0..6);
  my $payload;
  my $decoded;
  if ( !eval { $decoded  = decode_json($wp_profile_data) ; 1 } ) {
    Log3($name, 1, "JSON decoding error in $wp_profile provided by $wp_name: $@");
    return;
  }

  for my $i (@days) {
      $payload .= defined $payload ? ', ' : '';
      $payload .= '"schedule_' . $DD[$i].'":';
      my $schedule;
      for my $j (0..3) {
        my $time = $decoded->{$D[$i]}{'time'}[$j];
        my ($hour,$minute) = split m{:}xms, $time;
        $hour += 0;
        $minute += 0;
        last if !defined $time;
        my $tmp  = $decoded->{$D[$i]}{'temp'}[$j]+0;
        my $k = $j+1;
        next if !looks_like_number($tmp);
        $schedule .= defined $schedule ? ' ' : '"';
        $schedule .= sprintf("%02d:%02d/%d", $hour, $minute, $tmp);
      }
      $payload .= $schedule . '"';
  }
  #  Log3($hash,3,"$payload");
  return if !defined $payload;
  $payload = '{'.$payload;
  $payload .='}';

  #Log3($hash,3,"Setting ${name} to new weekprofile: ${payload}");
  readingsSingleUpdate( $hash, 'weekprofile', "$wp_name $wp_profile",1);
  return qq{$topic $payload};
}


1;

__END__

=pod
=item summary helper functions needed for zigbee2mqtt thermostats in MQTT2_DEVICE
=item summary_DE Hilfsfunktionen für zigbee2mqtt MQTT2_DEVICE-Thermostate 

=begin html

<a id="attrT_z2m_thermostat_Utils"></a>
  There may be room for improvement, please adress any issues in https://forum.fhem.de/index.php/topic,116535.0.html.
<h3>attrT_z2m_thermostat_Utils</h3>
<ul>
  <b>z2t_send_weekprofile</b>
  <br>
  This is a special function to request temperature list data from <i>weekprofile</i> and convert and send it out via MQTT<br>
  <br>
  General requirements and prerequisites:<br>
  <ul>
  <li>existing <i>weekprofile</i> device with activated <i>useTopic</i> feature</li>
  <li>weekprofile attribute set at calling MQTT2_DEVICE</li>
  </ul>
  <br>
  Special remarks for usage with attrTemplate <i>zigbee2mqtt_thermostat_with_weekrofile</i>:<br>
  <ul>
  <li>existing <i>setList</i> entries required (<i>workdays</i> and <i>holidays</i>)</li>
  <li>for conversion from <i>weekprofile</i> data to entries <i>workdays</i> and <i>holidays</i> only monday and sunday data will be used, other days will be ignored</li>
  <li>as parameters, <i>$name</i> (name of the calling MQTT2_DEVICE), <i>$wp_name</i> (name of the weekprofile device) and $wp_profile (in "topic:entity" format) have to be used, when topic changes are done via weekprofile, the relevent data will be sent to the MQTT2_DEVICE instances with suitable <i>weekprofile</i> attribute automatically.<br>
  Additionally you may force sending holiday data by adding a forth parameter ($model) and set that to '5+2'.<br>
  So entire Perl command for <i>zigbee2mqtt_thermostat_with_weekrofile</i> should look like:
  <ul>
   <code>FHEM::attrT_z2m_thermostat_Utils::z2t_send_weekprofile($NAME, $EVTPART1, $EVTPART2)</code><br>
  </ul><br>or 
  <ul>
   <code>FHEM::attrT_z2m_thermostat_Utils::z2t_send_weekprofile($NAME, $EVTPART1, $EVTPART2, '5+2')</code><br>
  </ul>
  </li>
  </ul>
</ul>
=end html
=cut