<?php

require_once dirname(__FILE__) . '/../../lib/phoneprovisioning.php';
require_once 'Gigaset.php';

class GigasetPhoneProvisioning extends VirtualPhoneProvisioningController
{

    private $sipAccount = null;

    protected function getProvURL($mac = null)
    {
        if (!$mac) {
            $mac = $this->mMacAddress;
        }

        $url = parent::getProvURL();
        $url = $url . "$mac.xml";
        return $url;
    }

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

    protected function checkMacAddress($mac)
    {
        return parent::checkMacAddress($mac);
    }

    protected function checkUserAgent($ua)
    {
        //$ua = "N720-DM-PRO/70.108.00.000.000;7C2F803D8383";
        //N510 IP PRO/42.231.00.000.000;7C2F80D29241

        $knownPhoneModel = Gigaset::getSupportedPhoneModels();
        Log::d($ua . " contacs us", "gigaset-dls");

        $firstExplode = explode('/', $ua);
        $phoneModelName = str_replace(' ', '-', $firstExplode[0]);

        if (!array_key_exists($phoneModelName, $knownPhoneModel)) {
            return false;
        }

        $secondExplode = explode(';', $firstExplode[1]);
        $firmwareString = $secondExplode[0];

        $vParts = explode(".", $firmwareString);

        $vPartsNormal = array();
        $vPartsNormal[] = str_pad($vParts[0], 2, '0', STR_PAD_LEFT);
        $vPartsNormal[] = str_pad($vParts[1], 3, '0', STR_PAD_LEFT);
        $vPartsNormal[] = str_pad($vParts[2], 2, '0', STR_PAD_LEFT);
        $vPartsNormal[] = str_pad($vParts[3], 3, '0', STR_PAD_LEFT);
        $vPartsNormal[] = str_pad($vParts[4], 3, '0', STR_PAD_LEFT);
        $firmwareVersion = implode(".", $vPartsNormal);

        $this->setCurrentPhoneModel($knownPhoneModel[$phoneModelName]);
        $this->setCurrentFirmwareVersion($firmwareVersion);
        return true;
    }

    protected function loadConfigValues($mac = NULL)
    {
        $this->getTemplate(); //loads the template into the current Settings Array

        $Phone = $this->getPhoneModel();
        $phoneType = $Phone->type;
        $SipAccount = $Phone->getSipAccount();
        $this->sipAccount = $SipAccount;

        if (startsWith($phoneType, 'N510')) {
            $this->loadConfigValuesForN510($SipAccount);
        } else if (startsWith($phoneType, 'N720')) {
            $this->loadConfigValuesForN720($SipAccount);
        } else {
            return false;
        }

        $this->hexSetting('BS_AE_SwConfig.ucCountryCodeTone', $this->getToneScheme());
        $this->setting('BS_VOIP_Data.ul_SIP_AllowMethodMask', '0x373e');
        $this->setting('BS_CUSTOM_ORG.bit.bEct', '1'); //Enable call transfer by On-Hook;

        $provisioningProfile = ProvisioningProfile::model()->findByPk($Phone->provisioning_profile);

        if ($provisioningProfile) {
            $params = $provisioningProfile->getParamsForPhone($phoneType);
            $paramKeys = array_keys($this->mPhonalisaProvisioningVariables);
            foreach ($params as $param) {
                $param->value = str_replace($paramKeys, $this->mPhonalisaProvisioningVariables, $param->value);

                $this->setting($param->name, $param->value);
                Log::v("Overwriting key from parameter profile: " . $param->name . " = " . $param->value, "gigaset-dls");
            }
        }
        return true;
    }

