<?php
namespace nur\passwd;

abstract class AbstractGenerator {
  const NB_BLOCKS = 4;
  const NB_PUNCTS = 2;

  const CONSONNES = "bcdfghjklmnpqrstvwxz";
  const VOYELLES = "aeiuoy";
  const PUNCTS = "+-*/=!@?:#";
  const MAPPINGS = [
    # éviter certaines combinaisons potentiellement gênantes
    "sexe" => "xese",
    "sexy" => "xyse",
    "suce" => "cesu",
    "bite" => "tebi",
    "cabo" => "boca",
    "pipe" => "pepi",
    "cocu" => "cuco",
    "pute" => "tepu",
  ];

  function __construct(?int $nbBlocks=null, ?int $nbPuncts=null) {
    if ($nbBlocks === null) $nbBlocks = static::NB_BLOCKS;
    if ($nbPuncts === null) $nbPuncts = static::NB_PUNCTS;
    if ($nbBlocks < 1) $nbBlocks = 1;
    if ($nbPuncts > $nbBlocks) $nbPuncts = $nbBlocks;
    $this->nbBlocks = $nbBlocks;
    $this->nbPuncts = $nbPuncts;
  }

  /** @var int */
  protected $nbBlocks, $nbPuncts;

  function generate(?int $nbBlocks=null, ?int $nbPuncts=null, ?int $seed=null): string {
    if ($nbBlocks === null) $nbBlocks = $this->nbBlocks;
    if ($nbPuncts === null) $nbPuncts = $this->nbPuncts;
    if ($seed !== null) mt_srand($seed);
    $maxc = strlen(self::CONSONNES) - 1;
    $maxv = strlen(self::VOYELLES) - 1;
    $maxp = strlen(self::PUNCTS) - 1;
    $size = $nbBlocks * 2;
    $sindex = 0;
    $syllabes = [];
    while ($sindex < $size) {
      $lindex = count($syllabes) - 1;
      do {
        $consonne = self::CONSONNES[mt_rand(0, $maxc)];
        $voyelle = self::VOYELLES[mt_rand(0, $maxv)];
        $syllabe = $consonne.$voyelle;
        # pas deux fois la même syllabe de suite
        $repetition = ($lindex >= 0 && $syllabes[$lindex] == $syllabe)
          || ($lindex >= 1 && $syllabes[$lindex - 1] == $syllabe);
      } while ($repetition);
      $syllabes[] = $syllabe;
      $sindex++;
      if ($nbPuncts > 0 && $sindex % 2 == 0 && mt_rand() % 2 == 0) {
        $syllabes[] = self::PUNCTS[mt_rand(0, $maxp)];
        $nbPuncts--;
      }
    }
    $password = implode("", $syllabes);
    foreach (self::MAPPINGS as $from => $to) {
      $password = str_replace($from, $to, $password);
    }
    return $password;
  }
}
