#!/usr/bin/php
<?php

define('APL_VAR_PATH', $_SERVER['APL_VAR']);

$DEFAULTS = array('PROBE'=>'NORMAL','HTTP_PORT'=>80,'DEVID'=>0);
$HTTP_TIMEOUT = 15;

ini_set('default_socket_timeout', $HTTP_TIMEOUT);

$wsdlDir = dirname(__FILE__) . "/../wsdl";
$conf = array();
$args = array();
$result = array('MODELID'=>'0','FIRMWARE'=>'0.0','STATUS'=>'OK','RC_SET'=>'NONE','AUDIO_SET'=>'NONE');
$soapPort = 80;
$soapAuth = SOAP_AUTHENTICATION_BASIC;
$useStreamConfig = true;    // Whether camera support StreamConfiguration API
$settingToFind = null;

function findSetting($s)
{
    global $settingToFind;
    return isset($s->name) && $settingToFind!=null && $s->name == $settingToFind;
}

function probeResult(array $res) {
    global $conf;
    $dev = $conf["DEVID"];
    ksort($res);
    if ($dev) {
        if ($f = fopen(APL_VAR_PATH . "/conf/$dev/conf.probe", "w")) {
            foreach ($res as $k => $v) {
                fwrite($f, "$k=$v\n");
            }
            fclose($f);
        }
    }

    foreach ($res as $k => $v) {
        print "$k=$v\n";
    }
    if ($res["STATUS"] === "ERROR") exit(1);
    exit(0);
}

function probeErr($err, $text, $comments = "") {
    global $args;
    $dev = $args["DEVID"];
    if (strlen($comments) && $comments) {
        $comments = "\n#" . join("\n#", explode("\n",$comments));
    }
    probeResult(array("STATUS" => "ERROR: $err [$dev] $text $comments"));
}

function loadConf($devid) {
    $cfg = array();
    $confPath = APL_VAR_PATH . "/conf/" . $devid . "/conf";
    if (!is_file($confPath))
        return null;
    $content = file_get_contents($confPath);
    $lines = explode("\n", $content);
    foreach ($lines as $line) {
        if (preg_match("/^(\\w+)=(.+)$/", $line, $matches)) {
            $cfg[$matches[1]] = $matches[2];
        }
    }

    return $cfg;
}

function probeRequest ($path, $isPOST = false, array $content = null, array $hdr = null) {
    global $HTTP_TIMEOUT;
    global $conf;

    $devip = $conf["DEVIP"];
    $port = isset($conf["HTTP_PORT"]) ? $conf["HTTP_PORT"] : 80;
    $usrname = $conf["USRNAME"];
    $passwd = $conf["PASSWD"];

    $url = "http://".$devip.":".$port.$path;
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HTTPAUTH, base64_encode($usrname.":".$passwd));
    curl_setopt($ch, CURLOPT_POST, $isPOST);
    if ($isPOST && $content != null) {
        $fld = "";
        foreach ($content as $k => $v) {
            if (strlen($fld) > 0) $fld .= "&";
            $fld .= urlencode($k)."=".urlencode($v);
        }
        curl_setopt($ch, CURLOPT_POSTFIELDS, $fld);
    }
    if ($hdr !== null) {
        curl_setopt($ch, CURLOPT_HTTPHEADER, $hdr);
    }
    curl_setopt ($ch, CURLOPT_USERAGENT, "SKM Probe");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, $HTTP_TIMEOUT);
    $respond = curl_exec ($ch);
    $httpCode = intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
    curl_close ($ch);

    $message = $respond;
    $error = false;
    if ($httpCode >= 400) {
        $message = "HTTP ERROR [$httpCode]\n$url";
        $error = true;
    }
    return array('respond' => $respond, 'message' => $message, 'code' => $httpCode, 'error' => $error);
}

