<?php

require_once dirname(__FILE__) . '/../../lib/phoneprovisioning.php';
require_once dirname(__FILE__) . '/../tiptel-32xx/Tiptel32xx.php';
require_once dirname(__FILE__) . '/../tiptel-32xx/Tiptel32xxTimeZone.php';
require_once "TimezoneHelper.php";

class TiptelPhoneProvisioning extends VirtualPhoneProvisioningController
{

    const LOG_TAG = 'tiptel32xx-dls';

    protected function getXMLURL($app)
    {
        $url = parent::getProvURL();
        return $url . $app . "/" . $this->mMacAddress . ".xml";
    }

    protected function getPhonebookURL()
    {
        return $this->getXMLURL('pb');
    }

    protected function getLoginAppURL()
    {
        return $this->getXMLURL('login');
    }

    protected function getConfigAppURL()
    {
        return $this->getXMLURL('cfgapp');
    }

    protected function getVoicemailAppURL()
    {
        return $this->getXMLURL('voicemail');
    }

    protected function checkUserAgent($ua)
    {
        Log::d("Requested Provisioning " . $ua, self::LOG_TAG);
        $ua_parts = explode(' ', $ua);
        $manufactorName = strToLower(@$ua_parts[0]);
        $phoneModelName = @$ua_parts[1];

        if ($manufactorName === 'tiptel' && startsWith($phoneModelName, '32')) {
            $found = false;
            $agentName = $manufactorName . $phoneModelName;

            foreach ($this->_getSupportedPhoneModels() as $model) {
                if ($model->getUserAgent() == $agentName) {
                    $this->setCurrentPhoneModel($model);
                    $found = true;
                    break;
                }
            }

            $firmware = @$ua_parts[2];
            # find out the firmware version of the phone
            $fw_vers = (preg_match('/(\d+\.\d+\.\d+\.\d+)/', $firmware, $m)) ? $m[1] : '0.0.0.0';
            $fw_vers_nrml = $this->tiptel_normalize_version($fw_vers);
            $this->setCurrentFirmwareVersion($fw_vers_nrml);
            return $found;
        }
        return parent::checkUserAgent($ua);
    }

    protected function _getSupportedPhoneModels()
    {
        return Tiptel32xx::getSupportedPhoneModels();
    }

    private function tiptel_normalize_version($fwvers)
    {
        return Tiptel32xx::tiptel_normalize_version($fwvers);
    }

    protected function runFirmwareUpdate(Firmware $fwUpdate)
    {
        Log::d("Run firmware-update to version '$fwUpdate->version' for '$fwUpdate->phoneType'", self::LOG_TAG);
        $this->psetting('Config.Firmware.Url', $this->getFirmwareURL($fwUpdate));
    }

    protected function getFirmwareURL($fwUpdate)
    {
        $protocol = cfg('PROV_PROTOCOL');
        $host = cfg('PROV_HOST');
        $port = cfg('PROV_PORT');

        if ($port != '') {
            $port = ":" . $port;
        }
        return $protocol . $host . $port . '/firmware/' . $fwUpdate->fileName;
    }

