#!/usr/bin/perl

use strict;
use warnings;
use utf8;
use Getopt::Long;
use Cwd 'abs_path';
use POSIX ":sys_wait_h";
binmode STDOUT, ":encoding(UTF-8)";

my $debug = 0;

my $dogpath = abs_path($0);
my $doghome = $ENV{'HOME'} . '/.dog';
mkdir($doghome) if (! -e $doghome);
my $cachehome = $ENV{'HOME'} . '/.dog/cache';
mkdir($cachehome) if (! -e $cachehome);
my $loghome = $ENV{'HOME'} . '/.dog/log';
mkdir($loghome) if (! -e $loghome);
my $confighome = $ENV{'HOME'} . '/.dog/config';
mkdir($confighome) if (! -e $confighome);

##### START OF FUNCTIONS #####

sub exec_monitor {
  my $cmd = shift;
  my $pid = fork();
  die "Could not fork\n" if not defined $pid;
 
  if (not $pid) {
    exec($cmd);
  }
 
  # "In parent of $pid";
  $| = 1;
  my $begin_time = time();
  my $sudo_timer = time();
  `sudo echo "安装需要管理员权限"`; # refresh sudo sudo_timer

  while (1) {
    sleep(5);
    if (time() - $sudo_timer > 240) {
      `sudo echo "安装需要管理员权限"`; # refresh sudo sudo_timer
      $sudo_timer = time();
    }

    my $res = waitpid($pid, WNOHANG);
 
    if ($res == -1) {
      print "遇到未知错误：", $? >> 8;
    }

    if ($res) {
      print "\n安装耗时: " . (time() - $begin_time) . "秒\n";
      last;
    }
   
    print '.';
  }
}

sub get_os_type() {
  my $line = `lsb_release -a 2>&1 | grep 'Distributor ID'`;
  my $value = '';
  if ($line =~ /Distributor ID:\s*([A-Za-z]+)/) {
    $value = $1;
  }

  return $value;
}

sub get_codename() {
  my $codeLine = `lsb_release -a 2>&1 | grep Codename`;
  my $codename = '';
  if ($codeLine =~ /Codename:\s*([a-z]+)/) {
    $codename = $1;
  }

  return $codename;
}

sub get_release() {
  my $releaseLine = `lsb_release -a 2>&1 | grep Release`;
  my $release = 0;
  if ($releaseLine =~ /Release:\s*([0-9.]+)/) {
    $release = $1;
  }

  return $release;
}