    private function loadConfigValuesForN510($SipAccount)
    {
        //NTP-Settings
        $this->setting('BS_IP_Data1.ucB_TIME_USE_AUTOMATIC_NTP_SYN', '1');
        $this->stringSetting('BS_IP_Data1.aucS_TIME_NTP_SERVER[0]', cfg('NTP_HOST'));
        $this->stringSetting('BS_IP_Data1.aucS_TIME_NTP_SERVER[40]', cfg('NTP_HOST'));
        $this->setting('BS_IP_Data1.ucB_TIME_USE_AUTOMATIC_DST', '1');
        $this->hexSetting('BS_IP_Data1.uiI_TIME_TIMEZONE', $this->getTimeZone());

        // XML-Phonebook
        $this->stringSetting('BS_XML_Netdirs.astNetdirProvider[1].aucServerURL', $this->getPhonebookURL());
        $this->stringSetting('BS_XML_Netdirs.astNetdirProvider[1].aucWhitePagesDirName', __('ADDRESSESS_FORM_HEADER_BUSINESS'));
//        $this->stringSetting('BS_XML_Netdirs.astNetdirProvider[1].aucPrivatePagesDirName', __('ADDRESSESS_FORM_HEADER_PRIVATE'));

        $this->setting('BS_IP_Data1.ucB_USE_R_KEY_FOR_CALL_TRANSFER', '1');
        $this->hexSetting('BS_IP_Data1.ucI_DIALING_PLAN_COUNTRY_ID', $this->getDialingPlanCountryID());
        $this->setting('BS_IP_Data1.ucI_HTTPLANGUAGE', $this->getLanguage());

        $registrationExpireTime = $SipAccount->expires;
        $this->configureSipAccountN510($SipAccount, 1, $registrationExpireTime);

        // enumerating devices in database
        for ($i = 2; $i < 6; $i++) {
            $phone = Phones::model()->findByAttributes(array("mac_addr" => $this->mMacAddress . "-" . $i));
            if (!$phone) {
                $this->setting('BS_IP_Data1.ucB_SIP_ACCOUNT_IS_ACTIVE_' . $i, '0');
                $this->setting('BS_IP_Data1.ucB_VOIP_NET_AM_ENABLED_' . $i, '0');
                $this->hexSetting('BS_IP_Data1.uiI_RE_REGISTRATION_TIMER_' . $i, $registrationExpireTime);
            } else {
                $SipAccount = $phone->getSipAccount();
                $this->configureSipAccountN510($SipAccount, $i, $registrationExpireTime);
            }
        }
    }

    private function configureSipAccountN510($SipAccount, $index, $registrationExpireTime)
    {
        $this->setting('BS_IP_Data1.ucB_SIP_ACCOUNT_IS_ACTIVE_' . $index, '1');
        $this->setting('BS_IP_Data1.ucB_VOIP_NET_AM_ENABLED_' . $index, '1');
        $this->stringSetting('BS_IP_Data1.aucS_SIP_ACCOUNT_NAME_' . $index, $SipAccount->authname);
        $this->stringSetting('BS_IP_Data1.aucS_VOIP_NET_AM_NUMBER_' . $index, cfg('DP_VOICEMAIL_LISTENING_PREFIX'));

        //Audio-Codec-Settings
        $this->setting('BS_IP_Data1.ucI_SIP_PREFERRED_VOCODER_' . $index, $this->getPhoneCodecs());
        $this->setting('BS_IP_Data1.ucI_SIP_AVAILABLE_VOCODER_' . $index, $this->getPhoneCodecs());

        $keyIndex = '_' . $index;
        if ((int) $index == 1) {
            $keyIndex = '';
            $index = (int) $index - 1;
        }
        $this->stringSetting('BS_Accounts.astAccounts[' . $index . '].aucAccountName[0]', $SipAccount->authname);
        $this->stringSetting('BS_AE_Subscriber.stMtDat[' . $index . '].aucTlnName[0]', $SipAccount->authname);
        $this->stringSetting('BS_IP_Data1.aucS_SIP_DISPLAYNAME' . $keyIndex, $SipAccount->authname);
        $this->stringSetting('BS_IP_Data3.aucS_SIP_LOGIN_ID' . $keyIndex, $SipAccount->authname);
        $this->stringSetting('BS_IP_Data1.aucS_SIP_PASSWORD' . $keyIndex, $SipAccount->secret);
        $this->stringSetting('BS_IP_Data1.aucS_SIP_USER_ID' . $keyIndex, $SipAccount->authname);
        $this->hexSetting('BS_IP_Data1.uiI_RE_REGISTRATION_TIMER' . $keyIndex, $registrationExpireTime);

        //Provisioning-Settings
        $this->stringSetting('BS_IP_Data1.aucS_SIP_PROVIDER_NAME' . $keyIndex, cfg('PHONALISA_NAME'));
        $this->stringSetting('BS_IP_Data1.aucS_SIP_DOMAIN' . $keyIndex, $SipAccount->getSIPRegistrar());
        $this->stringSetting('BS_IP_Data1.aucS_SIP_SERVER' . $keyIndex, $SipAccount->getSIPRegistrar());
        $this->hexSetting('BS_IP_Data1.uiI_SIP_SERVER_PORT' . $keyIndex, cfg('INTERNAL_SIP_PORT'));
        $this->hexSetting('BS_IP_Data1.uiI_SIP_REGISTRAR_PORT' . $keyIndex, cfg('INTERNAL_SIP_PORT'));
        $this->stringSetting('BS_IP_Data1.aucS_SIP_REGISTRAR' . $keyIndex, $SipAccount->getSIPRegistrar());

        $this->setting('BS_IP_Data1.ucI_SIP_PREFERRED_TRANSPORT_LAYER' . $keyIndex, '6');
    }