    protected function loadConfigValues($mac = null)
    {
        Log::d("Load config values for $mac", self::LOG_TAG);

        $template = $this->getTemplate();
        $Phone = $this->getPhoneModel();
        $SipAccount = $Phone->getSipAccount();
        $User = $SipAccount->getUser();
        $label = $this->getAccountLabel($User, $SipAccount);

        foreach ($template as $key => $value) {
            $this->psetting($key, $value);
        }

        $this->psetting('Config.Account1.SIP.Server', $SipAccount->getSIPRegistrar());
        $this->psetting('Config.Account1.SIP.Port', cfg("INTERNAL_SIP_PORT"));

        $this->psetting('Config.Firmware.Url', '');
        $this->psetting('Config.Autoprovision.General.Url', $this->getProvURL());

        $this->psetting('Config.Account1.GENERAL.Enable', '1');
        $this->psetting('Config.Account1.GENERAL.Label', $label);
        $this->psetting('Config.Account1.GENERAL.DisplayName', $label);
        $this->psetting('Config.Account1.GENERAL.UserName', $SipAccount->authname);
        $this->psetting('Config.Account1.GENERAL.AuthName', $SipAccount->authname);
        $this->psetting('Config.Account1.GENERAL.Pwd', $SipAccount->secret);

        $this->psetting('Config.Account1.REG.Timeout', $SipAccount->expires);
        $this->psetting('Config.Account1.SUBSCRIBE.SubscribeMWIExpire', $SipAccount->expires);
        $this->psetting('Config.Account1.SUBSCRIBE.SubscribeACDExpire', $SipAccount->expires);
        $this->psetting('Config.Account1.SUBSCRIBE.SubscribePeriod', $SipAccount->expires);
        $this->psetting('Config.Account1.BLF.SubscribePeriod', $SipAccount->expires);

        $this->psetting('Config.Account1.ENCRYPTION.SRTPEncryption', cfg("RTP_ENABLE_SRTP"));

        $this->psetting('Config.Settings.SNTP.Enable', '1');
        $this->psetting('Config.Settings.SNTP.DTS', date('I'));
        $this->psetting('Config.Settings.SNTP.NTPServer1', cfg('NTP_HOST'));
        $this->psetting('Config.Settings.SNTP.TimeZone', $this->getTimeZone($User));
        $this->psetting('Config.Settings.SNTP.Name', $this->getTimeZone($User, true));

        $this->psetting('Config.Settings.DateTime.TimeFormat', $this->getTimeFormat($User));
        $this->psetting('Config.Settings.DATETIME.DateFormat', $this->getDateFormat($User));

        $this->psetting('Config.Account1.FEATURE_DND.Enable', '1');
        $this->psetting('Config.Account1.FEATURE_DND.ReturnCode', '486');
        $this->psetting('Config.Account1.FEATURE_DND.OnCode', cfg('DP_DND_ON_PREFIX'));
        $this->psetting('Config.Account1.FEATURE_DND.OffCode', cfg('DP_DND_ON_PREFIX'));
        $this->psetting('Config.Settings.AUDIO.KeyVol', $this->getKeyToneSettings($User));

        $this->psetting('Config.Account1.Anonymous_Call.Enable', 0);
        $this->psetting('Config.Account1.VOICEMSG.Number', '80');

        $this->psetting('Config.Settings.LANGUAGE.Type', $this->getUserLanguage($User));
        $this->psetting('Config.Settings.LANGUAGE.WebLang', $this->getUserLanguage($User));

        $this->psetting('Config.Features.CALL_WAITING.Enable', $this->getCallWaitingSettings($User));

        $this->psetting('Config.Voice.TONE.Country', $this->getCountryTone($User));

        $this->psetting('Config.Account1.CALL.CIDSource', 2); //Caller ID Header: 0 = FROM, 1 = PAI, 2 = PAI-FROM, 3 = RPID-FROM, 4 = PAI-RPID-FROM, 5 = RPID-PAI-FROM

        $this->setXmlApps($User);
        $this->setAudioCodecs();
        $this->setRintones();
        $this->setVolumeSettings($User);
        $this->processSoftkeys();

        $this->changeSettingsWithProvisioningProfile($User, $Phone->type);
        return true;
    }

    protected function setXmlApps($User)
    {
        $language = $this->getUserLanguage($User);

        if (!$User) {
            $this->setXmlAppToKey('SOFTKEY03', $this->getLoginAppURL(), "Login");
        } else {
            $this->setXmlAppToKey('SOFTKEY02', $this->getPhonebookURL(), $this->getXmlPhonebookKeyName($language));
            $this->setXmlAppToKey('SOFTKEY03', $this->getVoicemailAppURL(), $this->getXmlVoicemailKeyName($language));
            $this->setXmlAppToKey('FWD', $this->getConfigAppURL());
        }
    }

    protected function setXmlAppToKey($key, $url, $label = '')
    {
        $this->psetting('Config.Programable.' . $key . '.Type', 21);
        if ($label != '') {
            $this->psetting('Config.Programable.' . $key . '.Label', $label);
        }
        $this->psetting('Config.Programable.' . $key . '.Param1', $url);
        $this->psetting('Config.Programable.' . $key . '.Param2', 1);
        $this->psetting('Config.Programable.' . $key . '.Param3', '');
    }

    protected function getXmlPhonebookKeyName($language)
    {
        $keyName = "Dir.";
        if ($language === "de_de") {
            $keyName = "Verz.";
        }
        return $keyName;
    }