sub install {
  my $software = shift;
  
  my $server = 'http://120.24.87.124';

  if ($software eq 'ekho' || $software eq 'ekho-huang' ||
      $software eq 'ibusreader' || $software eq 'tianqi' ||
      $software eq 'orca' || $software eq 'speechd') {
    my $filename = $software . '-latest.tar.xz';

    my $os_type = get_os_type();
    my $codename = get_codename();
    my $release = get_release();

    if ($software eq 'ekho') {
      $server = '--no-check-certificate https://eguidedog-1256623257.cos.ap-guangzhou.myqcloud.com';
      $filename = $software . '-11.0.tar.xz';
      if ($os_type eq 'Ubuntu') {
        if ($release <= 18.04) {
          $filename = $software . '-18.04.tar.xz';
        } elsif ($codename eq 'focal' || $release <= 20.04) {
          $filename = $software . '-20.04.tar.xz';
        } elsif ($release <= 22.04) {
          $filename = $software . '-22.04.tar.xz';
        }
      }
    } elsif ($software eq 'ibusreader') {
      if ($os_type eq 'Debian' && ($release == 12 || $codename eq 'trixie')) {
        $filename = $software . '-debian12.tar.xz';
      } elsif ($os_type eq 'Ubuntu' && $release <= 24.04) {
        $filename = $software . '-ubuntu24.tar.xz';
      }
    }

    `sudo echo "安装需要管理员权限"`;

    print "现在下载文件，请耐心等待。。。\n";
    `wget -q $server/$software/$filename.md5sum -O $cachehome/$filename.md5sum`;
    `wget -c $server/$software/$filename -O $cachehome/$filename`;
    my ($local_md5sum, $p) = split(/\s/, `md5sum $cachehome/$filename`, 2);
    my ($remote_md5sum, $p2) = split(/\s/, `cat $cachehome/$filename.md5sum`, 2);

    if ($local_md5sum ne $remote_md5sum) {
      print "下载文件的MD5SUM不一致，尝试重新下载\n";
      `wget $server/$software/$filename -O $cachehome/$filename`;
      ($local_md5sum, $p) = split(/\s/, `md5sum $cachehome/$filename`, 2);
      ($remote_md5sum, $p2) = split(/\s/, `cat $cachehome/$filename.md5sum`, 2);
 
      if ($local_md5sum ne $remote_md5sum) {
        print "重新下载后还是不一致，请尝试更换网络环境下载。\n";
        exit;
      }
    }

    print "下载完毕，开始安装软件，这可能需要几分钟时间\n";
    `sudo echo "安装需要管理员权限"`;
    exec_monitor("rm -rf $cachehome/tmp && mkdir $cachehome/tmp && tar xJf $cachehome/$filename -C $cachehome/tmp && cd $cachehome/tmp/* && ./deploy.sh 1>$loghome/$software.out 2>$loghome/$software.err");

    my $success = 1;
    if ($software =~ /ekho/) {
      $success = `ekho --version 2>/dev/null`;
      if ($success) {
        print "软件版本：$success\n";
      }
    } elsif ($software eq 'orca') {
      $success = `orca --version`;
      if ($success) {
        print "软件版本：$success\n";
      }
    } elsif ($software eq 'ibusreader') {
      $success = `/usr/lib/ibus/ibus-engine-libpinyin -V`;
      if ($success) {
        print "软件版本：$success\n";
      }
    }

    my $machineid = `cat /etc/machine-id`;
    chomp($machineid);
    $machineid = 'dog|' . $machineid;
    if ($success) {
      print "软件安装完毕。\n";
      `wget -q "http://cto.eguidedog.net/howto/log/add/1/$machineid/dog?content=$software:INSTALL_SUCCESS" -O /dev/null &`;
    } else {
      print "安装失败，请联系作者黄冠能调试问题。\n";
      `wget -q "http://cto.eguidedog.net/howto/log/add/1/$machineid/dog?content=$software:INSTALL_FAIL" -O /dev/null &`;
    }
  } else {
    print "不能识别的软件名称\n";
  }
}

sub kill_process {
  my $keyword = shift;
  `ps -ef | grep $keyword | grep -v dog | grep -v grep | awk '{print \$2}' | xargs -I {} kill {} 2>/dev/null`;
}

sub restart_speech() {
  $debug = 0;
  if ($debug) {
    `date >> /tmp/dog.log`;
    `echo 'restart zhttsServer' >> /tmp/dog.log`;
  }

  if (-f "/usr/bin/zhttsServer.py" && !`pgrep -f zhttsServer.py`) {
    system("/opt/miniconda3/envs/py38/bin/python /usr/bin/zhttsServer.py 1>/dev/null 2>/dev/null &");
    sleep(10);
  }

  if ($debug) {
    `date >> /tmp/dog.log`;
    `echo 'restart piper' >> /tmp/dog.log`;
  }
  
  if (-d "/usr/local/share/ekho-data/piper" && !`pgrep -f piper`) {
    system("cd /usr/local/share/ekho-data/piper && /opt/miniconda3/bin/python3 -m piper.http_server -m en_US-amy-low --host 127.0.0.1 --port 5001 2>/dev/null &");
    sleep(10);
  }

  if ($debug) {
    `date >> /tmp/dog.log`;
    `echo 'restart other' >> /tmp/dog.log`;
  }
  
  kill_process('speech-dispatcher');
  kill_process('ibus-ui-gtk3');
  
  if (-f "/usr/bin/pulseaudio" && `pgrep pulseaudio`) {
    `pulseaudio -k`;
  }
  
  system("orca --replace 2>/dev/null &"); # can't use `` to run process in background
  `command -v ibus-daemon && ibus-daemon -drx && sleep 1 && (ibus restart 2>/dev/null) && sleep 1 && (ibus engine libpinyin 2>/dev/null)`;

  if ($debug) {
    `date >> /tmp/dog.log`;
    `echo 'restart finish' >> /tmp/dog.log`;
  }
}