    private function loadConfigValuesForN720($SipAccount)
    {
        //Provisioning-Settings
        $this->stringSetting('BS_VOIP_Data.astVoipProviders[0].aucSIP_PROVIDER_NAME', cfg('PHONALISA_NAME'));
        $this->stringSetting('BS_VOIP_Data.astVoipProviders[0].aucSIP_DOMAIN', $SipAccount->getSIPRegistrar());
        $this->stringSetting('BS_VOIP_Data.astVoipProviders[0].aucSIP_SERVER', $SipAccount->getSIPRegistrar());
        $this->hexSetting('BS_VOIP_Data.astVoipProviders[0].uiSIP_SERVER_PORT', cfg('INTERNAL_SIP_PORT'));
        $this->hexSetting('BS_VOIP_Data.astVoipProviders[0].uiSIP_REGISTRAR_PORT', cfg('INTERNAL_SIP_PORT'));
        $this->stringSetting('BS_VOIP_Data.astVoipProviders[0].aucSIP_REGISTRAR', $SipAccount->getSIPRegistrar());

        $this->setting('BS_XML_Netdirs.aucNetdirSelForDirectAccess[%]', '3');
        $this->setting('BS_IP_Data.ucB_ADD_SIPID_TO_HTTP_REQ', '1');

        $this->setting('BS_IP_Data.ucB_USE_R_KEY_FOR_CALL_TRANSFER', '1');

        $this->configureSipAccountN720($SipAccount, 0);
        $this->hexSetting('BS_VOIP_Data.astVoipProviders[0].uiRE_REGISTRATION_TIMER', $SipAccount->expires);
        $this->hexSetting('BS_VOIP_Data.astVoipProviders[%].uiRE_REGISTRATION_TIMER', $SipAccount->expires);

        // enumerating devices in database
        for ($i = 1; $i < 100; $i++) {
            $phone = Phones::model()->findByAttributes(array("mac_addr" => $this->mMacAddress . "-" . $i));
            if (!$phone) {
                $this->setting('BS_VOIP_Data.astVoipAccounts[' . $i . '].ucB_SIP_ACCOUNT_IS_ACTIVE_1', '0');
                $this->setting('BS_VOIP_Data.astVoipAccounts[' . $i . '].ucB_VOIP_NET_AM_ENABLED_1', '0');
            } else {
                $SipAccount = $phone->getSipAccount();
                $this->configureSipAccountN720($SipAccount, $i);
            }
        }

        $this->setting('BS_IP_Data.ucI_HTTPLANGUAGE', $this->getLanguage());
        $this->setting('BS_VOIP_Data.ucB_USE_R_KEY_FOR_CALL_TRANSFER', $this->getLanguage());

        // XML-Phonebook
        $this->stringSetting('BS_XML_Netdirs.astNetdirProvider[1].aucServerURL[%]', $this->getPhonebookURL());
        $this->stringSetting('BS_XML_Netdirs.astNetdirProvider[1].aucWhitePagesDirName[%]', __('ADDRESSESS_FORM_HEADER_BUSINESS'));
        $this->stringSetting('BS_XML_Netdirs.astNetdirProvider[1].aucPrivatePagesDirName[%]', __('ADDRESSESS_FORM_HEADER_PRIVATE'));

        //NTP-Settings
        $this->setting('BS_IP_Data.ucB_TIME_USE_AUTOMATIC_NTP_SYN', '1');
        $this->stringSetting('BS_IP_Data.aucS_TIME_NTP_SERVER', cfg('NTP_HOST'));
        $this->setting('BS_IP_Data.ucB_TIME_USE_AUTOMATIC_DST', '1');
        $this->hexSetting('BS_IP_Data.uiI_TIME_TIMEZONE', $this->getTimeZone());

        //Audio-Codec-Settings
        $this->setting('BS_VOIP_Data.astVoipAccounts[%].aucI_SIP_PREFERRED_VOCODER[%]', $this->getPhoneCodecs());
        $this->setting('BS_VOIP_Data.ucI_SIP_AVAILABLE_VOCODER[%]', $this->getPhoneCodecs());
        $this->setting('BS_VOIP_Data.ucI_SIP_PREDEFINED_VOCODER[%]', $this->getPhoneCodecs());

        $this->setting('BS_VOIP_Data.astVoipProviders[%].ucSIP_PREFERRED_TRANSPORT_LAYER', '6');

        $this->stringSetting('BS_VOIP_Data.astVoipAccounts[%].aucS_VOIP_NET_AM_NUMBER_1', cfg('DP_VOICEMAIL_LISTENING_PREFIX'));
        $this->hexSetting('BS_IP_Data.ucI_DIALING_PLAN_COUNTRY_ID', $this->getDialingPlanCountryID());
    }