    protected function getXmlVoicemailKeyName($language)
    {
        $keyName = "VMail";
        if ($language === "de_de") {
            $keyName = "AB";
        }
        return $keyName;
    }

    protected function getAccountLabel($User, $SipAccount)
    {
        $label = $SipAccount->getShortLabel(true);
        // check if user has callforwarding -> Change displayname!
        if ($User && $User->defaultCallForwardSet != $User->callForwardSet) {
            $preset = ForwardPreset::model()->findByPk($User->callForwardSet);
            if ($preset != NULL) {
                $label = $preset->name;
            } else {
                $label = "FWD?";
            }
        }
        return $label;
    }

    protected function getUserLanguage($User)
    {
        $language = $this->getLanguage($User);
        return $this->convertLanguageCode($language);
    }

    protected function getLanguage($User)
    {
        $language = cfg("GUI_LANGS");
        if ($User && $User->user_language != "") {
            $language = $User->user_language;
        }
        return $language;
    }

    public function convertLanguageCode($code)
    {
        $codes = array();
        $codes["en_us"] = 0;
        $codes["de_de"] = 10;
        $codes["fr_fr"] = 9;
        $codes["nl_nl"] = 8;

        if (array_key_exists($code, $codes)) {
            return $codes[$code];
        }
        return 0;
    }

    protected function getCallWaitingSettings($User)
    {
        $callwaiting = 0;
        if ($User && $User->call_waiting) {
            $callwaiting = 1;
        }
        return $callwaiting;
    }

    protected function getTimeFormat($User)
    {
        $time_format = cfg("USER_TIME_FORMAT");
        if ($User && $User->user_time_format != "") {
            $time_format = $User->user_time_format;
        }

        $convertedTimeFormat = 0;
        if ($time_format == "24") {
            $convertedTimeFormat = 1;
        }
        return $convertedTimeFormat;
    }

    protected function getDateFormat($User)
    {
        $date_format = cfg("USER_DATE_FORMAT");
        if ($User && $User->user_date_format != "") {
            $date_format = $User->user_date_format;
        }
        return $this->convertDateFormat($date_format);
    }

    protected function convertDateFormat($date_format)
    {
        $convertedDateFormat = 2;
        if ($date_format == 'yyyy-MM-dd') {
            $convertedDateFormat = 0;
        }
        return $convertedDateFormat;
    }

    protected function getTimeZone($User, $name = false)
    {
        $date_format = cfg("TIMEZONE");
        if ($User && $User->user_timezone != "") {
            $date_format = $User->user_timezone;
        }
        return $this->convertTimeZone($date_format, $name);
    }

    protected function convertTimeZone($date_format, $name)
    {
        $date_format = str_replace('/', '_', $date_format);
        $date_format = strtoupper($date_format);
        $timeZone = Tiptel32xxTimeZone::$$date_format;

        $result = $timeZone['zone'];
        if ($name) {
            $result = $timeZone['name'];
        }
        return $result;
    }

    protected function getCountryTone($User)
    {
        $language = $this->getLanguage($User);

        $codes = array();
        $codes["en_us"] = 1;
        $codes["de_de"] = 10;
        $codes["fr_fr"] = 11;
        $codes["nl_nl"] = 11;

        if (array_key_exists($language, $codes)) {
            return $codes[$language];
        }
        return 1;
    }

    protected function getKeyToneSettings($User)
    {
        $keytone = 0;
        if (isset($User->key_tone) && !empty($User->key_tone)) {
            $keytone = 8;
        }
        return $keytone;
    }

    protected function getExtTiptelModules()
    {
        $ext = array();
        $Phone = $this->getPhoneModel();
        $getSupportedPhoneModels = Tiptel32xx::getSupportedPhoneModels();
        $extModules = $getSupportedPhoneModels[$Phone->type]->getExtModules();
        if ($extModules != 0) {
            for ($i = 0; $i <= $extModules - 1; $i++) {
                $ext[] = "ext" . $i;
            }
        }
        return $ext;
    }

    protected function processSoftkeys()
    {
        $Phone = $this->getPhoneModel();
        $SipAccount = $Phone->getSipAccount();
        $User = $SipAccount->getUser();
        $ext = $this->getExtTiptelModules();

        $levels = array("line", "memory");

        if (!empty($ext)) {
            $levels = array_merge($levels, $ext);
        }

        foreach ($levels as $level) {
            $softKeys = $Phone->getCurrentPhoneKeys($User, $level);
            foreach ($softKeys as $softKey) {
                $this->processSoftKey($softKey);
            }
        }
    }