sub sendlog() {
  `tar cJvf /tmp/dog-logs.tar.xz $loghome`;
  `sudo apt-get install -y curl`;
  `curl -F"operation=upload" -F"file=\@/tmp/dog-logs.tar.xz" https://eguidedog.net/ekho/upload.php`;
  print "分析日志已发送，请发送邮件通知技术支持人员。\n";
}

sub update_config {
  my ($file, $key, $value) = @_;
  if (-e $file) {
    `mv $file $file.old`;
    `grep -v $key $file.old > $file`;
    `echo '$key=$value' >> $file`;
  } else {
    `echo '$key=$value' > $file`;
  }
}

sub ibusreader_config_help() {
  print << 'END_HELP';

ibusreader支持的参数：
rate <语速值> - 语速值为0到100
char_limit <解释字数上限> - 默认为2，即当词组的字数不大于2时，会把每个字组词读出

END_HELP
}

sub set_config {
  my ($software, $key, $value) = @_;
  if ($software eq 'ibusreader') {
    if (!$key) {
      ibusreader_config_help();
    } elsif ($key eq 'rate') {
      if ($value =~ /^\d+$/) {
        update_config("$confighome/$software", $key, $value);
        `spd-say -w -r $value "语速已调整为$value"`;
      } else {
        print "参数值不合法，应为整数: $value\n";
      }
    } elsif ($key eq 'char_limit') {
      if ($value =~ /^\d+$/) {
        update_config("$confighome/$software", $key, $value);
      } else {
        print "参数值不合法，应为大于0的整数\n";
      }
      `ibus restart`;
    } else {
      print "不能识别的软件参数: $key\n";
      ibusreader_config_help();
    }
  } else {
    print "不能识别的软件名称: $software\n";
  }
}

sub parse_options() {
  my $command = shift(@ARGV);
  my $help;

  GetOptions(
    #"length=i" => \$length,    # numeric
    #"file=s"   => \$data,      # string
    "debug|d"  => \$debug,
    "help|h" => \$help
  );

  #print $command, $debug, @ARGV, "\n";

  if ($help || !$command || $command eq 'help') {
    show_help();
  } elsif ($command eq 'install') {
    if (@ARGV) {
      foreach my $software (@ARGV) {
        install($software);
      }
    } else {
      print "请输入需要安装的软件名称\n";
    }
  } elsif ($command eq 'ask') {
    ask(join(' ', @ARGV));
  } elsif ($command eq 'restart') {
    restart_speech();
  } elsif ($command eq 'kill') {
    kill_process($ARGV[0]);
  } elsif ($command eq 'update') {
    if ($ARGV[0] && $ARGV[0] eq 'apt') {
      update_apt();
    } else {
      update(1);
    }
  } elsif ($command eq 'sendlog') {
    sendlog()
  } elsif ($command eq 'set') {
    set_config(@ARGV);
  } else {
    show_help();
  }
}

sub show_help() {
  print << 'END_HELP';
eGuideDog工具箱

用法: dog <命令> [选项] [参数]

支持的命令有:
install <软件名称> - 安装软件，目前的软件有ekho, orca, tianqi
restart - 重启读屏
update - 更新工具箱
update apt - 更新apt源到较快的镜像
sendlog - 发送分析日志
help - 显示帮助信息

例子:
1. 安装Ekho语音引擎并设置为Orca读屏软件的默认语音引擎
dog install ekho

作者: 黄冠能 <hgneng at gmail.com>
网站: https://eguidedog.net

END_HELP

exit;
}

