<?php
/**
 * Created by PhpStorm.
 * User: qiuzijian
 * Date: 2021-04-28
 * Time: 20:59
 */

namespace Modules\Camera\Http\Controllers\Api;

use App\Enum\ApiEnum;
use App\Http\Controllers\Api\BaseController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Log;
use Modules\Camera\Entities\CameraList;
use Modules\Camera\Enum\CameraEnum;
use Modules\Mine\Entities\MineList;
use Modules\Mine\Services\MineServices;
use Modules\Camera\Services\CameraServices;


class HaiKangController extends BaseController
{
    protected $pre_url;  // 海康接口地址
    protected $app_key;  // 海康app_key
    protected $app_secret;  // 海康app_secret
    protected $sign;//  签名
    protected $time;//  时间戳
    protected $artemis;//  OpenAPI接口的上下文

    public function __construct()
    {
//        if ($request->has('mine')) {
//            $this->pre_url    = config('haikang.' . $request->input('mine') . '.pre_url');  // https://120.253.79.51:4433
//            $this->app_key    = config('haikang.' . $request->input('mine') . '.app_key');  // 25720460
//            $this->app_secret = config('haikang.' . $request->input('mine') . '.app_secret');  // qqP7NLcIDwO9MgtYmp8L
//            $this->artemis    = config('haikang.' . $request->input('mine') . '.artemis');  // /artemis
//        } else {
//            $this->pre_url    = env('HAI_KANG_URL');  // https://120.253.79.51:4433
//            $this->app_key    = env('HAI_KANG_APP_KEY');  // 25720460
//            $this->app_secret = env('HAI_KANG_APP_SECRET');  // qqP7NLcIDwO9MgtYmp8L
//            $this->artemis    = env('HAI_KANG_ARTEMIS_PATH');  // /artemis
//        }
//        $this->pre_url    = 'https://10.71.252.64:4433';  // https://120.253.79.51:4433
//        $this->app_key    = '25720460';  // 25720460
//        $this->app_secret = 'qqP7NLcIDwO9MgtYmp8L';  // qqP7NLcIDwO9MgtYmp8L
//        $this->artemis    = '/artemis';  // /artemis
        $this->pre_url    = Input::get('url');  // https://120.253.79.51:4433
        $this->app_key    = Input::get('key');  // 25720460
        $this->app_secret = Input::get('secret');  // qqP7NLcIDwO9MgtYmp8L
        $this->artemis    = '/artemis';  // /artemis

        list($msec, $sec) = explode(' ', microtime());
        $this->time = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
    }

    /**
     * 获取区域列表
     * @return bool|mixed|string
     */
    public function getRegionsList()
    {
        $url = $this->artemis . '/api/resource/v1/regions';
        //请求参数
        $params = [];
//        $params['pageNo']   = $request->has('pageNo') ? intval($request->input('pageNo')) : 1;
//        $params['pageSize'] = $request->has('pageSize') ? intval($request->input('pageSize')) : 1000;
        $params['pageNo']   = 1;
        $params['pageSize'] = 1000;
        $this->sign         = $this->get_sign($url);

        $type    = Input::get('type');
        $mine_id = Input::get('mine_id');

        $result = $this->curlPost($this->pre_url . $url, json_encode($params));
        $result = json_decode($result, true);

        $area_list = [];
        if (isset($result['code']) && $result['code'] == 0) {
            $list = $result['data']['list'];
            foreach ($list as $key => $item) {
                $area_list[] = $item['indexCode'];
                if ($item['parentIndexCode'] == '-1') {
                    unset($list[$key]);
                }
            }
            $result['data']['list'] = array_values($list);
        }

        if ($type == CameraEnum::REQUEST_TYPE_API) {
            $result['data']['list'] = $this->regionsTree($result['data']['list']);
        } else {
            //先删除不存在的区域
            if ($area_list) {
                MineList::where('degree', 'like', $mine_id . '|%')->whereNotIn('index_code', $area_list)->delete();
            }

            //更新区域
            $result['data']['list'] = $this->saveTree($result['data']['list'], $mine_id);
        }

        return $result;
    }

