<?php

namespace Drupal\term_reference_tree\Element;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\CompositeFormElementTrait;
use Drupal\Core\Render\Element\FormElement;

/**
 * Provides a form element for term reference tree.
 *
 * @FormElement("checkbox_tree")
 */
class CheckboxTree extends FormElement {

  use CompositeFormElementTrait;

  /**
   * {@inheritdoc}
   */
  public function getInfo() {
    $class = get_class($this);
    return [
      '#input' => TRUE,
      '#tree' => TRUE,
      '#process' => [
        [$class, 'processCheckboxTree'],
      ],
      '#pre_render' => [
        [$class, 'preRenderCompositeFormElement'],
      ],
      '#default_value' => [],
      '#attached' => [
        'library' => [
          'term_reference_tree/term_reference_tree_js',
          'term_reference_tree/term_reference_tree_css',
        ],
      ],
      '#theme' => 'checkbox_tree',
    ];
  }

  /**
   * Processes a checkboxes form element.
   */
  public static function processCheckboxTree(&$element, FormStateInterface $form_state, &$complete_form) {
    $value = is_array($element['#value']) ? $element['#value'] : [];
    $allowed = [];
    $filter = FALSE;
    if (!empty($element['#max_choices']) && $element['#max_choices'] != '-1') {
      $element['#attached']['drupalSettings'] = [
        'term_reference_tree' => [
          'trees' => [
            $element['#id'] => [
              'max_choices' => $element['#max_choices'],
            ],
          ],
        ],
      ];
    }
    if (!empty($element['#allowed'])) {
      $allowed = $element['#allowed'];
      $filter = TRUE;
    }

    if (empty($element['#options'])) {
      $options_tree = [];
      foreach ($element['#vocabularies'] as $vocabulary) {
        $options = _term_reference_tree_get_term_hierarchy(0, $vocabulary->id(), $allowed, $filter, '', $value);
        $options_tree = array_merge($options_tree, $options);
      }
      $element['#options_tree'] = $options_tree;
      $element['#options'] = _term_reference_tree_get_options($element['#options_tree'], $allowed, $filter);
    }

    $terms = !empty($element['#options_tree']) ? $element['#options_tree'] : [];

    $tree = new \stdClass();
    $tree->children = $terms;
    unset($element['#needs_validation']);
    $element['checkbox_tree'] = _term_reference_tree_build_level($element, $tree, $form_state, $value, $element['#max_choices'], [], 1);

    // track list
    if ($element['#track_list']) {
      if($form_state->getTriggeringElement()){
        // we are on ajax
        // $value is not updated on ajax callback
        // we need an other way to get wich options are checked
        // and give an accurate track list

        $parent = $element['#parents'][0]; // get the element's parent field
        $input = $form_state->getUserInput(); // get all inputs from form state
        $checkbox_tree_input = $input[$parent]['checkbox_tree']; // get the chekbox_tree input
        $selected = _term_reference_tree_get_flatten_selected_values($checkbox_tree_input); // get selected flattenized

        $track_list_input = $input[$parent]['track_list']; // get the current track_list input with correct order
        $old_list = array_keys($track_list_input); // flattenize

        // remove all chebox_tree input unselected from the list
        $list = [];
        foreach ($old_list as $id) {
          if(in_array($id, $selected)){
            $list[] = $id;
          }
        }
        // append newly selected
        $diff = array_diff($selected, $list);
        foreach ($diff as $id) {
          $list[] = $id;
        }
      }else{
        // if not ajax just get the default value
        $list = $value;
      }
      // build the track_list form element
      $element['track_list'] = _term_reference_tree_build_track_list_order($list, $element['#options']);
    }
    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
    $value = [];
    $element += ['#default_value' => []];
    foreach ($element['#default_value'] as $key) {
      if (isset($key['target_id'])) {
        $value[$key['target_id']] = $key['target_id'];
      }
    }
    return $value;
  }

}