    private function configureSipAccountN720($SipAccount, $index)
    {
        $this->stringSetting('BS_VOIP_Data.astVoipAccounts[' . $index . '].aucS_SIP_DISPLAYNAME', $SipAccount->authname);
        $this->stringSetting('BS_VOIP_Data.astVoipAccounts[' . $index . '].aucS_SIP_LOGIN_ID', $SipAccount->authname);
        $this->stringSetting('BS_VOIP_Data.astVoipAccounts[' . $index . '].aucS_SIP_PASSWORD', $SipAccount->secret);
        $this->stringSetting('BS_VOIP_Data.astVoipAccounts[' . $index . '].aucS_SIP_USER_ID', $SipAccount->authname);
        $this->stringSetting('BS_VOIP_Data.astVoipAccounts[' . $index . '].aucS_SIP_ACCOUNT_NAME_1', $SipAccount->authname);
        $this->setting('BS_VOIP_Data.astVoipAccounts[' . $index . '].ucB_SIP_ACCOUNT_IS_ACTIVE_1', '1');
        $this->setting('BS_VOIP_Data.astVoipAccounts[' . $index . '].ucB_VOIP_NET_AM_ENABLED_1', '1');
    }

    private function getTemplate()
    {
        // READ general gigaset template
        $filename = dirname(__FILE__) . "/templates/gigaset.xml";
        if (file_exists($filename)) {
            $this->readTemplateFile($filename);
        }

        // Read phone specific template if this exists
        $filename = dirname(__FILE__) . "/templates/" . $this->mCurrentPhoneModel->getPhoneModelName() . ".xml";
        if (file_exists($filename)) {
            $this->readTemplateFile($filename);
        }
        return $this->mCurrentSettings;
    }

    private function readTemplateFile($filename)
    {
        $data = file_get_contents($filename);
        $xml = new SimpleXMLElement($data);
        foreach ($xml as $section => $element) {
            foreach ($element as $key => $value) {
                if (strtoupper($key) != 'SYMB_ITEM') {
                    continue;
                }
                $this->setting((string) $value["ID"], (string) $value["value"], (string) $value["class"]);
            }
        }
    }

    protected function stringSetting($key, $value = '')
    {
        $this->setting($key, '"' . $value . '"');
    }

    protected function hexSetting($key, $value = '')
    {
        $this->setting($key, $this->gigasetHexFormat($value));
    }

    protected function gigasetHexFormat($value)
    {
        return '0x' . dechex($value);
    }

    protected function setting($key, $value = '', $class = 'symb_item')
    {
        $setting = array();
        $setting[$key]["class"] = $class;
        $setting[$key]["value"] = $value;
        $this->mCurrentSettings[$key] = $setting[$key];
    }

