<?php
/**
 * The GraphDS graph exporter.
 */
namespace GraphDS\Persistence;

use GraphDS\Graph\Graph;
use InvalidArgumentException;
use SimpleXMLElement;
use DOMDocument;

/**
 * Class defining the graph exporter methods.
 */
class ExportGraph
{
    /**
     * Reference to the graph.
     *
     * @var Graph
     */
    public $graph;

    /**
     * Constructor for the graph exporter.
     *
     * @param Graph $graph The graph to be exported
     *
     * @throws InvalidArgumentException
     */
    public function __construct(Graph $graph)
    {
        $this->graph = &$graph;
    }

    /**
     * Returns the GraphML output representing the graph.
     *
     * @return string
     */
    public function getGraphML()
    {
        $directionality = $this->graph->directed ? 'directed' : 'undirected';
        $export = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?>'
                                      .'<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">'
                                      .'</graphml>');

        $keyNode = $export->addChild('key');
        $keyNode->addAttribute('id', 'd0');
        $keyNode->addAttribute('for', 'node');
        $keyNode->addAttribute('attr.name', 'value');
        $keyNode->addAttribute('attr.type', 'string');
        $keyNode->addChild('default', '');

        $keyEdge = $export->addChild('key');
        $keyEdge->addAttribute('id', 'd1');
        $keyEdge->addAttribute('for', 'edge');
        $keyEdge->addAttribute('attr.name', 'weight');
        $keyEdge->addAttribute('attr.type', 'double');
        $keyEdge->addChild('default', '');

        $graphElem = $export->addChild('graph');
        $graphElem->addAttribute('id', 'G');
        $graphElem->addAttribute('edgedefault', $directionality);
        $graphElem->addAttribute('parse.nodes', $this->graph->getVertexCount());
        $graphElem->addAttribute('parse.edges', $this->graph->getEdgeCount());
        $graphElem->addAttribute('parse.nodeids', 'free');
        $graphElem->addAttribute('parse.edgeids', 'free');
        $graphElem->addAttribute('parse.order', 'nodesfirst');

        foreach ($this->graph->vertices as $vertexKey => $vertex) {
            $node = $graphElem->addChild('node');
            $node->addAttribute('id', $vertexKey);
            if (null !== ($value = $vertex->getValue())) {
                $data = $node->addChild('data', $value);
                $data->addAttribute('key', 'd0');
            }
        }
        foreach ($this->graph->edges as $edgeSource) {
            foreach ($edgeSource as $edgeTarget) {
                $edge = $graphElem->addChild('edge');
                $edge->addAttribute('source', $edgeTarget->vertices['from']);
                $edge->addAttribute('target', $edgeTarget->vertices['to']);
                if (null !== ($value = $edgeTarget->getValue())) {
                    $data = $edge->addChild('data', $value);
                    $data->addAttribute('key', 'd1');
                }
            }
        }

        $dom = new DOMDocument('1.0');
        $dom->preserveWhiteSpace = false;
        $dom->formatOutput = true;
        $dom->loadXML($export->asXML());

        return $dom->saveXML();
    }

    /**
     * Saves given data to a files.
     *
     * @param mixed $data The data to be saved
     * @param string $file Filename where the data should be saved to
     * @throws \InvalidArgumentException
     */
    public function saveToFile($data, $file)
    {
        $dir = dirname($file);
        if (!is_writable($dir)) {
            throw new InvalidArgumentException('Directory '.$dir.' not writable. Cannot write to '.$file.'.');
        }
        $handle = fopen($file, 'w');
        fwrite($handle, $data);
        fclose($handle);
    }
}