    protected function regionsTree($regions, $pid = 'root000000')
    {
        $arr = [];
        if (empty($regions)) {
            return [];
        }

        foreach ($regions as $key => $value) {
            if (isset($value['parentIndexCode']) && $value['parentIndexCode'] == $pid) {
                $arr[$key]['indexCode']       = $value['indexCode'];
                $arr[$key]['mine_id']         = $value['indexCode'];
                $arr[$key]['name']            = $value['name'];
                $arr[$key]['parentIndexCode'] = $value['parentIndexCode'];
                $arr[$key]['treeCode']        = $value['treeCode'];
                $arr[$key]['children']        = array_values(self::regionsTree($regions, $value['indexCode']));
                unset($arr[$key]['indexCode']);
                unset($arr[$key]['parentIndexCode']);
                unset($arr[$key]['treeCode']);
                if (count($arr[$key]['children']) == 0) {
                    unset($arr[$key]['children']);
                }
            }
        }

        return array_values($arr);
    }

    protected function saveTree($regions, $parent_id, $pid = 'root000000')
    {

        $arr = [];
        if (empty($regions)) {
            return [];
        }

        $mineService = new MineServices();
        $mineService->initMineList();

        $arr_count = count($regions);
        foreach ($regions as $key => $value) {
            if (isset($value['parentIndexCode']) && $value['parentIndexCode'] == $pid) {
                $arr[$key]['indexCode']       = $value['indexCode'];
                $arr[$key]['mine_id']         = $value['indexCode'];
                $arr[$key]['name']            = str_replace('#', '号', $value['name']);
                $arr[$key]['parentIndexCode'] = $value['parentIndexCode'];
                $arr[$key]['treeCode']        = $value['treeCode'];

                $id      = MineList::where('index_code', $value['indexCode'])->value('id');
                $params  = [
                    'id'         => $id,
                    'parent_id'  => $parent_id,
                    'title'      => $value['name'],
                    'sort'       => $arr_count - $key,
                    'index_code' => $value['indexCode'],
                ];
                $result  = $mineService->add($params);
                $mine_id = $result->id;

                $arr[$key]['children'] = array_values(self::saveTree($regions, $mine_id, $value['indexCode']));
                if (count($arr[$key]['children']) == 0) {
                    unset($arr[$key]['children']);
                }
            }
        }

        return array_values($arr);
    }

    /**
     * 获取区域监控列表
     * @return bool|mixed|string
     */
    public function getCamerasList()
    {
        if (!Input::has('indexCode')) {
            return $this->error(1, '缺少必要参数');
        }
        $url = $this->artemis . '/api/resource/v1/regions/regionIndexCode/cameras';
        //请求参数
        $params                    = [];
        $params['regionIndexCode'] = Input::get('indexCode');
//        $params['pageNo']          = $request->has('pageNo') ? intval($request->input('pageNo')) : 1;
//        $params['pageSize']        = $request->has('pageSize') ? intval($request->input('pageSize')) : 1000;
        $params['pageNo']   = 1;
        $params['pageSize'] = 1000;
        $this->sign         = $this->get_sign($url);

        $result = $this->curlPost($this->pre_url . $url, json_encode($params));
        $result = json_decode($result, true);
        return $result;
    }

    /**
     * 获取摄像头码流url
     * @return array|mixed
     */
    public function getCamerasUrl()
    {
        if (!Input::has('cameraIndexCode')) {
            return $this->error(1, '缺少必要参数');
        }
        $protocol   = Input::get('protocol', 'hls');
        $streamType = Input::get('streamType', 1);
        $url        = $this->artemis . '/api/video/v1/cameras/previewURLs';
//        $cameras_info = json_decode($this->getCamerasInfo(Input::all()), true);
        //请求参数
        $params = [];
//        $params['regionIndexCode'] = $request->input('regionIndexCode');
        $params['cameraIndexCode'] = Input::get('cameraIndexCode');
        $params['streamType']      = $streamType;  //0主码流 1子码流 2第三码流
        $params['protocol']        = $protocol;
        $params['transmode']       = 1;  //0:UDP 1:TCP
        $params['expand'] = 'transcode=1&systemformat=ps&videotype=h264';

        $this->sign = $this->get_sign($url);

        $result = $this->curlPost($this->pre_url . $url, json_encode($params));
        $result = json_decode($result, true);
        if (isset($result['code']) && $result['code'] == 0) {  // 将内网地址替换为外网地址
            $video_url = $result['data']['url'];
            preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/i', $video_url, $res);  // 提取内网ip
            preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/i', $this->pre_url, $res_n);  // 提取外网ip
            $result['data']['url'] = str_replace($res, $res_n, $video_url);
        }
        return $result;
    }