function probeInit() {
    global $argv;
    global $args;
    global $DEFAULTS;
    global $conf;

    $parts = array_reverse(explode(DIRECTORY_SEPARATOR, __FILE__));
    $device = $parts[3]; $brand = $parts[2]; $dir = $parts[1];
    if (! preg_match("/^(camera|audio|sensor|relay)$/", $device)) {
        error_log(__FILE__." should have a correct location ( device/<BRAND>/bin/probe) \n");
    }
    if ($dir !== "bin") {
        error_log(__FILE__." should have a correct location ( device/<BRAND>/bin/probe) \n");
    }
    // Parse command line
    foreach ($argv as $arg) {
        if (preg_match("/^(\\w+)=(.+)$/", $arg, $matches)) {
            $args[$matches[1]] = $matches[2];
        }
    }

    $args = array_merge($DEFAULTS, $args);
    $conf = $args;
    if ($conf["DEVID"] != '0') unset($conf["DEVID"]);
    $conf["_probe_brand"] = $brand;
    $conf["_probe_device"] = $device;
    $cfg = loadConf($args["DEVID"]);
    if ($cfg === null)
        return;

    $conf = array_merge($DEFAULTS, $cfg, $args, array('_probe_brand'=>$brand,'_probe_device'=>$device));
}

function _probePortAuth($port, $auth) {
    global $conf;
    global $wsdlDir;

    $resp = array(
        "ok" => false,
        "error" => ""
    );
    try {
        $pelcoConfigClient = new SoapClient($wsdlDir . '/PelcoConfigurationV1.wsdl',
            array("location" => "http://".$conf["DEVIP"].":". $port . "/control/PelcoConfiguration-1",
                  "login" => $conf["USRNAME"],
                  "password" => $conf["PASSWD"],
                  "authentication" => $auth));

        $response = $pelcoConfigClient->GetModelName();
        $resp["MODELID"] = $response->modelName;
        $resp["ok"] = true;
        $resp["port"] = $port;
        $resp["auth"] = $auth;
    } catch (Exception $e) {
        $resp["ok"] = false;
        $resp["error"] = $e->getMessage();
    }
    
    return $resp;
}

function probeModel() {
    global $conf;
    global $wsdlDir;
    global $result;
    global $soapPort;
    global $soapAuth;

    $errMsg = "";

    $testPorts = array(80, 49152, 49153, 49154, 49155, 49156);
    $portFound = false;

    foreach ($testPorts as $port) {
        $auth = SOAP_AUTHENTICATION_BASIC;
        $ok = false;
        $resp = _probePortAuth($port, $auth);
        //echo "Probing port=$port  auth=BASIC OK=".$resp["ok"]." ERR=".$resp["error"]."\n";
        if ($resp["ok"] === false) {
            if (@stristr($resp["error"], "Could not connect") === false) {
                $auth = SOAP_AUTHENTICATION_DIGEST;
                $resp = _probePortAuth($port, $auth);
                //echo "Probing port=$port  auth=DIGEST OK=".$resp["ok"]." ERR=".$resp["error"]."\n";
            }
            $ok = $resp["ok"];
        }
        else {
            $ok = true;
        }
        
        if ($ok === true) {
            $result["MODELID"] = $resp["MODELID"];
            $soapPort = $port;
            $portFound = true;
            $errMsg = "";
            $soapAuth = $auth;
            break;
        }
        else {
            $errMsg .= "(port=$port) " . $resp["error"]. "\n";
            continue;
        }
    }

    if (!$portFound || !$result["MODELID"]) {
        probeErr("PCE-0030","Cannot get MODELID or FIRMWARE", $errMsg);
    }
}

