<?php

namespace Drupal\backup_migrate\Core\Destination;

use Drupal\backup_migrate\Core\Plugin\FileProcessorTrait;
use Drupal\backup_migrate\Core\File\BackupFileInterface;

/**
 * Store extended meta data in a sidecar file.
 *
 * This trait allows destinations to store extended file metadata as a sidecar
 * file to the same destination. Sidecar files are .ini formatted. This trait
 * will work with any destination derived from DestinationBase.
 *
 * @package Drupal\backup_migrate\Core\Destination
 */
trait SidecarMetadataDestinationTrait {
  use FileProcessorTrait;

  /**
   * {@inheritdoc}
   */
  protected function loadFileMetadataArray(BackupFileInterface $file) {
    $info = [];

    $id = $file->getFullName();
    $filename = $id . '.info';
    if ($this->fileExists($filename)) {
      $meta_file = $this->getFile($filename);
      $meta_file = $this->loadFileForReading($meta_file);
      $info = $this->iniToArray($meta_file->readAll());
    }
    return $info;
  }

  /**
   * {@inheritdoc}
   */
  protected function saveTheFileMetadata(BackupFileInterface $file) {
    // Get the file metadata and convert to INI format.
    $meta = $file->getMetaAll();
    $ini = $this->arrayToIni($meta);

    // Create an info file.
    $meta_file = $this->getTempFileManager()->pushExt($file, 'info');
    $meta_file->write($ini);

    // Save the metadata.
    $this->saveTheFile($meta_file);
  }

  /**
   * {@inheritdoc}
   */
  public function deleteFile($id) {
    $this->deleteTheFile($id);
    $this->deleteTheFile($id . '.info');
  }

  /**
   * Parse an INI file's contents.
   *
   * For simplification this function only parses the simple subset of INI
   * syntax generated by SidecarMetadataDestinationTrait::arrayToIni();
   *
   * @param $ini
   *
   * @return array
   */
  protected function iniToArray($ini) {
    $out = [];
    $lines = explode("\n", $ini);
    foreach ($lines as $line) {
      $line = trim($line);
      // Skip comments (even though there probably won't be any.
      if (substr($line, 0, 1) == ';') {
        continue;
      }

      // Match the key and value using a simplified syntax.
      $matches = [];
      if (preg_match('/^([^=]+)\s?=\s?"(.*)"$/', $line, $matches)) {
        $key = $matches[1];
        $val = $matches[2];

        // Break up a key in the form a[b][c].
        $keys = explode('[', $key);
        $insert = &$out;
        foreach ($keys as $part) {
          $part = trim($part, ' ]');
          $insert[$part] = '';
          $insert = &$insert[$part];
        }
        $insert = $val;
      }
    }

    return $out;
  }

  /**
   * @param $data
   * @param string $prefix
   *
   * @return string
   */
  protected function arrayToIni($data, $prefix = '') {
    $content = "";
    foreach ($data as $key => $val) {
      if ($prefix) {
        $key = $prefix . '[' . $key . ']';
      }
      if (is_array($val)) {
        $content .= $this->arrayToIni($val, $key);
      }
      else {
        $content .= $key . " = \"" . $val . "\"\n";
      }
    }
    return $content;
  }

}