    protected function processSoftKey(PhoneKey $key)
    {
        if ($key->function === 'no') {
            return;
        }
        $parameter = $this->getParameterName($key);
        $keyFunction = $this->convertSoftKeyFunction($key->function, 16);
        $keyValue = $this->convertSoftKeyValue($key);
        $keyLabel = $key->label;
        $pickupValue = $this->convertPickupCode($key->function, array(16, 'X06'));
        $account = 1;

        if (strtolower($key->phone) === 'tiptel-3220xl' || strtolower($key->phone) === 'tiptel-3220xlr') {
            $keyFunction = 10;
            $keyValue = $this->convertSoftKeyValue($key);
            $keyLabel = '';
            $pickupValue = '';
            $account = '-1';
        }
        return $this->setSoftKeySettings($parameter, $keyFunction, $keyValue, $keyLabel, $pickupValue, $account);
    }

    public function getParameterName($key)
    {
        $parameterName = '';
        if ($key->level == 'line') {
            $parameterName = "Config.Programable.LineKey" . $key->key_idx;
        } else if ($key->level == 'memory') {
            $parameterKeyName = "DSSKEY";
            if (strtolower($key->phone) === 'tiptel-3220xl' || strtolower($key->phone) === 'tiptel-3220xlr') {
                $parameterKeyName = "MEMORYKEY";
            }
            $parameterName = "Config.Programable.$parameterKeyName" . str_pad((int) $key->key_idx, 2, '0', STR_PAD_LEFT);
        } else if (startsWith($key->level, 'ext')) {
            $key_idx = explode('.', $key->key_idx);
            $extModule = str_replace('ext', '', $key_idx[0]);
            $parameterName = 'Config.Em' . ($extModule + 1) . '.EXTKEY' . str_pad((int) $key_idx[1], 2, '0', STR_PAD_LEFT);
        }
        return $parameterName;
    }

    protected function convertSoftKeyFunction($keyFunction, $blfKey)
    {
        if (startsWith($keyFunction, 'X')) {
            $keyFunction = $blfKey;
        } else if ($keyFunction === '') {
            $keyFunction = 0;
        }
        return $keyFunction;
    }

    protected function setSoftKeySettings($parameter, $keyFunction, $keyValue, $keyLabel, $pickupValue, $account)
    {
        $this->psetting($parameter . '.Type', $keyFunction);
        $this->psetting($parameter . '.Label', $keyLabel);
        $this->psetting($parameter . '.Param1', $keyValue);     //Number
        $this->psetting($parameter . '.Param2', $account);      //Account
        $this->psetting($parameter . '.Param3', $pickupValue);  //BLF-Code
    }

    protected function generateOutput()
    {
        Log::d("Generate output", self::LOG_TAG);
        foreach ($this->mCurrentSettings as $pKey => $value) {
            echo "$pKey=$value", "\n";
        }
    }

    protected function getTemplate()
    {
        $contents = file_get_contents(dirname(__FILE__) . "/templates/tiptel-32xx.tpl");
        $contents = explode("\n", $contents);
        $config = array();
        foreach ($contents as $line) {
            $line = trim($line);
            if (strlen($line) == 0) {
                continue;
            }

            if ($line[0] == "#") {
                continue;
            }
            $parts = explode("=", $line);
            $key = trim($parts[0]);
            $value = trim(@$parts[1]);

            $config[$key] = $value;
        }
        return $config;
    }

    protected function psetting($sectionname, $val = '')
    {
        $this->mCurrentSettings[$sectionname] = $val;
    }

    protected function setAudioCodecs()
    {
        $phonalisaCodecs = str_replace(' ', '', cfg('GLOBAL_CODEC_PREFS'));
        $codecs = explode(',', $phonalisaCodecs);
        $phoneCodecs = $this->getPhoneCodecs();
        $accountCount = 6;

        $key = 12;
        foreach ($codecs as $value) {
            if (array_key_exists($value, $phoneCodecs)) {
                $phoneCodecs[$value]['enable'] = 1;
                $oldPriority = $phoneCodecs[$value]['priority'];
                $oldCodecWithPriority = $this->searchForPriority($key, $phoneCodecs);
                $phoneCodecs[$oldCodecWithPriority]['priority'] = $oldPriority;
                $phoneCodecs[$value]['priority'] = $key;
                $key--;
            }
        }

        for ($i = 0; $i <= $accountCount; $i++) {
            $this->setAudioCodecToSettings($phoneCodecs, $i);
        }
    }