function probeCapabilities() {
    global $conf;
    global $wsdlDir;
    global $result;
    global $useStreamConfig;
    global $soapPort;
    global $soapAuth;
    global $settingToFind;

    $streamConfigClient = new SoapClient($wsdlDir . '/StreamConfigurationV1.wsdl',
        array("location" => "http://".$conf["DEVIP"].":" . $soapPort .
                            "/control/StreamConfiguration-1",
              "login" => $conf["USRNAME"],
              "password" => $conf["PASSWD"],
              "authentication" => $soapAuth
        ));
    try {
        $response = $streamConfigClient->QueryAllOptions();
        $currentSettings = $streamConfigClient->GetAllSettings();
    } catch (Exception $e) {
        $useStreamConfig = false;
    }
    if ($useStreamConfig) {
        $settingOptions = $response->result->streamInfo[0]->result->settingOptions;

        $fmtList = array();
        $resList = array();

        foreach ($settingOptions as $setting) {
            if ($setting->name === "encoding") { // Available media formats
                $formats = $setting->stringEnumeration->value;
                if (is_array($formats)) {
                    foreach ($formats as $fmt) {
                        if (preg_match("/^(JPEG|MJPEG)$/i",$fmt)) {
                           $fmtList[] = "mjpg";
                        } elseif (preg_match("/^(H\\.?264)$/i",$fmt)) {
                            $fmtList[] = "h264";
                        } elseif (preg_match("/^(MPEG-?4)$/i",$fmt)) {
                            $fmtList[] = "mpeg4";
                        }
                    }
                }
            }
            elseif ($setting->name === "resolution") { // Available resolutions
                $resolutions = $setting->resolutionEnumeration->value;
                $settings = &$currentSettings->streamSettingsList->streamIDSettings[0]->settings->setting;
                $settingToFind = "resolution";
                $resolutionItem = array_filter($settings, 'findSetting');
                $resolutionItemKey = key($resolutionItem);
                $resolutionItem = &$settings[$resolutionItemKey];
                $settingToFind = "bitrate";
                $bitrateItem = array_filter($settings, 'findSetting');
                $settings[key($bitrateItem)]=array_pop ( $settings );
                $framerates = "";
                if (is_array($resolutions)) {
                    foreach ($resolutions as $res) {
                        $resList[] = $res->width . "x" . $res->height;
                        $resolutionItem->resolutionValue->width = $res->width;
                        $resolutionItem->resolutionValue->height = $res->height;
                        $queryOptionsRequest = (object) array(
                            "stream" => $currentSettings->streamSettingsList->streamIDSettings[0]->stream,
                            "filter" => $currentSettings->streamSettingsList->streamIDSettings[0]->settings
                        );
                        try {
                            $compatibleSettings = $streamConfigClient->QueryOptions($queryOptionsRequest);
                            $settingToFind = "framerate";
                            $framerateItem = array_filter($compatibleSettings->result->settingOptions, 'findSetting');
                            $framerateItem = array_shift($framerateItem);
							$framerate = "";
							try {
								$framerate = $res->width . "x" . $res->height.":" . join("\\,",$framerateItem->decimalEnumeration->value).',';
							} catch(Exception $e)
							{
								$framerate = "";
							}
							$framerates .= $framerate;

                        }
                        catch (Exception $e) { }

                    }
                    $result['FRAMERATE_LIST']=rtrim($framerates,',');
                }
            }
        }

        $result["MEDIA_FORMAT_LIST"] =join(",", $fmtList);
        $result["IMAGESIZE_LIST"] = join(",", $resList);
    }
    else {
        // Stream config doesn't work
        // Use pre-defined options
    }
}

function cameraConfig() {
    global $conf;
    global $wsdlDir;
    global $soapPort;
    global $soapAuth;

    $camConfigClient = new SoapClient($wsdlDir . '/CameraConfigurationV1.wsdl',
        array("location" => "http://".$conf["DEVIP"].":" . $soapPort .
                            "/control/CameraConfiguration-1",
                            "login" => $conf["USRNAME"],
                            "password" => $conf["PASSWD"],
                            "authentication" => $soapAuth));
    try {
        $response = $camConfigClient->GetConfiguration();

        $response->cameraConfig->localQuality = 1;

        $videopair = $response->cameraConfig->videopair;
        $channel = $videopair->primary;
        $channel->tvformat = 0; // NTSC
        $channel->framerate = $conf["FRAMERATE"];
        if (preg_match("/^\\d+$/", $conf["IMAGESIZE"])) {
            $channel->videosize = $conf["IMAGESIZE"];
        }
        if ($conf["MEDIA_FORMAT"] === "mpeg4" || $conf["MEDIA_FORMAT"] === "h264") {
    	    $channel->format = $conf["MEDIA_FORMAT"] === "mpeg4" ? "Mpeg-4" : "H.264";
        }
        if (isset($conf["RC_GOP_DISTANCE"]))
            $channel->gop = $conf["RC_GOP_DISTANCE"];
        if ($conf["RC_TARGETBITRATE"] !== "cam-defined")
            $channel->bitrate = $conf["RC_TARGETBITRATE"] * 1000;

        $camConfigClient->SetConfiguration($response);

    } catch (Exception $e) {
        return false;
    }

    return true;
}