    /**
     * 获取摄像头详情
     * @param array $data
     * @return array|bool|string
     */
    public function getCamerasInfo($data)
    {
        $url = $this->artemis . '/api/resource/v1/cameras/indexCode';
        //请求参数
        $params                    = [];
        $params['cameraIndexCode'] = $data['cameraIndexCode'];

        $this->sign = $this->get_sign($url);
        $result     = $this->curlPost($this->pre_url . $url, json_encode($params));
        return $result;
    }

    //获取编码设备信息
    public function getTranscodeInfo()
    {
        $url = $this->artemis . '/api/resource/v1/encodeDevice/get';
        //请求参数
        $params             = [];
        $params['pageNo']   = 1;
        $params['pageSize'] = 1;

        $this->sign = $this->get_sign($url);
        $result     = $this->curlPost($this->pre_url . $url, json_encode($params));
        return $result;
    }

    //根据区域获取下级编码设备列表
    public function getTest()
    {
        $url = $this->artemis . '/api/resource/v1/encodeDevice/subResources';
        //请求参数
        $params             = [];
        $params['pageNo']   = 1;
        $params['pageSize'] = 1000;
        $params['regionIndexCode'] = Input::get('indexCode');

        $this->sign = $this->get_sign($url);
        $result     = $this->curlPost($this->pre_url . $url, json_encode($params));
        $result = json_decode($result, true);
        return $result;
    }

    //获取编码设备列表
    public function getTranscodeList()
    {
        $url = $this->artemis . '/api/resource/v1/encodeDevice/subResources';
        //请求参数
        $params['regionIndexCode'] = 'root000000';
        $params['pageNo']          = 1;
        $params['pageSize']        = 100;

        $this->sign = $this->get_sign($url);
        $result     = $this->curlPost($this->pre_url . $url, json_encode($params));
        $result     = json_decode($result, true);
        return $result;
    }

    public function getRegionsInfo()
    {
        $url = $this->artemis . '/api/resource/v1/region/regionCatalog/regionInfo';
        //请求参数
        $params = [];

        $indexCodes = Input::get('indexCodes');
        $params['indexCodes'] = $indexCodes;

        $this->sign = $this->get_sign($url);

        $result = $this->curlPost($this->pre_url . $url, json_encode($params));
        $result = json_decode($result, true);
//        if (isset($result['code']) && $result['code'] == 0) {
//        }

        return $result;
    }

    public function getHikRtsp(){
        $result['status'] = true;
        $result['msg']    = ApiEnum::RETURN_SUCCESS;

        $query = CameraList::where('id',Input::get('camera_id'))->first();

        if (!$query) {
            $result['status'] = false;
            $result['msg'] = ApiEnum::NO_CAMERA_URL;
            return $result;
        }

        if($query->code_stream == 2){
            $code_stream = 0;
        }else{
            $code_stream = 1;
        }

        $url        = $this->artemis.'/api/video/v1/cameras/previewURLs';
        $params['cameraIndexCode'] = $query->index_code;
        $params['streamType']      = $code_stream;  //0主码流 1子码流 2第三码流
        $params['protocol']        = 'rtsp';
        $params['transmode']       = 1;  //0:UDP 1:TCP
        $params['expand'] = 'streamform=rtp';

        $this->sign = $this->get_sign($url);

        $rtsp = $this->curlPost($this->pre_url . $url, json_encode($params));

        $rtsp = json_decode($rtsp, true);

        $result['data'] = [
            'camera_id' => Input::get('camera_id'),
            'url' => $rtsp['data']['url']
        ];

        return $result;
    }