    protected function getPhoneCodecs()
    {
        $codecs = TiptelPhoneProvisioning::getAudioPayloadCodecs();
        $result = array();
        $priority = 12;
        $key = 0;
        foreach ($codecs as $codec => $payload) {
            $result[$codec] = array(
                'key' => $key,
                'enable' => 0,
                'priority' => $priority,
                'payload' => $payload,
            );
            $priority--;
            $key++;
        }
        return $result;
    }

    protected static function getAudioPayloadCodecs()
    {
        return array(
            'PCMU' => 0,
            'PCMA' => 8,
            'G723_53' => 4,
            'G723_63' => 4,
            'G729' => 18,
            'G722' => 9,
            'G726-16' => 112,
            'G726-24' => 102,
            'G726-32' => 2,
            'G726-40' => 104,
            'iLBC_13_3' => 97,
            'iLBC_15_2' => 97,
            'NONE' => 0
        );
    }

    protected function searchForPriority($id, $array)
    {
        foreach ($array as $key => $val) {
            if ($val['priority'] === $id) {
                return $key;
            }
        }
        return null;
    }

    protected function setAudioCodecToSettings($phoneCodecs, $account)
    {
        foreach ($phoneCodecs as $codecKey => $codec) {
            $key = "Config.Account$account.Audio" . $codec['key'];
            $this->psetting("$key.Enable", $codec['enable']);
            $this->psetting("$key.Name", $codecKey);
            $this->psetting("$key.Priority", $codec['priority']);
            $this->psetting("$key.Payload", $codec['payload']);
        }
    }

    protected function setRintones()
    {
        for ($i = 1; $i <= 10; $i++) {
            $key = str_pad($i, 2, '0', STR_PAD_LEFT);
            $ringtone = "Ring$i.wav";

            if ($i === 10) {
                $ringtone = "Bellcore-dr1.wav";
            }
            $this->psetting("Config.Settings.RINGER.Keyword$key", "Ringer$i");
            $this->psetting("Config.Settings.RINGER.Ringtone$key", $ringtone);
        }
    }

    protected function setVolumeSettings($User)
    {
        if ($User) {
            $this->setVolumeUserSettings($User);
        } else {
            $this->psetting('Config.Settings.AUDIO.RingVol', 8);
            $this->psetting('Config.Settings.HANDSET.SpkVol', 8);
            $this->psetting('Config.Settings.HANDSET.MicVol', 4);
            $this->psetting('Config.Settings.HEADSET.SpkVol', 8);
            $this->psetting('Config.Settings.HEADSET.MicVol', 4);
            $this->psetting('Config.Settings.HANDFREE.SpkVol', 8);
            $this->psetting('Config.Settings.HANDFREE.MicVol', 4);
        }
    }

    protected function setVolumeUserSettings($User)
    {
        $userSettings = $User->getSettings();
        if ($userSettings) {
            $settings = $userSettings->attributes;
            unset($settings['userId']);
            foreach ($settings as $key => $value) {
                if (empty($value)) {
                    return $this->setVolumeSettings(false);
                }
                $divisor = 15 / 10;
                $value = (int) $value * $divisor;
                $settings[$key] = round($value);
            }

            $this->psetting('Config.Settings.AUDIO.RingVol', $settings['ringVolume']);
            $this->psetting('Config.Settings.HANDSET.SpkVol', $settings['receiverVolume']);
            $this->psetting('Config.Settings.HANDSET.MicVol', $settings['receiverMicroVolume']);
            $this->psetting('Config.Settings.HEADSET.SpkVol', $settings['headsetVolume']);
            $this->psetting('Config.Settings.HEADSET.MicVol', $settings['headsetMicroVolume']);
            $this->psetting('Config.Settings.HANDFREE.SpkVol', $settings['speakerVolume']);
            $this->psetting('Config.Settings.HANDFREE.MicVol', $settings['speakerMicroVolume']);
        }
    }
}
