/**
  * This function extracts headings from the html formatted $content.  It will pull out
  * only the required headings as specified in the options.  For all qualifying headings,
  * this function populates the $find and $replace arrays (both passed by reference)
  * with what to search and replace with.
  *
  * Returns a HTML formatted string of list items for each qualifying heading.  This
  * is everything between and NOT including <ul> and </ul>
  *
  * @access private
  * @since  1.0
  * @static
  *
  * @param array  $find
  * @param array  $replace
  * @param string $content
  *
  * @return bool|string
  */
 public static function extract_headings(&$find, &$replace, $content = '')
 {
     global $wp_query;
     $post = $wp_query->post;
     $matches = array();
     $anchor = '';
     $items = '';
     $headings = get_post_meta($post->ID, '_ez-toc-heading-levels', TRUE);
     $exclude = get_post_meta($post->ID, '_ez-toc-exclude', TRUE);
     $altText = get_post_meta($post->ID, '_ez-toc-alttext', TRUE);
     if (!is_array($headings)) {
         $headings = array();
     }
     if (empty($headings)) {
         $headings = ezTOC_Option::get('heading_levels', array());
     }
     if (empty($exclude)) {
         $exclude = ezTOC_Option::get('exclude');
     }
     // reset the internal collision collection as the_content may have been triggered elsewhere
     // eg by themes or other plugins that need to read in content such as metadata fields in
     // the head html tag, or to provide descriptions to twitter/facebook
     self::$collision_collector = array();
     if (is_array($find) && is_array($replace) && $content) {
         // get all headings
         // the html spec allows for a maximum of 6 heading depths
         if (preg_match_all('/(<h([1-6]{1})[^>]*>).*<\\/h\\2>/msuU', $content, $matches, PREG_SET_ORDER)) {
             // remove undesired headings (if any) as defined by heading_levels
             if (count($headings) != 6) {
                 $new_matches = array();
                 for ($i = 0; $i < count($matches); $i++) {
                     if (in_array($matches[$i][2], $headings)) {
                         $new_matches[] = $matches[$i];
                     }
                 }
                 $matches = $new_matches;
             }
             // remove specific headings if provided via the 'exclude' property
             if ($exclude) {
                 $excluded_headings = explode('|', $exclude);
                 $excluded_count = count($excluded_headings);
                 if ($excluded_count > 0) {
                     for ($j = 0; $j < $excluded_count; $j++) {
                         $excluded_headings[$j] = preg_quote($excluded_headings[$j]);
                         // escape some regular expression characters
                         // others: http://www.php.net/manual/en/regexp.reference.meta.php
                         $excluded_headings[$j] = str_replace(array('\\*'), array('.*'), trim($excluded_headings[$j]));
                     }
                     $new_matches = array();
                     for ($i = 0; $i < count($matches); $i++) {
                         $found = FALSE;
                         for ($j = 0; $j < $excluded_count; $j++) {
                             if (@preg_match('/^' . $excluded_headings[$j] . '$/imU', strip_tags($matches[$i][0]))) {
                                 $found = TRUE;
                                 break;
                             }
                         }
                         if (!$found) {
                             $new_matches[] = $matches[$i];
                         }
                     }
                     if (count($matches) != count($new_matches)) {
                         $matches = $new_matches;
                     }
                 }
             }
             // remove empty headings
             $new_matches = array();
             for ($i = 0; $i < count($matches); $i++) {
                 if (trim(strip_tags($matches[$i][0])) != FALSE) {
                     $new_matches[] = $matches[$i];
                 }
             }
             if (count($matches) != count($new_matches)) {
                 $matches = $new_matches;
             }
             $toc = $matches;
             // Replace headers with toc alt text.
             if ($altText) {
                 $alt_headings = array();
                 $split_headings = preg_split('/\\r\\n|[\\r\\n]/', $altText);
                 $split_headings_count = count($split_headings);
                 if ($split_headings) {
                     for ($k = 0; $k < $split_headings_count; $k++) {
                         $explode_headings = explode('|', $split_headings[$k]);
                         if (0 < strlen($explode_headings[0]) && 0 < strlen($explode_headings[1])) {
                             $alt_headings[$explode_headings[0]] = $explode_headings[1];
                         }
                     }
                 }
                 if (0 < count($alt_headings)) {
                     for ($i = 0; $i < count($toc); $i++) {
                         foreach ($alt_headings as $original_heading => $alt_heading) {
                             $original_heading = preg_quote($original_heading);
                             // escape some regular expression characters
                             // others: http://www.php.net/manual/en/regexp.reference.meta.php
                             $original_heading = str_replace(array('\\*'), array('.*'), trim($original_heading));
                             if (@preg_match('/^' . $original_heading . '$/imU', strip_tags($toc[$i][0]))) {
                                 //$matches[ $i ][0] = str_replace( $original_heading, $alt_heading, $matches[ $i ][0] );
                                 $toc[$i][0] = $alt_heading;
                             }
                         }
                     }
                 }
             }
             // check minimum number of headings
             if (count($matches) >= ezTOC_Option::get('start')) {
                 for ($i = 0; $i < count($matches); $i++) {
                     // get anchor and add to find and replace arrays
                     $anchor = isset($toc[$i][0]) ? self::url_anchor_target($toc[$i][0]) : self::url_anchor_target($matches[$i][0]);
                     $find[] = $matches[$i][0];
                     $replace[] = str_replace(array($matches[$i][1], '</h' . $matches[$i][2] . '>'), array($matches[$i][1] . '<span class="ez-toc-section" id="' . $anchor . '">', '</span></h' . $matches[$i][2] . '>'), $matches[$i][0]);
                     // assemble flat list
                     if (!ezTOC_Option::get('show_hierarchy')) {
                         $items .= '<li><a href="' . esc_url('#' . $anchor) . '">';
                         //$title  = apply_filters( 'ez_toc_title', strip_tags( wp_kses_post( $toc[ $i ][0] ) ) );
                         $title = strip_tags(apply_filters('ez_toc_title', $toc[$i][0]));
                         //if ( 'decimal' == ezTOC_Option::get( 'counter' ) ) {
                         //
                         //	$items .= count( $replace ) . ' ';
                         //}
                         $items .= $title . '</a></li>';
                     }
                 }
                 // build a hierarchical toc?
                 // we could have tested for $items but that var can be quite large in some cases
                 if (ezTOC_Option::get('show_hierarchy')) {
                     $items = self::build_hierarchy($toc);
                 }
             }
         }
     }
     return $items;
 }