use strict;

sub search_entries
{

 # Declare all our variables as local to this subroutine:
 
 my ($searchpatt,$searchdata,$start,$stop,$pnum,$nkeys,$match,
	 $case,$page,$query,$entry,$field,$limit,$searchstr,$maxpages,
	 $maxrpages,@results,@results_page,%processed,%res,%data);


	# First thing we do is collect the following data:

	# 1. A 2D hash of all entry data we want to search.
	# 2. The number of entries in the above data.
	# 3. The search string to look for in the entry data.
	#    (Note: We strip any special chars from this.)

 %data		= %{get_data()};
 $nkeys		= keys(%data);
 $searchstr	= param("query") || '';
 $searchstr =~ s/\W//g;


	# If no search string was specified:
	
	# 1. Print a content header to tell it we're sending HTML.
	# 2. Call the prepare_search_page() function to print the search page.
	# 3. Tell it we're done (exit);

 if (!$searchstr){
  print "Content-type: text/html\n\n";
  print prepare_search_page(\%data);
  exit;
 }

 	# Now we need to gather any optional information as follows:
 	
 	# 1. If the parameter value matches a vaild pattern, we use it.
 	# 2. If the parameter value doesn't match a valid pattern, use the default.
 	
 	# Example:	If the 'case' parameter is 'n','no','y' or 'yes', we use it.
 	#			If the 'case' parameter isn't one of those values, default to 'n'.

 $match = ( param("match")	=~ /^(n?match|n?contain)$/	) ? $1 : 'contain';
 $case	= ( param("case")	=~ /^(n|no|y|yes)$/ 		) ? $1 : 'n';
 $field = ( param("field")	=~ /^(\w+)$/				) ? $1 : 'any';
 $page	= ( param("page")	=~ /^(\d+)$/				) ? $1 : 1;
 $limit = ( param("limit")	=~ /^(\d+)$/				) ? $1 : 10;

 # Re-construct the query string minus the requested page:
 $query = "action=search&match=$match&case=$case&field=$field&limit=$limit&query=$searchstr";


	# To search all entries, we need to use nested loops as follows:
	
	# 1. Loop over each key from the entries hash.
	# 2. Loop over each key from the subhash corrosponding to the above key.
		
 foreach my $key (keys %data){
  foreach my $sub (keys %{$data{$key}}){
   
   
	# Before we do anything with the data, we need to take care of special characters.
	# Special characters may be a part of an entry, but can interfere with the search.
   
	# For the moment all we need to take care of is square, round, and curly brackets.
	# We also remove all UBB code for the search preview to keep it neat.
	# Should it be neccessary, you can easily add more characters to be replaced.
   
   $data{$key}{$sub} =~ s!\[/?\w+\]!!gi;
   $data{$key}{$sub} =~ s!(\[|\]|\(|\)|\{|\})!chr(38).chr(35).ord($1).chr(59)!eg;

   
	# If the field name matches the one we want, or we want to search all fields:
   
	# 1. Copy the data from the entry field into $searchdata.
	# 2. Continue on to check for case sensitivity.
   
   if ( ($sub eq $field) || $field eq "any" ){
    $searchdata = $data{$key}{$sub};
  
  
    # Now we check for case sensitivity and prepare an appropriate search pattern:
    
    # 1. If it's not case sensitive:
    # 	a. Prepare a regex according to what type of match we want.
    # 	b. Append the 'i' switch to the prepared pattern.
    
    # 2. If it is case sensitive:
    #	a. Prepare a regex according to what type of match we want.
    #   b. Leave the regex unchanged (by default it's case sensitive).
    
    if ($case !~ /(y|yes)/){
     $searchpatt = ($match =~ /(match|nmatch)/) ? qr(^$searchstr$)i : qr($searchstr)i;
    }else{
     $searchpatt = ($match =~ /(match|nmatch)/) ? qr(^$searchstr$) : qr($searchstr);
    }


	# Here we test for the following possible matches:
	
	# 1. The match option is negated, and the data doesn't match the supplied pattern.
	# 2. The match option is normal, and the data does match the supplied pattern.
	
    if ( ($match =~ /^n\w+/ && $searchdata !~ /$searchpatt/) || 
		 ($match !~ /^n\w+/ && $searchdata =~ /$searchpatt/) ){
   
   
	# If we have a match from either of the above, continue as follows:
	
	# 1. Calculate which page the entry appears on and add it to the entry hash:
	
	#	a. Get the page number using the formula: (entry_number / number_per_page)
	#	b. If the above isn't a whole number, round it up to the nearest number.
	
     $pnum = int($data{$key}{'entry'} / $perpage);
     if ( ($data{$key}{'entry'} % $perpage) > 0 ){ $pnum++; }
     
    
	#	c. Get the maxium number of entry pages, using the formula: (num_of_entries / num_per_page)
	# 	d. If the above number isn't a whole number, round it up to the nearest number.
    # 	e. Calculate the actual page number using the formula: (max_pages - pagenum) + 1
    
     $maxpages = int($nkeys/$perpage);
     if (($nkeys % $perpage) > 0){$maxpages++;}
     $data{$key}{'page'} = int(($maxpages - $pnum) +1);
     
     
	# 2. If we haven't allready added this entry to our results list:
	#	a. Add it to our results list (@results).
	#	b. Add it to our processed list (%processed), so we don't double up.
	
     if (!$processed{$key}){ push (@results,$data{$key}); }
     $processed{$key} = 1;
    
    }
   }
  }
 }
   
 # Here we calculate the maximum number of result pages as follows:

 # 1. Get the maximum number of pages using the formula: (number_of_results / results_per_page)
 # 2. If the above isn't a whole number, round it up to the nearest whole number.

 $maxrpages = int(@results / $limit);	
 if ( (@results % $limit) > 0 ){ $maxrpages++; }
   
   
	# Now we need to calculate which results to display for which page.
	# This is done as follows:
	
	# 1. If we're on the first page:
	#	a. The start index will be zero (0).
	#	b. The stop index will be one less than the per page limit.
	
	# 2. If we're on any other page:
	#	a. The start index will folow the formula: (per_page*page_number) - per_page
	#	b. The stop index will follow the formula: (start_index + per_page) - 1
	
 if ($page == 1){
  $start = 0;
  $stop = $limit-1;
 }else{
  $start = ($limit*$page)-($limit);
  $stop = ($start+$limit)-1;
 }

	# Ok, Now we have done all the hard work, it's time to display some results.
	# We'll start by printing a content header to indicate we're sending HTML.
	
 print "Content-type: text/html\n\n";


 	# Next we have to get the HTML that makes up the search results page.
 	# This comes from a template file set in the config file like everything else.
 	
 @results_page = getfile($results_template,0);


	# Now we have something to print, we continue on and print the page.
	# We do this by looping over each line of HTML (above).
 	
 foreach my $pline (@results_page){
 
 
	# If the line contains a special content tag to display the search results,
	# We continue on as follows:
 
  if ($pline =~ /<!--BEGIN_SEARCH_RESULTS-->/){
  
  
	# 1. Feed the line through expand_vars() and print it incase it contains other code.
	# 2. If there are any results to display, continue on to display them as follows:
	
   print expand_vars($pline,'');
   if ($results[0]){
   
   
	# If we have any results for this page:
    if ($results[$start]){
    
    
	# Loop over each result, feeding it through search_replace() to return an array reference.
	# Now we just print out the HTML contained in the referenced array above ($entry).
	
     for (my $i=$start;$i<=$stop;$i++){
      if ($results[$i]){
       $results[$i]{'comments'} = substr($results[$i]{'comments'},0,70).' .....';
       $entry = search_replace($results[$i],$results_layout,1);
       foreach my $line (@$entry){ print expand_vars($line,''); }
      }
     }
   
   
	# If we don't have any results for the requested page:
	# Print an error message to let the user know the page doesn't exist.
   
    }else{
     print "No such page: $page";
    }
   }
   
   
   
	# If we have more than one page of results to be displayed, 
	# And if the line contains a special content tag to print page numbers:
   
  }elsif ($maxrpages > 1 && $pline =~ /<!--ECHO_PAGE_NUMS-->/){
  
  
	# 1. Feed the line through expand_vars() and print it incase it contains other code.
	# 2. If there are more than 5 pages of results, set the limit at 5.
	# 3. If we're on at least page 2, print a "<< Prev" link to the previous results page.
  
   print expand_vars($pline,'');
   if ($maxrpages > 5){$maxrpages = 5;}
   if ($page > 1){ print "<a href='http://$host$scripturl?$query&page=@{[$page-1]}' style='font-weight:bold'><< Prev</a>&nbsp;"; }
   
   
	# 4. Loop over each page number of results from 1 to the maximum as follows:
	# 	a. If we're up to the page currently being viewed, print the number as normal text.
	# 	b. If it's any other page number, print it as a link to that page.
   
   for (my $i=1;$i<=$maxrpages;$i++){
    if ($i == $page){
     print "<a>$i</a>&nbsp;";
    }else{
     print "<a href='http://$host$scripturl?$query&page=$i' style='font-weight:bold'>$i</a>&nbsp;";
    }   
   }
   
   
	# 5. If we're not up to the last page:
	# 	 Print a "Next >>" link to the next page of results.
	
   if ($results[$stop+1]){ print "<a href='http://$host$scripturl?$query&page=@{[$page+1]}' style='font-weight:bold'>Next >></a>"; }
  
  
  
	# If the line is just an ordinary line of HTML and doesn't contain any search specific tags:
  
	# 1. Find any occurances of the "<!--%%QUERY%%-->" tag and replace with the search string.
	# 2. Find any occurances of the "<!--%%MAXRESULTS%%-->" tag and replace with the number of results found.
	# 3. Feed the line through expand_vars() to handle generic content tags, and print the line.
  
  }else{
   $pline =~ s!<\!--\%\%QUERY\%\%-->!$searchstr!g;
   $pline =~ s!<\!--\%\%MAXRESULTS\%\%-->!@results!ge;
   print expand_vars($pline,'');
  }
  
  
 }		# Close the foreach() loop of the "Search Results" HTML.
 exit;	# Tell it we're done (exit).
}		# Close the subroutine.

1;		# Return true (required).