    protected function generateOutput()
    {
        $xw = new xmlWriter();
        $xw->openMemory();
        $xw->setIndent(TRUE);
        $xw->startDocument('1.0', 'UTF-8'); // start document

        $xw->startElement("ProviderFrame");
        $xw->startElement("Provider");

        $xw->startElement('MAC_ADDRESS');
        $xw->writeAttribute('value', @$this->getPhoneModel()->mac_addr);
        $xw->endElement();

        $xw->startElement('VERSION');
        $xw->writeAttribute('value', PhonalisaDate::convertServerDateTo('dmyHi', PhonalisaDate::newServerDate()));
        $xw->endElement();

        $xw->startElement('PROFILE_NAME');
        $xw->writeAttribute('class', 'string');
        $xw->writeAttribute('value', @$this->getPhoneModel()->type);
        $xw->endElement();

        foreach ($this->mCurrentSettings as $section_name => $section_values) {
            $xw->startElement('SYMB_ITEM');
            $xw->writeAttribute('ID', $section_name);
            $xw->writeAttribute('class', $section_values['class']);
            $xw->writeAttribute('value', $section_values['value']);
            $xw->endElement();
        }

        $xw->endElement();
        $xw->endElement();
        $xw->endDocument(); // end document

        $xml = $xw->outputMemory();
        echo str_replace(array('"&quot;', '&quot;"'), array("'\"", "\"'"), $xml);
        return true;
    }

    protected function generateNewPhone()
    {
        $phone = $this->generatePhoneForGigasetWithMac($this->mMacAddress);

        $sysConfigPhonesMax = cfg('GIGASET_DECT_DEVICE_MAX');
        if (!isset($sysConfigPhonesMax)) {
            $sysConfigPhonesMax = 10;
        }

        $maxPhones = (int) $sysConfigPhonesMax;
        if (startsWith($phone->type, 'N510') && $maxPhones > 6) {
            $maxPhones = 6;
        }
        for ($i = 1; $i < $maxPhones; $i++) {
            $this->generatePhoneForGigasetWithMac($this->mMacAddress . "-" . $i);
        }
        return $phone;
    }

    private function generatePhoneForGigasetWithMac($mac)
    {
        $model = new Phones();
        $model->mac_addr = $mac;
        $model->type = $this->mCurrentPhoneModel->getPhoneModelName();
        $model->nobody_index = 1;
        $model->added = 1;
        $model->firmware = $this->mCurrentFirmwareVersion;
        $model->fw_manual_update = 0;
        $model->ipv4_address = $this->mIPv4Address;
        $model->setIsNewRecord(true);

        //generate dummy sip account for this phone
        $sipaccount = new SipAccounts();
        $sipaccount->authname = $this->getNextFreeDummyNumber();
        $sipaccount->secret = PasswordGenerator::generateSipPassword();
        $sipaccount->setIsNewRecord(true);

        $sipaccount->save(false);

        $model->sip_account_id = $sipaccount->id;
        $model->nobody_index = $sipaccount->id;

        if (!$model->save(false)) {
            foreach ($model->errors as $errors) {
                foreach ($errors as $key => $value) {
                    Log::d("$key => $value", "gigaset-dls");
                }
            }
        }
        return $model;
    }

    protected function getLanguage()
    {
        $guiLanguage = cfg('GUI_LANGS');
        $languageCodes = array(
            'en_us' => '0x01',
            'de_de' => '0x02',
            'fr_fr' => '0x09',
            'nl_nl' => '0x0a'
        );

        $language = '0x01';
        if (isset($languageCodes[$guiLanguage])) {
            $language = $languageCodes[$guiLanguage];
        }
        return $language;
    }

    protected function getTimeZone()
    {
        $timeZone = new DateTimeZone(cfg('TIMEZONE'));
        $dateTime = new DateTime("now", $timeZone);
        $gmtDifference = $dateTime->format('P');
        $timezoneCodes = $this->getGigasetTimeZoneCode();

        $timezone = '26';
        if (isset($timezoneCodes[$gmtDifference])) {
            $timezone = $timezoneCodes[$gmtDifference];
        }
        return $timezone;
    }

