<?php
/**
 * Created by PhpStorm.
 * User: 91373
 * Date: 7/8/2016
 * Time: 6:27 PM
 */

namespace Element\Search;

class Search {

    /* @var $model \Illuminate\Database\Eloquent\Model */
    public function search( SearchableContract $model, $string ){
        $query = $this->formatQuery( $string );
        $models = $model->all();
        $returns = [];
        $toOpt = [];
        $optHits = [];
        $orgHits = [];

        /**
         * Count the number of times each word in the search
         * string is found in the $item's searchable fields
         * and push those to an array.
         */
        /* @var $item \Illuminate\Database\Eloquent\Model */
        foreach ( $models as $item ){
            $counts = [];
            foreach ( $query as $word ){
                $wordcount = 0;
                foreach ( $model->getSearchable() as $field ){
                    $wordcount = $wordcount + substr_count( strtolower( $item->$field ), $word );
                }
                $counts[ $word ] = $wordcount;
            }
            array_push( $toOpt, [
                'counts' => $counts,
                'item'   => $item
            ]);
        };

        /**
         * Convert the word found count for each individual
         * word to a single unified hit count, adjusting
         * for common words and letters and push to an
         * array.
         */
        foreach ( $toOpt as $formattedItem ){
            $hits = 0;
            foreach ( $formattedItem['counts'] as $word => $count ){
                if ( strlen( $word ) == 1 ){
                    $hits += ($count*0.1);
                }
                elseif ( strlen( $word ) == 2 ){
                    $hits += ($count*0.3);
                }
                else {
                    $hits += $count;
                }
            }
            array_push( $optHits, [
                'hits' => $hits,
                'item' => $formattedItem['item']
            ]);
        }

        /**
         * Sort the items in an array based on hit count.
         * Where the key is the number of hits, push all
         * items with that specific number of hits to
         * the sub array.
         */
        foreach ( $optHits as $hitCountItem ){
            if ( isset( $orgHits[ (string) $hitCountItem['hits'] ] ) ){
                array_push( $orgHits[ (string) $hitCountItem['hits'] ], $hitCountItem['item'] );
            }
            else {
                $orgHits[ (string) $hitCountItem['hits'] ] = [ $hitCountItem['item'] ];
            }
        }

        /**
         * Sort the array by the number of hits
         * from low to high. So, the items with
         * least hits to highest hits, still
         * grouped.
         */
        ksort( $orgHits );

        /**
         * Push each item to a single-dimensional array
         * in order of least number of hits to highest
         * number of hits.
         */
        foreach ( $orgHits as $hitCountGroup ){
            foreach ( $hitCountGroup as $item ){
                array_push( $returns, $item );
            }
        }

        /**
         * Reverse the order of the array
         * so that the first item has the
         * greatest number of hits, and
         * the last one has the least.
         */
        $returns = array_reverse( $returns );

        /**
         * return the array in the form of a
         * Laravel model collection
         */
        return collect( $returns );
    }

    public function formatQuery( $string ){
        $words = explode(' ', $string);
        $return = [];
        foreach ( $words as $word ){
            $word = preg_replace('/[^[:alpha:]]/', '', $word); // removes all punctuation
            $word = strtolower( $word );
            if ( ! in_array( $word, $return ) ) {
                array_push($return, $word);
            }
        }

        return $return;
    }

}