<?php

class ExpressionHelper
{
  const DICT_PLATE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  
  /**
   * 
   * @param string $plate
   * @return string[]
   */
  public static function getPlateValues($plate)
  {
    return self::getValues(self::parse($plate), self::DICT_PLATE);
  }
  
  /**
  * create an ExpressionObject from a string
  * @return ExpressionObject an ExpressionObject that describes the input string 
  */
  public static function parse($text)
  {
  	$expr = new ExpressionObject();
    $current = new ExpressionChunk();
    
    $expr->model = $text;
    
    $current = null;
    $inBrack = false;
    
    $textArray = str_split($text);
    
    foreach($textArray as $c)
    {
      if($c == '[')
      {
        if ($inBrack)
          return null;
        $inBrack = true;
        $current = null;
        continue;
      }
      
      if ($c == ']')
      {
        if (!$inBrack)
          return null;
        $inBrack = false;
        $current = null;
        continue;
      }

      if ($current == null)
      {
        $current = new ExpressionChunk();
        $expr->chunks[] = $current;
        if ($inBrack)
          $current->type = ExpressionChunk::CHUNK_TYPE_CHOICE;
        else
          if ($c == '?')
          {
            $current->type = ExpressionChunk::CHUNK_TYPE_ANY;
            $current->value = "?";
            $current = null;
            continue;
          }
      }
      else
        if ($c == '?')
        {
          $current = new ExpressionChunk();
          $current->type = ExpressionChunk::CHUNK_TYPE_ANY;
          $current->value = "?";
          $expr->chunks[] = $current;
          $current = null;
          continue;
      }

      $current->value .= $c;	
    }
  	
    return $expr;
  }
  
  /**
  * create corresponding values from the given ExpressionObject, using the specified dictionary
  * for example, the expression (parsed) "CA[NS]E?" with the dictionary "?!", will produce these values:
  *   => CANE?
  *   => CANE!
  *   => CASE?
  *   => CASE!
  * @return array an array of strings 
  */
  public static function getValues(ExpressionObject $expr, $dictionary)
  {
    $dict = str_split($dictionary);
    
    $values = array();
    
    foreach ($expr->chunks as $chunk)
    {
      if(count($values)<=0)
        $values = $values + $chunk->getValues($dict);
      else
      {
        $valuesNew = array();
        foreach ($values as $v)
        {
          foreach ($chunk->getValues($dict) as $c)
          {
            $valuesNew[] = $v.$c;
          }
        }
        $values = $valuesNew;
      }
    }    
    
    return $values;
  }
  
}

class ExpressionObject
{
  public $model = "";
  public $chunks = array();
}

class ExpressionChunk
{
  const CHUNK_TYPE_FIXED = 0;
  const CHUNK_TYPE_ANY = 1;
  const CHUNK_TYPE_CHOICE = 2;
  
  public $value = "";
  public $type = ExpressionChunk::CHUNK_TYPE_FIXED;
  
  public function getValues(array $dict)
  {
    switch($this->type)
    {
      case ExpressionChunk::CHUNK_TYPE_FIXED:
        return array($this->value);
      case ExpressionChunk::CHUNK_TYPE_ANY:
        return $dict;
      case ExpressionChunk::CHUNK_TYPE_CHOICE:
        return str_split($this->value);
      default:
        return array();
    }        
  }
}