function streamConfig() {
    global $conf;
    global $wsdlDir;
    global $soapPort;
    global $soapAuth;
    global $result;
    global $settingToFind;

    $gop = false;

    $streamConfigClient = new SoapClient($wsdlDir .'/StreamConfigurationV1.wsdl',
        array("location" => "http://".$conf["DEVIP"].":" . $soapPort .
                            "/control/StreamConfiguration-1",
                            "login" => $conf["USRNAME"],
                            "password" => $conf["PASSWD"],
                            "authentication" => $soapAuth));
    try {
        // First query all options
        $allOptions = $streamConfigClient->QueryAllOptions();
        $settingOptions = $allOptions->result->streamInfo[0]->result->settingOptions;
        $fmtMap = array();
        $resList = array();
        $fpsList = array();

        foreach ($settingOptions as $setting) {
            if ($setting->name === "encoding") { // Available media formats
                $formats = $setting->stringEnumeration->value;
                if (is_array($formats)) {
                    foreach ($formats as $fmt) {
                        if (preg_match("/^(JPEG|MJPEG)$/i",$fmt)) {
                           $fmtMap["mjpg"] = $fmt;
                        } elseif (preg_match("/^(H\\.?264)$/i",$fmt)) {
                            $fmtMap["h264"] = $fmt;
                        } elseif (preg_match("/^(MPEG-?4)$/i",$fmt)) {
                            $fmtMap["mpeg4"] = $fmt;
                        }
                    }
                }
            }
            elseif ($setting->name === "resolution") { // Available resolutions
                $resolutions = $setting->resolutionEnumeration->value;
                if (is_array($resolutions)) {
                    foreach ($resolutions as $res) {
                        $resList[] = $res->width . "x" . $res->height;
                    }
                }
            }
            elseif ($setting->name === "framerate") {
                if (is_array($setting->decimalEnumeration->value)) {
                    $fpsList = $setting->decimalEnumeration->value;
                    rsort($fpsList);
                }
            }
        }

        // Then get current stream settings
        $response = $streamConfigClient->GetAllSettings();

        $settings = &$response->streamSettingsList->streamIDSettings[0]->settings->setting;
        if (is_array($settings)) {
            foreach ($settings as &$setting) {
                if ($setting->name === "encoding") { // Set MEDIA_FORMAT
                    $localVal = $conf["MEDIA_FORMAT"];
                    if (isset($fmtMap[$localVal])) {
                        $setting->stringValue = $fmtMap[$localVal];
                    }
                }
                elseif ($setting->name === "framerate") {
                    $localVal = $conf["FRAMERATE"];
                    if (in_array($localVal, $fpsList)) {
                        $setting->decimalValue = $localVal;
                    } else {
                        foreach ($fpsList as $fps) {
                            if ($fps < $localVal) {
                                $setting->decimalValue = $fps;
                                break;
                            }
                        }
                    }
                }
                elseif ($setting->name === "gop") {
                    if (isset($conf["RC_GOP_DISTANCE"])) {
                	$gop = true;
                        $setting->intValue = $conf["RC_GOP_DISTANCE"];
                    }
                }
                elseif ($setting->name === "resolution") {
                    $localVal = $conf["IMAGESIZE"];
                    if (preg_match("/^\\d+$/",$localVal)) { // Index
                        if ($result["MODELID"] === 'TXB-N' || $result["MODELID"] === 'IM10') {
                            if ($localVal == 1) {
                                $setting->resolutionValue->width = 352;
                                $setting->resolutionValue->height = 240;
                            }
                            elseif ($localVal == 3) {
                                $setting->resolutionValue->width = 704;
                                $setting->resolutionValue->height = 480;
                            }
                        }
                        elseif (count($resList) > $localVal) {
                            list($width,$height) = explode("x", $resList[$localVal]);
                            $setting->resolutionValue->width = $width;
                            $setting->resolutionValue->height = $height;
                        }
                    }
                    elseif (preg_match("/^(\\d+)x(\\d+)$/", $localVal, $matches)) {
                        $setting->resolutionValue->width = $matches[1];
                        $setting->resolutionValue->height = $matches[2];
                    }
                }
                elseif($setting->name === "bitrate") {
                    if (isset($conf["RC_TARGETBITRATE"]) && $conf["RC_TARGETBITRATE"] !== "cam-defined") {
                        $setting->intValue = $conf["RC_TARGETBITRATE"] * 1000;
                    }
                }
            }
            if (preg_match("/^IM10LW10/", $result["MODELID"]) || preg_match("/^TXB-N/", $result["MODELID"])
        	|| preg_match("/^ESTI6/", $result["MODELID"]) || preg_match("/^D5230/", $result["MODELID"])
        	|| preg_match("/^TI3/", $result["MODELID"]))
            {
                  $response->streamSettingsList->streamIDSettings[1]->settings = new stdClass();
                  $settingToFind = "bitrate";
                  $bitrate = array_filter($settings,"findSetting");
                  if(is_array($bitrate))
                  {
                    $bitrate_key = key($bitrate);
                    unset($response->streamSettingsList->streamIDSettings[0]->settings->setting[$bitrate_key]);
                    for($i=$bitrate_key+1;$i<count($settings)+1;++$i)
                    {
                        $response->streamSettingsList->streamIDSettings[0]->settings->setting[$i-1] = $response->streamSettingsList->streamIDSettings[0]->settings->setting[$i];
                        unset($response->streamSettingsList->streamIDSettings[0]->settings->setting[$i]);
                    }
                    $queryOptionsRequest = (object) array(
                        "stream" => $response->streamSettingsList->streamIDSettings[0]->stream,
                        "filter" => $response->streamSettingsList->streamIDSettings[0]->settings
                    );
                    try {
                        $compatibleSettings = $streamConfigClient->QueryOptions($queryOptionsRequest);
                    }
                    catch (Exception $e)
                    {
                        return;
                    }
                    $compatibleBitrate = array_filter($compatibleSettings->result->settingOptions ,"findSetting");
                    if(is_array($compatibleBitrate))
                    {
                        $bitrateItem = array_shift($compatibleBitrate);
                        $allowedBitrate = $bitrateItem->intRange->default;
                        $bitrate[$bitrate_key]->intValue = $allowedBitrate;
                    }

                    $response->streamSettingsList->streamIDSettings[0]->settings->setting[$bitrate_key] = array_shift($bitrate);
                  }
            }
            
            if(!$gop && preg_match("/^IXP31/", $result["MODELID"]) && $conf["MEDIA_FORMAT"] == 'h264' && isset($conf["RC_GOP_DISTANCE"])) {
		$response->streamSettingsList->streamIDSettings[0]->settings->setting[] = (object) array(
		    "name" => "gop",
		    "intValue" => $conf["RC_GOP_DISTANCE"]
		);
	    }
        }

        // Finally save all settings
        $req = new stdClass();
        $req->streamType = "video";
        $req->streamSettingsList = $response->streamSettingsList;
        $streamConfigClient->SaveAllSettings($req);

    } catch (Exception $e) {
        return false;
    }

    return true;
}

