<?php

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

use \Exception;
use \MGModule\DNSManager2\mgLibs\custom\dns;
use \MGModule\DNSManager2\mgLibs\custom\dns\exceptions;
use \MGModule\DNSManager2\mgLibs\custom\dns\interfaces;
use \MGModule\DNSManager2\mgLibs\custom\dns\utils\Patterns;

class DNScom extends dns\SubmoduleAbstract implements interfaces\SubmoduleTTLInterface, interfaces\SubmoduleRDNSInterface, interfaces\SubmoduleImportInterface {
    public $configFields = array(
        'token' =>array (
                'friendlyName'  =>  'Token',
                'validators' => array(
                    'required' => 'required',
                )
        ),
        'test' => array(
                'friendlyName'  =>  'Sandbox',
                'type' => 'yesno',
        ),
        'default_ip' =>array (
                'friendlyName'  =>  'Default IP',
                'validators' => array(
                    'pattern' => Patterns::IP4_OR_IP6,
                )
        ),
    );
    
    public $availableTypes = array('A', 'AAAA', 'NS', 'MX', 'CNAME', 'TXT', 'SRV');
    

    private function makeQuery($arr, $key="", $recurs=1) {
        if ($recurs >= 10)
            throw Exception('Maximum Recursions');
        $ret = "";
        foreach ($arr as $k => $v) {
            $k = urlencode($k);
            if(substr($k,-2,1) == '_') $k = substr($k,0, strrpos($k, '_')); // poor fix, yet works so far...
            if (is_array($v)) {
                $ret .= $this->makeQuery($v, $k, ++ $recurs);
                continue;
            }
            $k = ($key) ? urlencode($key).'[]' : urlencode($k);
            $v = urlencode($v);
            $ret .= "$k=$v&";
        }
        return $ret;
    }
    
    private function get($function, $params = array()) {
        $token = strtoupper($this->config['token']);
        if($this->config['test'])
            $host = "http://sandbox.dns.com/api/";
        else
            $host = "http://dns.com/api/";
        
        $url = $host.$function.'/?';
        $params['AUTH_TOKEN'] = $token;
        $url .= $this->makeQuery($params);
        $ch = curl_init();
        $chOptions = array (
                CURLOPT_URL => trim ($url),
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => 90
        );
        curl_setopt_array($ch, $chOptions);
        $out = curl_exec($ch);
        
        if (curl_errno($ch) || $out == false) {
            throw new exceptions\DNSSubmoduleException("cURL Error: " . curl_errno($ch) . " - " . curl_error($ch), dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM);
        }
        
        curl_close($ch);
        $response = json_decode($out);
        if(is_null($response)) {
            throw new exceptions\DNSSubmoduleException("Cannot parse response", dns\SubmoduleExceptionCodes::INVALID_RESPONSE);
        }
        
        if(isset($response->meta->error) && $response->meta->error != '') {
            throw new exceptions\DNSSubmoduleException($response->meta->error, dns\SubmoduleExceptionCodes::COMMAND_ERROR);
        }
        
        //$ret->meta->success if nie
        return $response;
    }
    
    public function testConnection() {
        $this->get('getDomains');
    }

    /**
     * Sprawdza czy domena istnieje
     * @return boolean 
     */
    public function zoneExists() {
        try {
            $ret = $this->get('getDomains', array(
                'search_term'   =>  $this->domain
            ));
            return $ret->meta->success;
        } catch (exceptions\DNSSubmoduleException $e) {
            return false;
        }
    }

    
    /** NEED TESTING **/
    public function getRecords($recordType = false) {
        $ret = $this->get('getHostnamesForDomain',array(
            'domain'    =>  $this->domain
        ));
        
        $out = array();
        foreach($ret->data as $host) {
            $ret = $this->get('getRRSetForHostname', array(
                'domain'    =>  $this->domain,
                'host'      =>  $host->name
            ));

            foreach($ret->data as $r) {
                if(in_array($r->type, $recordType!==false ? array(strtoupper($recordType)) : $this->getAvailableRecordTypes())) {
                    $record = new dns\record\Record();
                    $record->line = $r>id;
                    $record->name = $host->name;
                    $record->type = $r->type;
                    $record->ttl = $r->ttl;
                    $record->createRDATAObject();
                    
                    switch($r->type) {
                        case 'MX':
                            $record->rdata->preference = $r->priority;
                            $record->rdata->exchange = $r->answer;
                            break;
                        case 'SRV':
                            $record->rdata->setDataFromArray((array)$r);
                            $record->rdata->target = $r->answer;
                            break;
                        default:
                            $record->rdata->setFirstProperty($r->answer);
                            break;
                    }
                    

                    $out[] = $record;
                }
            }
        }
        
        return $out;
    }

    private function recordToParamsArray(dns\record\Record $record) {
        $params = array(
            'host' => $record->nameToRelative($this->domain),
            'type' => $record->type,
            'ttl' => $record->ttl,
        );
        
        switch($record->type) {
            case 'MX':
                $params['priority'] = $record->rdata->preference;
                $params['rdata'] = $record->rdata->exchange;
                break;
            case 'SRV':
                $params['priority'] = $record->rdata->priority;
                $params['weight'] = $record->rdata->weight;
                $params['port'] = $record->rdata->port;
                $params['rdata'] = $record->rdata->target;
                break;
            default:
                $params['rdata'] = $record->rdata->toString();
                break;
        }
        
        return $params;
    }
    
    public function addRecord(dns\record\Record $record) {
        $this->get('createHostname', array(
            'domain'    =>  $this->domain,
            'host'      =>  $record->nameToRelative($this->domain)
        ));
        
        $params = $this->recordToParamsArray($record);
        try {
            $this->get('createRRData', $params);
            
        } catch (Exception $exc) {
            $this->get('removeHostname', array(
                'domain'    =>  $this->domain,
                'host'      =>  $record->nameToRelative($this->domain),
                'confirm'   =>  '1'
            ));
            
            throw $exc;
        }
    }

    public function editRecord(dns\record\Record $record) {
        $params = $this->recordToParamsArray($record);
        $params['rr_id'] = $record->line;
        $this->get('updateRRData', $params);
    }
    
    public function deleteRecord(dns\record\Record $record) {
        $records = $this->getRecords();
        
        $count = 0;
        foreach($records as $search) {
            if($search['name'] == $record->nameToRelative($this->domain) 
                    || $search['name'] == $record->nameToAbsolute($this->domain)) {
                $count++;
                if($count > 1) {
                    break;
                }
            }
        }
        
        if($count == 1) {
            $ret = $this->get('removeHostname', array(
                'domain'    =>  $this->domain,
                'host'      =>  $record->nameToRelative($this->domain),
                'confirm'   =>  '1'
            ));
        } else {
            $ret = $this->get('removeRR', array(
                'rr_id'     =>  $record->line,
                'confirm'   => '1'
            ));
        }
    }
    
    public function activateZone() {
        $this->get('createDomain', array(
            'domain'    =>  $this->domain,
            'mode'      =>  'advanced',
        ));
    }
    
    public function terminateZone() {
        $this->get('deleteDomain', array(
            'domain'    =>  $this->domain,
            'confirm'   =>  '1'
        ));
    }

    public function getZones() {
        $ret = $this->get('deleteDomain');
        $out = array();
        foreach($ret->data as $zone) {
            $out[$zone->name] = '';
        }
    }
    
}