    //海康easy_drawin获取rtsp并推流
    public function getRtspByCameraId(){
        $result['status'] = true;
        $result['msg']    = ApiEnum::RETURN_SUCCESS;

        $query = CameraList::where('id',Input::get('camera_id'))->first();

        if (!$query) {
            $result['status'] = false;
            $result['msg'] = ApiEnum::NO_CAMERA_URL;
            return $result;
        }

        $degree = MineList::where('id', $query->mine_id)->value('degree');

        $mine_list = MineList::whereIn('id', explode('|', $degree))->select(['title'])->get()->toArray();

        $path = '';
//        foreach ($mine_list as $key => $val) {
//            $path .= '/' . $val['title'];
//        }
        $path = $mine_list[0]['title'];
        $new_camera_name = str_replace('-','',$query->camera_name);
        $new_camera_name = str_replace('#','号',$new_camera_name);
        $path .= '/' . $new_camera_name;

        //标记摄像头为在线状态
        CameraList::where('id', Input::get('camera_id'))->update(['camera_status' => CameraEnum::CAMERA_STATUS_ONLINE]);

        //已有拉流id，直接返回拉流链接
        if($query->revert_id != 'NullId'){
            $result['data'] = [
                'camera_id' => Input::get('camera_id'),
                'url' => env('EASY_DARWIN_JF_RTSP') .  $path
            ];
            return  $result;
        }

        if($query->code_stream == 2){
            $code_stream = 0;
        }else{
            $code_stream = 1;
        }

        $url        = $this->artemis.'/api/video/v1/cameras/previewURLs';
        $params['cameraIndexCode'] = $query->index_code;
        $params['streamType']      = $code_stream;  //0主码流 1子码流 2第三码流
        $params['protocol']        = 'rtsp';
        $params['transmode']       = 1;  //0:UDP 1:TCP
        $params['expand'] = 'streamform=rtp';

        $this->sign = $this->get_sign($url);

        $rtsp = $this->curlPost($this->pre_url . $url, json_encode($params));

        $rtsp = json_decode($rtsp, true);

        $data = [
            'url'        => $rtsp['data']['url'],
            'customPath' => $path,
        ];
        // dd($data);
        Log::info('请求流媒体服务器参数------------');
        Log::info($data);

        $curl = env('EASY_DARWIN_JF_URL') . CameraEnum::API_STREAM_START . '?' . http_build_query($data);

        //请求流媒体服务器拉流
        $curl_res = curl_request($curl);

        // DB::table('camera_list')->where('id',$camera_id)->update($res);
        // dd($res);
        Log::info('请求流媒体服务器result------------');
        Log::info($curl_res);

        if (strpos($curl_res,' ')) {
            //切换码流重新获取rtsp
            if($code_stream == 1){
                $code_stream = 0;
            }else{
                $code_stream = 1;
            }

            $params['streamType'] = $code_stream;  //0主码流 1子码流 2第三码流

            $rtsp = $this->curlPost($this->pre_url . $url, json_encode($params));

            $rtsp = json_decode($rtsp, true);

            $data = [
                'url'        => $rtsp['data']['url'],
                'customPath' => $path,
            ];
            // dd($data);
            Log::info('请求流媒体服务器参数------------');
            Log::info($data);

            $curl = env('EASY_DARWIN_JF_URL') . CameraEnum::API_STREAM_START . '?' . http_build_query($data);

            //请求流媒体服务器拉流
            $curl_res = curl_request($curl);

            if(strpos($curl_res,' ')){
                //标记摄像头为异常状态
                CameraList::where('id', Input::get('camera_id'))->update(['camera_status' => CameraEnum::CAMERA_STATUS_ERROR]);

                $result['status'] = false;
                $result['msg']    = ApiEnum::EASY_DAWIN_REQUEST_FAIL;
                return $result;
            }
        }

        //更新拉流id
        CameraList::where('id', Input::get('camera_id'))->update(
            [
                'revert_id'  => trim($curl_res, '"'),
                'updated_at' => date('Y-m-d H:i:s'),
            ]
        );

        $result['data'] = [
            'camera_id' => Input::get('camera_id'),
            'url' => env('EASY_DARWIN_JF_RTSP') . $path
        ];

        //查询是否存在封面图
        $cover_picture = CameraList::where('id', Input::get('camera_id'))->value('cover_picture');

        if (!$cover_picture) {
            if (!is_dir('/www/wwwroot/video.nxjiewei.com/public/' . CameraEnum::M3U8_FILE_PATH . '/' . $path)) {
                mkdir('/www/wwwroot/video.nxjiewei.com/public/' . CameraEnum::M3U8_FILE_PATH . '/' . $path, 0777, true);
            }
            // dd($result_td['content'][0]['url']);
            $exec = 'nohup /usr/bin/ffmpeg -i "' . $rtsp['data']['url'] . '" -vcodec copy -acodec copy -vbsf h264_mp4toannexb -f hls -hls_flags delete_segments -segment_list_size 10 -hls_list_size 2 /www/wwwroot/video.nxjiewei.com/public/' . CameraEnum::M3U8_FILE_PATH . '/' . $path . '/' . CameraEnum::M3U8_FILE_NAME . ' >/dev/null &';

            shell_exec($exec);

            //sleep10秒生成文件
            $i = 1;
            while ($i <= 10) {
                //判断m3u8文件是否存在
                $file_exists = file_exists(public_path() . '/' . CameraEnum::M3U8_FILE_PATH . $path . '/' . CameraEnum::M3U8_FILE_NAME);

                //判断ts文件是否存在
                $ts_exists = glob(public_path() . '/' . CameraEnum::M3U8_FILE_PATH . $path  . '/' . CameraEnum::TS_FILE_SUFFIX);

                if ($file_exists && $ts_exists) {

                    //获取第一帧作为封面图
                    $file_name = time() . CameraEnum::COVER_PICTURE_NAME;

                    $cover_exec = 'ffmpeg -i ' . public_path() . '/' . CameraEnum::M3U8_FILE_PATH . $path  . '/' . CameraEnum::M3U8_FILE_NAME . ' -vf "select=between(mod(n\, 25)\, 0\, 0), setpts=N/24/TB" ' . public_path() . '/' . CameraEnum::M3U8_FILE_PATH . $path  . '/' . $file_name;
                    exec($cover_exec);

                    //判断图片是否存在
                    $picture_exists = file_exists(public_path() . '/' . CameraEnum::M3U8_FILE_PATH . $path  . '/' . $file_name);

                    if ($picture_exists) {
                        $cms = new CameraServices();

                        //上传图片到oss并更新数据库
                        $picture_path = $cms->uploadOssFile($file_name, public_path() . '/' . CameraEnum::M3U8_FILE_PATH . $path  . '/');

                        CameraList::where('id', Input::get('camera_id'))->update(['cover_picture' => $picture_path]);
                    }

                    return $result;
                } else {
                    sleep(1);
                }

                $i++;
            }
        }else{
            return $result;
        }

        //标记摄像头为异常状态
        CameraList::where('id', Input::get('camera_id'))->update(['camera_status' => CameraEnum::CAMERA_STATUS_ERROR]);

        $result['status'] = false;
        $result['msg']    = ApiEnum::EASY_DAWIN_REQUEST_FAIL;

        return $result;
    }