function configure() {
    global $useStreamConfig;
    global $result;

    if ($useStreamConfig) {
        $ok = streamConfig();
        $result["SNAPSHOT_MODE"] = "on";
    }
    else {
        $ok = cameraConfig();
    }

    if (!$ok) {
        $result["RC_SET"] = "ERROR: Update configuration failed";
    } else {
        $result["RC_SET"] = "OK";
    }
}

function main() {
    global $conf;
    global $result;
    global $soapPort;

    probeInit();

    if (! isset($conf["DEVID"])) probeErr("PCE-0001","configuration is not found");
    if (! isset($conf["DEVIP"])) probeErr("PCE-0003","DEVIP is not defined");
    if (! isset($conf["USRNAME"]) || ! isset($conf["PASSWD"]))
        probeErr("PCE-0002","USRNAME and PASSWD should be provided");

    // Do SOAP requests
    probeModel();

    if ($conf["PROBE"] === "DEFINE" || $conf["DEVID"] != 0)
        probeCapabilities();

    if ($conf["PROBE"] === "DEFINE" || $conf["PROBE"] === "FAST" || $conf["DEVID"] == 0)
        probeResult($result);

    $result["PELCO_SOAP_PORT"] = $soapPort;

    configure();

    probeResult($result);
}

main();
