
// WikiSearch.java
// Andrew Davison, ad@coe.psu.ac.th, July 2023

/* Search Wikipedia pages using the Wikimedia Action API.
   (https://www.mediawiki.org/wiki/API:Main_page)

   Usage: WikiSearch [-hV] [-m=<maxMatches>] [-v=<viewNum>] <srchStr
   Searches Wikipedia for articles.
         <srchStr>            The search string.
     -h, --help               Show this help message and exit.
     -m, --max=<maxMatches>   Max no. of matches (default = 10).
     -v, --view=<viewNum>     View the numbered webpage.
     -V, --version            Print version information and exit.
 
  Examples:
    > compile WikiSearch.java

    > run WikiSearch -h
    > run WikiSearch wilmslow
    > run WikiSearch wilmslow -v2
    > run WikiSearch "imperial college" -m 5

*/

import java.util.*;
import java.util.concurrent.*;

import picocli.CommandLine;
import picocli.CommandLine.*;
import picocli.CommandLine.Model.CommandSpec;

import com.google.gson.Gson;


@Command(name = "WikiSearch", 
         mixinStandardHelpOptions = true,
         version = "WikiSearch 1.0",
         description = "Searches Wikipedia for articles.")
public class WikiSearch implements Callable<Integer>
{

  private static final int MAX_MATCHES = 50;

  private static final String WIKI_LINK = "https://en.wikipedia.org/wiki?curid=";
     /* add a page ID to this string to get 
        the URL for the Wikipedia page */


  // variables that represent the command line arguments 
  @Parameters(index = "0", description = "The search string.")
  private String srchStr;  


  @Option(names = { "-m", "--max" }, 
          description = "Max no. of matches (default = 10).")
  private int maxMatches = 10;

  @Option(names = { "-v", "--view" }, 
          description = "View the numbered webpage.")
  private int viewNum = -1;


  @Spec
  private CommandSpec spec;


  public Integer call()
  // called by Picocli with the command variables initialized
  { 

    if ((maxMatches <= 0) || (maxMatches > MAX_MATCHES)) {
      System.out.println("Max matches out of range; using " + MAX_MATCHES);
      maxMatches = MAX_MATCHES;
    }


    String request = 
          "https://en.wikipedia.org/w/api.php?action=query&" +
          // "origin=*&" +
          "list=search&srsearch=" + HTTPUtils.encode(srchStr) + "&" +
          "srlimit=" + maxMatches + "&" +
          "srinfo=totalhits&srprop=snippet&" +
          "format=json";

    /* For details on all the possible parameters, see:
       https://www.mediawiki.org/w/api.php?action=help&modules=query
       https://www.mediawiki.org/w/api.php?action=help&modules=query%2Bsearch
    */

    String response = HTTPUtils.getResponse(request);
    if (response == null)
      return 1;

    // HTTPUtils.prettyPrint(response);   // useful for debugging
    WikiResponse wr = new Gson().fromJson(response, WikiResponse.class);
        // map JSON data to Java objects
    if (wr.query == null) {
      WikiError werr = new Gson().fromJson(response, WikiError.class);
      System.out.println("Mediawiki error: " + werr.error.info);
      return 1;
    }
    
    int numHits = wr.query.searchinfo.totalhits;
    System.out.println("No. of hits: " + numHits);
    ArrayList<Search> results = wr.query.search;
    System.out.println("No. of results: " + results.size() + "\n");


    // load a wiki page or list matching pages info
    if ((viewNum != -1) && isViewable(results.size())) {
      Search result = results.get(viewNum-1);
      pageInfo(viewNum, result);
      HTTPUtils.showInBrowser(WIKI_LINK + result.pageid);
    }
    else {
      for (int i=0; i < results.size(); i++)
        pageInfo( (i+1), results.get(i));
    }

    return 0;
  } /* call */



  private void pageInfo(int num, Search result)
  // display info for this numbered page
  {
    System.out.println(num + ". " + result.title);
    System.out.println("Page ID: " + result.pageid);
    String snippet = result.snippet.replaceAll("<[^>]*>", "");
    System.out.println(snippet + "...\n");
  }  // end of pageInfo()



  private boolean isViewable(int numMatches)
  // can the supplied page number be viewed?
  {
    if ((viewNum > 0) && (viewNum <= numMatches))  // valid number
      return true;
    else if ((viewNum == 0) || (viewNum > numMatches)) {
      // has a result number, but it's invalid
      System.out.println("Result number out of range; using 1\n");
      viewNum = 1;
      return true;
    }

    return false;
  }  // end of isViewable()



  // --------------------------------------

  public static void main(String[] args)
  {
    int exitCode = new CommandLine(new WikiSearch()).execute(args);
    System.exit(exitCode);
  } /* main */


}  // end of WikiSearch class

