<?php

namespace MGModule\DNSManager2\mgLibs\custom\dns\submodules;

use \MGModule\DNSManager2\mgLibs\custom\dns;
use \MGModule\DNSManager2\mgLibs\custom\dns\exceptions;
use \MGModule\DNSManager2\mgLibs\custom\dns\interfaces;
use \SoapClient;

class PowerDNSHosted extends dns\SubmoduleAbstract implements
        interfaces\SubmoduleTTLInterface, 
        interfaces\SubmoduleImportInterface
{

    public $configFields = array(
        'apikey' => array(
            'friendlyName' => 'API Key',
            'validators' => array(
                'required' => 'required',
            )
        ),
    );
    public $availableTypes = array('A', 'AAAA', 'NS', 'MX', 'CNAME', 'TXT');
    private $zoneID = false;

    private function get($function, $params = array())
    {
        $url = "https://www.powerdns.net/services/express.asmx?WSDL&apikey=" . $this->config['apikey'];
        $soap = new SoapClient($url, array("trace" => 1, "exceptions" => 0, "features" => SOAP_USE_XSI_ARRAY_TYPE + SOAP_SINGLE_ELEMENT_ARRAYS));
        $soap->__setLocation($url);
        $result = $soap->$function($params);
        if(is_a($result, 'SoapFault')) {
            throw new exceptions\DNSSubmoduleException('SOAP Error: ' . $result->getMessage(), dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM);
        }

        if($result == false) {
            throw new exceptions\DNSSubmoduleException('Unable to parse response', dns\SubmoduleExceptionCodes::INVALID_RESPONSE);
        }

        $n = $function . 'Result';
        if($result->$n->code != 100) {
            throw new exceptions\DNSSubmoduleException($result->$n->description? : 'Unknown error', dns\SubmoduleExceptionCodes::COMMAND_ERROR);
        }

        return $result;
    }

    public function testConnection()
    {
        $this->get('listZones');
    }

    public function getZoneID()
    {
        if($this->zoneID !== false) {
            return $this->zoneID;
        }

        $out = $this->get('listZones', array());
        foreach($out->listZonesResult->Zones->Zone as $zone) {
            if((string)$zone->Name == $this->domain) {
                $this->zoneID = (int)$zone->Id;
                return (int)$zone->Id;
                break;
            }
        }
        throw new exceptions\DNSSubmoduleException('Zone does not exist', dns\SubmoduleExceptionCodes::COMMAND_ERROR);
    }

    public function zoneExists()
    {
        try {
            $this->getZoneID();
            return true;
        } catch(exceptions\DNSSubmoduleException $e) {
            if($e->getCode() == dns\SubmoduleExceptionCodes::COMMAND_ERROR) {
                return false;
            }
            throw $e;
        }
    }

    public function getRecords($recordType = false)
    {
        $ret = $this->get('listRecords', array('zoneId' => $this->getZoneID()));
        $out = array();
        foreach($ret->listRecordsResult->Records->Record as $r) {
            $type = strtoupper((string)$r->Type);
            if(in_array($type, $recordType !== false ? array(strtoupper($recordType)) : $this->getAvailableRecordTypes())) {
                $new_record = new dns\record\Record();
                $new_record->line = (string)$r->Id;
                $new_record->name = (string)$r->Name;
                $new_record->type = $type;
                $new_record->ttl = (string)$r->TimeToLive;
                $new_record->createRDATAObject();

                switch($type) {
                    case 'MX':
                        $new_record->rdata->preference = (string)$r->Priority;
                        $new_record->rdata->exchange = (string)$r->Content;
                        break;
                    default:
                        $new_record->rdata->fromString((string)$r->Content);
                        break;
                }

                $out[] = $new_record;
            }
        }

        return $out;
    }

    private function recordToParamsArray(dns\record\Record $record)
    {
        $params = array(
            'zoneId' => $this->getZoneID(),
            'Name' => $record->nameToAbsolute($this->domain, false),
            'Type' => $record->type,
            'Content' => $record->rdata->toString(),
            'TimeToLive' => (int)$record->ttl,
            'Priority' => (int)$record['priority']
        );

        switch($record->type) {
            case 'MX':
                $value = $record->rdata->exchange;
                $prio = $record->rdata->preference;
                break;
            default:
                $value = $record->rdata->toString();
                $prio = 0;
                break;
        }

        return $params;
    }

    public function addRecord(dns\record\Record $record)
    {
        $this->get('addRecordToZone', $this->recordToParamsArray($record));
    }

    public function editRecord(dns\record\Record $record)
    {
        $params = $this->recordToParamsArray($record);
        $params['recordID'] = (int)$record->line;
        $this->get('UpdateRecord', $params);
    }

    public function deleteRecord(dns\record\Record $record)
    {
        $params = array(
            'recordId' => (int)$record->line,
        );
        $this->get('deleteRecordById', $params);
    }

    public function activateZone()
    {
        $this->get('addNativeDomain', array('domainName' => $this->domain));
    }

    public function terminateZone()
    {
        $this->get('deleteZoneByName', array('zoneName' => $this->domain));
    }

    public function getZones()
    {
        $ret = $this->get('listZones', array());
        $out = array();
        foreach($ret->listZonesResult->Zones->Zone as $zone) {
            $out[(string)$zone->Name] = (string)$zone->Master;
        }
        return $out;
    }

}