    /**
     * 以appSecret为密钥，使用HmacSHA256算法对签名字符串生成消息摘要，对消息摘要使用BASE64算法生成签名（签名过程中的编码方式全为UTF-8）
     */
    protected function get_sign($url)
    {
        $sign_str = $this->get_sign_str($url); //签名字符串
        $priKey   = $this->app_secret;
        $sign     = hash_hmac('sha256', $sign_str, $priKey, true); //生成消息摘要
        $result   = base64_encode($sign);
        return $result;
    }

    /**
     * 生成签名
     * @param $url
     * @return string
     */
    protected function get_sign_str($url)
    {
        // $next = "\n";
        $next = "\n";
        $str  = "POST" . $next . '*/*' . $next . 'application/json' . $next;
        $str  .= "x-ca-key:" . $this->app_key . $next;
        $str  .= "x-ca-timestamp:" . $this->time . $next;
        $str  .= $url;
        return $str;
    }

    /**
     * 发送请求
     * @param string $url
     * @param string $postData
     * @param array  $options
     * @return bool|string
     */
    public function curlPost($url = '', $postData = '', $options = array())
    {
        if (is_array($postData)) {
            $postData = http_build_query($postData);
        }
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数

        curl_setopt_array($ch, array(
            CURLOPT_HTTPHEADER => array(
                "Accept:" . '*/*',
                "Content-Type:" . 'application/json',
                "x-Ca-Key:" . $this->app_key,
                "X-Ca-Signature:" . $this->sign,
                "X-Ca-Timestamp:" . $this->time,
                "X-Ca-Signature-Headers:" . "x-ca-key,x-ca-timestamp",
            )
        ));

        //https请求 不验证证书和host
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $data = curl_exec($ch);
        curl_close($ch);
        return $data;
    }
}