sub update {
  my $force = shift;
  return if ((!$force) && -e "$cachehome/dog" && -M "$cachehome/dog" < 1);
  `wget -q https://eguidedog.net/files/dog -O $cachehome/dog`;
  if (-s "$cachehome/dog" > 0) {
    my ($current_md5sum, $p) = split(/\s/, `md5sum /usr/bin/dog`, 2);
    my ($new_md5sum, $p2) = split(/\s/, `md5sum $cachehome/dog`, 2);
    if ($current_md5sum ne $new_md5sum) {
      `sudo cp $cachehome/dog /usr/bin/dog && sudo chmod 777 /usr/bin/dog`;
      print "发现工具箱有新版本，已更新。\n";
      if (!$force) {
        sleep(3);
        exec("perl /usr/bin/dog @ARGV"); # exec never return
      }
    }
  } else {
    print "尝试检查工具箱更新但失败\n";
  }
}

sub update_apt() {
  my $time = time();
  my $os_type = get_os_type();
  my $codename = get_codename();
  if ($os_type eq 'Ubuntu') {
    `sudo mv /etc/apt/sources.list /etc/apt/sources.list.$time`;
    `echo 'deb https://mirrors.cloud.tencent.com/ubuntu/ $codename main restricted universe multiverse' >> /tmp/dog_sources.list`;
    `echo 'deb https://mirrors.cloud.tencent.com/ubuntu/ $codename-security main restricted universe multiverse' >> /tmp/dog_sources.list`;
    `echo 'deb https://mirrors.cloud.tencent.com/ubuntu/ $codename-updates main restricted universe multiverse' >> /tmp/dog_sources.list`;
    `echo 'deb https://mirrors.cloud.tencent.com/ubuntu/ $codename-backports main restricted universe multiverse' >> /tmp/dog_sources.list`;
    `sudo mv /tmp/dog_sources.list /etc/apt/sources.list`;
    print "/etc/apt/sources.list updated\n";
    exec("sudo apt update");
    exit;
  } elsif ($os_type eq 'Debian') {
    `sudo mv /etc/apt/sources.list /etc/apt/sources.list.$time`;
    `echo 'deb http://mirrors.163.com/debian/ $codename main' >> /tmp/dog_sources.list`;
    `echo 'deb http://security.debian.org/debian-security $codename-security main' >> /tmp/dog_sources.list`;
    `echo 'deb http://mirrors.163.com/debian/ $codename-updates main' >> /tmp/dog_sources.list`;
    `sudo mv /tmp/dog_sources.list /etc/apt/sources.list`;
    print "/etc/apt/sources.list updated\n";
    exec("sudo apt update");
  } else {
    print "不支持当前操作系统：$os_type\n";
  }
}

sub install_perl_module($) {
  my $module = shift;
  print "安装依赖模块$module, 请耐心等待。\n";
  `sudo apt install -y libjson-perl`;
  #system("env PERL_MM_USE_DEFAULT=1 cpan -i $module");
  print "已安装依赖模块$module\n";
}

sub ask($) {
  print "抱歉，此功能已下线。\n";
  return;

  my $question = shift;
  $question =~ s/(["])/\\$1/g;
  $question =~ s/(['])/'\\''/g;
  my $ret = `curl -s -k -H "Content-Type: application/json" -H "Authorization: Bearer 6|xbCk3yqCU46l9qw7WMWMMhnIHxNUeQ3Wzk3cF8ta" -d '{"messages": [{"role":"user","content":"$question"}]}' https://m.100week.cn/chat/api/chat 2>&1`;
  #install_perl_module('JSON');
  eval {
    require JSON;
    my $json = JSON::decode_json($ret);
    if (exists $json->{result}) {
      print $json->{result}, "\n";
    } else {
      print "结果异常：$ret\n";
    }
  } or do {
    my $error = $@;
    print "异常：$error\n";
    print "返回：$ret\n";

    if ($error =~ /curl/) {
      print "安装依赖模块curl\n";
      `sudo apt install -y curl`;
      print "已安装依赖模块curl，请重新运行。\n";
      #exec("dog ask $question");
    } elsif ($error =~ /JSON\.pm/) {
      install_perl_module('JSON');
      print "请重新运行\n";
    }
  };
}
##### END OF FUNCTIONS #####

##### START OF MAIN #####
parse_options();
##### END OF MAIN #####
