/*
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.   
 */
package org.apache.wiki.search;

import org.apache.wiki.WikiEngine;
import org.apache.wiki.WikiPage;
import org.apache.wiki.api.core.Engine;
import org.apache.wiki.api.core.Page;
import org.apache.wiki.api.search.QueryItem;
import org.apache.wiki.api.spi.Wiki;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;


/**
 * SearchMatcher performs the task of matching a search query to a page's contents. This utility class is isolated to simplify
 * WikiPageProvider implementations and to offer an easy target for upgrades. The upcoming(?) TranslatorReader rewrite will
 * presumably invalidate this, among other things.
 *
 * @since 2.1.5
 */
public class SearchMatcher {
	
    private final QueryItem[] m_queries;
    private final Engine m_engine;

    /**
     *  Creates a new SearchMatcher.
     *
     *  @param engine The Engine
     *  @param queries A list of queries
     */
    public SearchMatcher( final Engine engine, final QueryItem[] queries ) {
        m_engine = engine;
        m_queries = queries != null ? queries.clone() : null;
    }

    /**
     * Creates a new SearchMatcher.
     *
     * @param engine The Engine
     * @param queries A list of queries
     * @deprecated kept for compatibility with page/attachment providers not using public API. Use {@code SearchMatcher(Engine, QueryItem)}
     * instead.
     */
    @Deprecated
    public SearchMatcher( final WikiEngine engine, final org.apache.wiki.search.QueryItem[] queries ) {
        this( ( Engine )engine, queries );
    }

    /**
     * Compares the page content, available through the given stream, to the query items of this matcher. Returns a search result
     * object describing the quality of the match.
     *
     * <p>This method would benefit of regexps (1.4) and streaming. FIXME!
     * 
     * @param wikiname The name of the page
     * @param pageText The content of the page
     * @return A SearchResult item, or null, there are no queries
     * @throws IOException If reading page content fails
     */
    public org.apache.wiki.search.SearchResult matchPageContent( final String wikiname, final String pageText ) throws IOException {
        if( m_queries == null ) {
            return null;
        }

        final int[] scores = new int[ m_queries.length ];
        final BufferedReader in = new BufferedReader( new StringReader( pageText ) );
        String line;

        while( (line = in.readLine() ) != null ) {
            line = line.toLowerCase();

            for( int j = 0; j < m_queries.length; j++ ) {
                int index = -1;

                while( (index = line.indexOf( m_queries[j].word, index + 1 ) ) != -1 ) {
                    if( m_queries[j].type != QueryItem.FORBIDDEN ) {
                        scores[j]++; // Mark, found this word n times
                    } else {
                        // Found something that was forbidden.
                        return null;
                    }
                }
            }
        }

        //  Check that we have all required words.
        int totalscore = 0;

        for( int j = 0; j < scores.length; j++ ) {
            // Give five points for each occurrence of the word in the wiki name.
            if( wikiname.toLowerCase().contains( m_queries[ j ].word ) && m_queries[j].type != QueryItem.FORBIDDEN ) {
                scores[j] += 5;
            }

            //  Filter out pages if the search word is marked 'required' but they have no score.
            if( m_queries[j].type == QueryItem.REQUIRED && scores[j] == 0 ) {
                return null;
            }

            //  Count the total score for this page.
            totalscore += scores[j];
        }

        if( totalscore > 0 ) {
            return new SearchResultImpl( wikiname, totalscore );
        }

        return null;
    }

    /**
     *  A local search result.
     */
    public class SearchResultImpl implements org.apache.wiki.search.SearchResult {
    	
        final int  m_score;
        final Page m_page;

        /**
         *  Create a new SearchResult with a given name and a score.
         *  
         *  @param name Page Name
         *  @param score A score from 0+
         */
        public SearchResultImpl( final String name, final int score ) {
            m_page  = Wiki.contents().page( m_engine, name );
            m_score = score;
        }

        /**
         *  Returns Wikipage for this result.
         *  @return WikiPage
         */
        @Override
        public WikiPage getPage() {
            return ( WikiPage )m_page;
        }

        /**
         *  Returns a score for this match.
         *  
         *  @return Score from 0+
         */
        @Override
        public int getScore() {
            return m_score;
        }

        /**
         *  Returns an empty array, since BasicSearchProvider does not support context matching.
         *  
         *  @return an empty array
         */
        @Override
        public String[] getContexts() {
            // Unimplemented
            return new String[0];
        }
    }

}
