<?php
namespace nur\io\csv;

use nur\A;

class ut {
  static function split_header_mappings(array $header_mappings): array {
    $includes = [];
    $excludes = [];
    $index = 0;
    foreach ($header_mappings as $dest => $source) {
      if ($dest === $index) {
        $index++;
        $includes[$source] = true;
      } elseif ($source === null) {
        $excludes[$dest] = true;
      }
    }
    return [$includes, $excludes];
  }

  static function reader_map_keys(array $row, ?array $header_mappings): array {
    if ($header_mappings === null) return $row;
    # tout d'abord, recenser includes et excludes
    [$includes, $excludes] = self::split_header_mappings($header_mappings);
    $mapped = [];
    if ($includes) {
      # on spécifie des champs à inclure: n'inclure que ceux-là et dans
      # l'ordre du mapping, en les mappant le cas échéant
      $index = 0;
      foreach ($header_mappings as $dest => $source) {
        if ($dest === $index) {
          $index++;
          # inclure
          $dest = $source;
          $mapped[$dest] = A::get($row, $dest);
        } elseif ($source !== null) {
          # mapper
          $mapped[$dest] = A::get($row, $source);
        }
      }
      $row = $mapped;
    } else {
      # on ne spécifie pas les champs à inclure: inclure tous les champs en
      # les mappant le cas échéant, et dans l'ordre original
      foreach ($row as $key => $value) {
        # exclure les champs de $excludes
        if (array_key_exists($key, $excludes)) continue;
        # mapper le cas échéant
        $index = 0;
        $found = false;
        foreach ($header_mappings as $dest => $source) {
          if ($dest === $index) {
            $index++;
          } elseif ($source === $key) {
            $mapped[$dest] = $value;
            $found = true;
            break;
          }
        }
        if (!$found) $mapped[$key] = $value;
      }
      $row = $mapped;
    }
    return $row;
  }

  static function writer_map_headers(array $headers, ?array $header_mappings): array {
    if ($header_mappings === null) return $headers;
    # tout d'abord, recenser includes et excludes
    [$includes, $excludes] = self::split_header_mappings($header_mappings);
    $mapped = [];
    if ($includes) {
      # on spécifie des champs à inclure: n'inclure que ceux-là et dans
      # l'ordre du mapping, en les mappant le cas échéant
      $index = 0;
      foreach ($header_mappings as $source => $dest) {
        if ($source === $index) {
          $index++;
          # inclure
          $mapped[] = $dest;
        } elseif ($dest === null) {
          # exclure
          $mapped[] = $source;
        } else {
          # mapper
          $mapped[] = $dest;
        }
      }
      $headers = $mapped;
    } else {
      # on ne spécifie pas les champs à inclure: inclure tous les champs en
      # les mappant le cas échéant, et dans l'ordre original
      foreach ($headers as $header) {
        $found = false;
        foreach ($header_mappings as $source => $dest) {
          if ($source === $header) {
            if ($dest === null) $mapped[] = $source;
            else $mapped[] = $dest;
            $found = true;
            break;
          }
        }
        if (!$found) $mapped[] = $header;
      }
      $headers = $mapped;
    }
    return $headers;
  }

  static function writer_map_keys(array $row, ?array $header_mappings): array {
    if ($header_mappings === null) return $row;
    # tout d'abord, recenser includes et excludes
    [$includes, $excludes] = self::split_header_mappings($header_mappings);
    $mapped = [];
    if ($includes) {
      # on spécifie des champs à inclure: n'inclure que ceux-là et dans
      # l'ordre du mapping, en les mappant le cas échéant
      $index = 0;
      foreach ($header_mappings as $source => $dest) {
        if ($source === $index) {
          $index++;
          # inclure
          $mapped[$dest] = A::get($row, $dest);
        } elseif ($dest === null) {
          # "exclure"
          $mapped[$source] = null;
        } else {
          # mapper
          $mapped[$dest] = A::get($row, $source);
        }
      }
      $row = $mapped;
    } else {
      # on ne spécifie pas les champs à inclure: inclure tous les champs en
      # les mappant le cas échéant, et dans l'ordre original
      foreach ($row as $header => $value) {
        # mapper le cas échéant
        $found = false;
        foreach ($header_mappings as $source => $dest) {
          if ($source === $header) {
            if ($dest === null) $mapped[$source] = null;
            else $mapped[$dest] = $value;
            $found = true;
            break;
          }
        }
        if (!$found) $mapped[$header] = $value;
      }
      $row = $mapped;
    }
    return $row;
  }
}