<?php
class Relational {

  /**
   * Função privada que gera as opções para o campo de seleção (select) com base no tipo de post (post_type).
   *
   * @param string $post_type - O tipo de post para o qual gerar as opções.
   *
   * @return array - Um array multidimensional contendo as opções geradas.
   */
  private static function generate_options($post_type) {
    $args = array(
      'post_type' => $post_type,
      'orderby' => 'post_title',
      'order' => 'ASC',
      'posts_per_page' => -1
    );

    $posts = get_posts($args);

    $options = array();

    foreach ($posts as $post) {
      if (!isset($options[$post->post_type]))
        $options[$post->post_type] = array();

      $options[$post->post_type][] = array(
        'ID' => $post->ID,
        'post_title' => $post->post_title,
        'post_status' => $post->post_status
      );
    }

    return $options;
  }

  /**
   * Função privada que gera o campo de seleção (select) com as opções disponíveis.
   *
   * @param string $name - O nome do campo (atributo "name" do select).
   * @param string $placeholder - O texto de placeholder para o select.
   * @param bool $multiple - Indica se o select deve permitir seleção múltipla (true) ou única (false).
   * @param array $options - As opções geradas pela função generate_options().
   * @param mixed $value - O valor selecionado no campo (pode ser um único valor ou um array, se múltiplo).
   *
   * @return string - O HTML do campo de seleção (select) gerado.
   */
  private static function generate_select($name, $placeholder, $multiple, $options, $value) {
    if ($multiple)
      $html = sprintf('<select name="%s[]" multiple data-placeholder="%s" class="o2-select-relational">', $name, $placeholder);
    else
      $html = sprintf('<select name="%s" data-placeholder="%s" class="o2-select-relational">', $name, $placeholder);

    $html .= '<option value=""></option>';

    foreach ($options as $post_type => $array) {
      $post_type_object = get_post_type_object($post_type);
      $label = $post_type_object ? $post_type_object->label : $post_type;

      $html .= sprintf('<optgroup label="%s">', $label);

      $selected = is_array($value) ? $value : array($value);
      foreach ($array as $option) {
        $is_selected = array_search($option['ID'], $selected) !== false ? 'selected' : '';

        if ($option['post_status'] !== 'publish')
          $html .= sprintf('<option value="%d" %s>%s (<b>%s</b>)</option>', $option['ID'], $is_selected, $option['post_title'], $option['post_status']);
        else
          $html .= sprintf('<option value="%d" %s>%s</option>', $option['ID'], $is_selected, $option['post_title']);
      }

      $html .= '</optgroup>';
    }

    $html .= '</select>';

    return $html;
  }

  /**
   * Função pública que renderiza o campo de seleção (select) com opções relacionadas aos tipos de post (post_type).
   *
   * @param string $name - O nome do campo (atributo "name" do select).
   * @param mixed $value - O valor selecionado no campo (pode ser um único valor ou um array, se múltiplo).
   * @param array $attrs - Um array com os atributos personalizados para o campo (opcional).
   *
   * @return string - O HTML do campo de seleção (select) gerado.
   */
  public static function render($name, $value, $attrs = array()) {
    $args_default = array(
      'placeholder' => 'Selecione uma opção',
      'multiple' => false,
      'post_type' => 'any',
    );

    $args = array_merge($args_default, $attrs);

    if ($args['multiple'] && empty($attrs['placeholder']))
      $args['placeholder'] = 'Selecione uma ou mais opções';

    $options = self::generate_options($args['post_type']);
    $select = self::generate_select($name, $args['placeholder'], $args['multiple'], $options, $value);

    return $select;
  }
}
