/*
 (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.
 */

#include <cstdio>

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

// Demonstrates how to collect, curate, and write scored Brood fragment hits
// with OEScoreHitlist starting from a query and fragment database.

class BroodScoreHitlistOptions : public OESystem::OEOptions
{
public:
  BroodScoreHitlistOptions(std::string name = "BroodScoreHitlistOptions")
    : OESystem::OEOptions(name)
  {
    OESystem::OEStringParameter dbParam("-db");
    dbParam.SetRequired(true);
    dbParam.SetVisibility(OESystem::OEParamVisibility::Simple);
    dbParam.SetBrief("Brood database folder");
    m_dbParam = AddParameter(dbParam);

    OESystem::OEUIntParameter maxHitsParam("-maxScoreHits", 100);
    maxHitsParam.SetVisibility(OESystem::OEParamVisibility::Simple);
    maxHitsParam.SetBrief("Maximum number of unique scored fragments curated by OEScoreHitlist");
    m_maxHitsParam = AddParameter(maxHitsParam);

  }

  BroodScoreHitlistOptions(const BroodScoreHitlistOptions&) = default;
  BroodScoreHitlistOptions& operator=(const BroodScoreHitlistOptions&) = default;
  ~BroodScoreHitlistOptions() override = default;
  BroodScoreHitlistOptions* CreateCopy() const override { return new BroodScoreHitlistOptions(*this); }

  std::string GetDataBase() const { return m_dbParam->GetStringValue(); }

  unsigned int GetMaxScoreHits() const
  {
    if (m_maxHitsParam->GetHasValue())
      return static_cast<unsigned int>(std::stoi(m_maxHitsParam->GetStringValue()));
    return static_cast<unsigned int>(std::stoi(m_maxHitsParam->GetStringDefault()));
  }

private:
  OESystem::OEParameter* m_dbParam;
  OESystem::OEParameter* m_maxHitsParam;
};

int main(int argc, char* argv[])
{
  BroodScoreHitlistOptions broodOpts;
  OEChem::OESimpleAppOptions opts(
      broodOpts,
      "BroodScoreHitlist",
      OEChem::OEFileStringType::Mol3D,
      OEChem::OEFileStringType::Mol3D);

  if (OESystem::OEConfigureOpts(opts, argc, argv, false) == OESystem::OEOptsConfigureStatus::Help)
    return 0;
  broodOpts.UpdateValues(opts);

  OEChem::oemolistream ifs;
  if (!ifs.open(opts.GetInFile()))
    OESystem::OEThrow.Fatal("Unable to open %s for reading", opts.GetInFile().c_str());

  OEChem::oemolostream ofs;
  if (!ofs.open(opts.GetOutFile()))
    OESystem::OEThrow.Fatal("Unable to open %s for writing", opts.GetOutFile().c_str());

  OEBioisostere::OEBroodQuery query;
  unsigned retCode = OEBioisostere::OEReadBroodQuery(ifs, query);
  if (retCode != OEBioisostere::OEBroodStatusCode::Success)
    OESystem::OEThrow.Fatal("Failed to read query: %s", OEBioisostere::OEGetBroodStatus(retCode).c_str());

  // Read database packets and overlay them against the query.
  OEBioisostere::OEDBReader reader;
  if (reader.Init(broodOpts.GetDataBase(), query) != OEBioisostere::OEBroodStatusCode::Success)
    OESystem::OEThrow.Fatal("Unable to open Brood database '%s'", broodOpts.GetDataBase().c_str());

  OEBioisostere::OEBroodOverlay overlay;
  overlay.SetupRef(query);

  // Use OEScoreHitlist to deduplicate and rank scored fragment matches.
  OEBioisostere::OEScoreHitlist scoreHitlist(broodOpts.GetMaxScoreHits());

  unsigned packetCount = 0;
  OEBioisostere::OEBroodDBPacket packet;
  while (reader.GetNextPacket(packet))
  {
    ++packetCount;
    scoreHitlist.AddScores(overlay.Overlay(packet));
  }

  scoreHitlist.Build();

  printf("Database packets processed:    %u\n", packetCount);
  printf("Total scores added:            %u\n", scoreHitlist.GetAddCount());
  printf("Unique fragment matches:       %u\n", scoreHitlist.GetMatchCount());
  printf("Duplicate scores filtered:     %u\n", scoreHitlist.GetDuplicateCount());
  printf("Curated score hits:            %u\n", scoreHitlist.GetHitCount());

  // Write the curated fragment hits selected by the score hitlist.
  for (const auto& score : scoreHitlist.GetHits())
    OEChem::OEWriteConstMolecule(ofs, score.GetFrag());

  printf("Wrote %u molecules to %s\n", scoreHitlist.GetHitCount(), opts.GetOutFile().c_str());
  return 0;
}