    protected function getGigasetTimeZoneCode()
    {
        return array(
            '-12:00' => '0', //(GMT-12:00) International Date Line West
            '-11:00' => '1', //(GMT-11:00) Midway Island, Samoa
            '-10:00' => '2', //(GMT-10:00) Hawaii
            '-09:00' => '3', //(GMT-09:00) Alaska
            '-08:00' => '4', //(GMT-08:00) Pacific Time (US & Canada), Tijuana
            '-07:00' => '7', //(GMT-07:00) Mountain Time (US & Canada)
            '-06:00' => '9', //(GMT-06:00) Central Time (US & Canada)
            '-05:00' => '13', //(GMT-05:00) Eastern Time (US & Canada)
            '-04:00' => '15', //(GMT-04:00) Atlantic Time (Canada)
            '-03:30' => '18', //(GMT-03:30) Newfoundland
            '-03:00' => '21', //(GMT-03:00) Greenland
            '-02:00' => '22', //(GMT-02:00) Mid-Atlantic
            '-01:00' => '23', //(GMT-01:00) Azores
            '+00:00' => '26', //(GMT±00:00) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London
            '+01:00' => '27', //(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
            '+02:00' => '36', //(GMT+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius
            '+03:00' => '38', //(GMT+03:00) Baghdad
            '+03:30' => '42', //(GMT+03:30) Tehran
            '+04:00' => '43', //(GMT+04:00) Abu Dhabi, Muscat
            '+04:30' => '45', //(GMT+04:30) Kabul
            '+05:00' => '47', //(GMT+05:00) Islamabad, Karachi, Tashkent
            '+05:30' => '48', //(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi
            '+05:45' => '49', //(GMT+05:45) Kathmandu
            '+06:00' => '50', //(GMT+06:00) Almaty, Novosibirsk
            '+06:30' => '53', //(GMT+06:30) Rangoon
            '+07:00' => '54', //(GMT+07:00) Bangkok, Hanoi, Jakarta
            '+08:00' => '56', //(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi
            '+09:00' => '62', //(GMT+09:00) Seoul
            '+09:30' => '65', //(GMT+09:30) Darwin
            '+10:00' => '67', //(GMT+10:00) Canberra, Melbourne, Sydney
            '+11:00' => '71', //(GMT+11:00) Magadan, Solomon Is., New Caledonia
            '+12:00' => '72', //(GMT+12:00) Auckland, Wellington
            '+13:00' => '74' //(GMT+13:00) Nuku'alofa
        );
    }

    protected function getToneScheme()
    {
        $guiLanguage = cfg('GUI_LANGS');
        $toneSchemeCodes = array(
            'en_us' => '1',
            'de_de' => '12',
            'fr_fr' => '7',
            'nl_nl' => '9'
        );

        $toneScheme = '12';
        if (isset($toneSchemeCodes[$guiLanguage])) {
            $toneScheme = $toneSchemeCodes[$guiLanguage];
        }
        return $toneScheme;
    }

    protected function getPhoneCodecs()
    {
        $phonalisaCodecs = str_replace(' ', '', cfg('GLOBAL_CODEC_PREFS'));
        $codecs = explode(',', $phonalisaCodecs);
        $gigasetCodecs = array(
            'PCMU' => '0x00',
            'PCMA' => '0x01',
            'G726' => '0x02',
            'G729' => '0x03',
            'G726' => '0x04',
            'G722' => '0x05'
        );

        $phoneCodec = '';
        foreach ($codecs as $codec) {
            if (isset($gigasetCodecs[$codec])) {
                $phoneCodec .= $gigasetCodecs[$codec] . ',';
            }
        }
        return trim($phoneCodec, ',');
    }

    protected function getDialingPlanCountryID()
    {
        $guiLanguage = cfg('GUI_LANGS');
        $countryCodes = array(
            'en_us' => '75',
            'de_de' => '25',
            'fr_fr' => '24',
            'nl_nl' => '48'
        );

        $dialingCountryID = '25';
        if (isset($countryCodes[$guiLanguage])) {
            $dialingCountryID = $countryCodes[$guiLanguage];
        }
        return $dialingCountryID;
    }
}
