/* 
(C) 2022 Cadence Design Systems, Inc. (Cadence) 
All rights reserved.
TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
provided to current licensees or subscribers of Cadence products or
SaaS offerings (each a "Customer").
Customer is hereby permitted to use, copy, and modify the Sample Code,
subject to these terms. Cadence claims no rights to Customer's
modifications. Modification of Sample Code is at Customer's sole and
exclusive risk. Sample Code may require Customer to have a then
current license or subscription to the applicable Cadence offering.
THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED.  OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall Cadence be
liable for any damages or liability in connection with the Sample Code
or its use.
*/
/****************************************************************************
* Performs SMARTS search on substructure database file
****************************************************************************/

#include <openeye.h>
#include <oesystem.h>
#include <oechem.h>

#include "searchfastss.itf"

using namespace std;
using namespace OESystem;
using namespace OEChem;

////////////////////////////////////////////////////////////////////////
//  MAIN
////////////////////////////////////////////////////////////////////////

int main(int argc, char *argv[])
{
  OEInterface itf(InterfaceData);

  if(!OEParseCommandLine(itf, argc, argv))
    OEThrow.Fatal("Unable to interpret command line!");

  // check parameters

  const auto c = itf.Get<bool>("-count");
  const auto t = itf.Get<bool>("-titles");
  const auto o = itf.Has<string>("-out");

  if (!((c && !t && !o) || (!c && t && !o) || (!c && !t && o)))
    OEThrow.Fatal("Counting (-c) or outputting titles (-t) or molecules (-o) must be specified and are mutually exclusive.");

  oemolostream ofs;
  if (itf.Has<string>("-out"))
  {
    const auto ofname = itf.Get<string>("-out");
    if (!ofs.open(ofname))
      OEThrow.Fatal("Cannot open output file!");
  }

  const auto dbfname = itf.Get<string>("-db");
  const auto smarts = itf.Get<string>("-smarts");
  const auto nrthreads = itf.Get<unsigned>("-nrthreads");
  const auto maxmatches = itf.Get<unsigned>("-maxmatches");

  // initialize query

  OEQMol qmol;
  if (!OEParseSmarts(qmol, smarts.c_str()))
    OEThrow.Fatal("Unable to parse SMARTS pattern: %s", smarts.c_str());

  // initialize substructure search database

  const OESubSearchScreenTypeBase* screentype =
                OEGetSubSearchScreenType(OESubSearchScreenType::SMARTS);

  if (!OEIsValidSubSearchDatabase(dbfname, screentype))
    OEThrow.Fatal("Invalid SMARTS substructure search database file!");

  OESubSearchDatabase ssdb(OESubSearchDatabaseType::Default, nrthreads);
  OEConsoleProgressTracer tracer;
  if (!ssdb.Open(dbfname, tracer))
    OEThrow.Fatal("Substructure search database can not be initialized!");

  const string screenstr = screentype->GetName();
  const char*  infomsg = "Using %d processor(s) to search database with '%s'";
  OEThrow.Info(infomsg, ssdb.NumProcessors(), screenstr.c_str());

  // search database

  if (itf.Get<bool>("-count"))
  {
    OEThrow.Info("Number of hits: %d", ssdb.NumMatches(qmol));
  }
  else
  {
    OESubSearchQuery query(qmol, maxmatches);
    OESubSearchResult result;
    const auto status = ssdb.Search(result, query);

    cout << "Search status = " << OESubSearchStatusToName(status) << endl;

    cout << "Number of targets  = " << result.NumTargets()  << endl;
    cout << "Number of screened = " << result.NumScreened() << endl;
    cout << "Number of searched = " << result.NumSearched() << endl;
    cout << "Number of total matches = " << result.NumTotalMatches() << endl;
    cout << "Number of kept  matches = " << result.NumMatches() << endl;

    if (itf.Get<bool>("-titles"))
    {
      cout << "Matches:" << endl;
      for (OEIter<size_t> index = result.GetMatchIndices(); index; ++index)
        cout << ssdb.GetTitle(*index) << endl;
    }
    else if (itf.Has<string>("-out"))
    {
      OEGraphMol mol;
      for (OEIter<size_t> index = result.GetMatchIndices(); index; ++index)
      {
        if (ssdb.GetMolecule(mol, *index))
          OEWriteMolecule(ofs, mol);
      }
    }
  }
  return 0;
}
