//
//  Copyright (c) 2008, Novartis Institutes for BioMedical Research Inc.
//  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Novartis Institutes for BioMedical Research Inc.
//       nor the names of its contributors may be used to endorse or promote
//       products derived from this software without specific prior
//       written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//  created by Nik Stiefl May 2008
//

#include <RDGeneral/test.h>
#include <RDGeneral/RDLog.h>
#include <GraphMol/RDKitBase.h>
#include "FileParsers.h"
#include <GraphMol/SmilesParse/SmilesWrite.h>
#include <RDGeneral/FileParseException.h>
#include <RDGeneral/BadFileException.h>

#include <string>

using namespace RDKit;

void testGeneral(std::string rdbase) {
  BOOST_LOG(rdInfoLog) << "---------------------------------------"
                       << std::endl;
  BOOST_LOG(rdInfoLog) << "-- testing general mol2 file parsing --"
                       << std::endl;
  BOOST_LOG(rdInfoLog) << "---------------------------------------"
                       << std::endl;

  {
    bool ok = false;
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/nonExistFile.mol2";
    try {
      RWMol *m = Mol2FileToMol(fName);
      delete m;
    } catch (const BadFileException &) {
      ok = true;
    }
    TEST_ASSERT(ok);
  }
  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/pyrazole_pyridine.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getNumAtoms() == 5);

    // this was sf.net issue 2727976:
    TEST_ASSERT(m->getNumConformers() == 1);
    TEST_ASSERT(m->getConformer().is3D());
    TEST_ASSERT(feq(m->getConformer().getAtomPos(0).x, 1.5019));
    TEST_ASSERT(feq(m->getConformer().getAtomPos(0).y, 1.0435));
    TEST_ASSERT(feq(m->getConformer().getAtomPos(0).z, 0.0000));

    delete m;
  }
  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/benzene.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getNumAtoms() == 6);
    delete m;
  }
  {
    bool ok = false;
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/mol_noatoms.mol2";
    try {
      RWMol *m = Mol2FileToMol(fName);
      delete m;
    } catch (const FileParseException &) {
      ok = true;
    }
    TEST_ASSERT(ok);
  }
  {
    bool ok = false;
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/mol_nomol.mol2";
    try {
      RWMol *m = Mol2FileToMol(fName);
      delete m;
    } catch (const FileParseException &) {
      ok = true;
    }
    TEST_ASSERT(ok);
  }
  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/lonePairMol.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getNumAtoms() == 5 && m->getNumBonds() == 4);
    delete m;
  }
  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/symmetricGuanidine.mol2";
    RWMol *m = Mol2FileToMol(fName, false);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getAtomWithIdx(1)->getFormalCharge() == 1);
    TEST_ASSERT(m->getAtomWithIdx(8)->getFormalCharge() == 1);
    delete m;
  }

  {
    std::string fName =
        rdbase +
        "/Code/GraphMol/FileParsers/test_data/highlySymmetricGuanidine.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getAtomWithIdx(4)->getFormalCharge() == 1);
    TEST_ASSERT(m->getAtomWithIdx(12)->getFormalCharge() == 1);
    TEST_ASSERT(m->getAtomWithIdx(20)->getFormalCharge() == 1);
    delete m;
  }
  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/Noxide.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getAtomWithIdx(8)->getFormalCharge() == 1);
    TEST_ASSERT(m->getAtomWithIdx(9)->getFormalCharge() == -1);
    delete m;
  }
  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/Noxide.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getAtomWithIdx(8)->getFormalCharge() == 1);
    TEST_ASSERT(m->getAtomWithIdx(9)->getFormalCharge() == -1);
    delete m;
  }
  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/fusedRing.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);

    TEST_ASSERT(m->getAtomWithIdx(0)->getFormalCharge() == 0);
    TEST_ASSERT(m->getAtomWithIdx(5)->getFormalCharge() == 0);
    TEST_ASSERT(m->getAtomWithIdx(8)->getFormalCharge() == 0);
    TEST_ASSERT(m->getAtomWithIdx(13)->getFormalCharge() == 0);
    delete m;
  }
  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/pyridiniumPhenyl.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getAtomWithIdx(5)->getFormalCharge() == 1);
    TEST_ASSERT(m->getAtomWithIdx(6)->getFormalCharge() == 0);
    delete m;
  }
  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/sulfonAmide.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getAtomWithIdx(1)->getFormalCharge() == 0);
    delete m;
  }
  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/chargedAmidineRWH.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getAtomWithIdx(6)->getFormalCharge() == 1);
    delete m;
  }
  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/chargedAmidineEC.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getAtomWithIdx(3)->getFormalCharge() == 1);
    delete m;
  }
  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/chargedAmidine.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getAtomWithIdx(9)->getFormalCharge() == 1);
    delete m;
  }
  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/dbtranslateCharged.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getAtomWithIdx(8)->getFormalCharge() == 1);
    delete m;
  }
  {
    std::string fName =
        rdbase +
        "/Code/GraphMol/FileParsers/test_data/dbtranslateUncharged.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getAtomWithIdx(8)->getFormalCharge() == 0);
    delete m;
  }
  {
    std::string fName =
        rdbase +
        "/Code/GraphMol/FileParsers/test_data/dbtranslateUnchargedRing.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getAtomWithIdx(2)->getFormalCharge() == 0);
    delete m;
  }

#if 0
  {
    std::string fName = rdbase + "/Code/GraphMol/FileParsers/test_data/Sulfonate.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    BOOST_LOG(rdInfoLog) <<MolToSmiles(*m)<<std::endl;
    delete m;
  }
#endif

  BOOST_LOG(rdInfoLog) << "------------------------------------" << std::endl;
  BOOST_LOG(rdInfoLog) << "-- DONE general mol2 file parsing --" << std::endl;
  BOOST_LOG(rdInfoLog) << "------------------------------------" << std::endl;
}

void testAromaticChargedFail(std::string rdbase) {
  BOOST_LOG(rdInfoLog) << "---------------------------------------"
                       << std::endl;
  BOOST_LOG(rdInfoLog) << "-- testing subst arom groups in mol2 --"
                       << std::endl;
  BOOST_LOG(rdInfoLog) << "---------------------------------------"
                       << std::endl;

  // this one checks on substituted aromatic groups that need to be charged,
  // e.g. c1ccccn1C which
  // should convert to c1cccc[n+]1C

  {
    // this one is supposed to have a sanitisation error!
    // have to fix that one next ...
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/badSubstPyridine.mol2";
    RWMol *m = Mol2FileToMol(fName);
    if (m) {
      delete m;
    }
  }

  BOOST_LOG(rdInfoLog) << "------------------------------------" << std::endl;
  BOOST_LOG(rdInfoLog) << "-- DONE subst arom groups in mol2 --" << std::endl;
  BOOST_LOG(rdInfoLog) << "------------------------------------" << std::endl;
}

void testIssue3399798(std::string rdbase) {
  BOOST_LOG(rdInfoLog) << "---------------------------------------"
                       << std::endl;
  BOOST_LOG(rdInfoLog) << "-- testing issue 3399798             --"
                       << std::endl;
  BOOST_LOG(rdInfoLog) << "---------------------------------------"
                       << std::endl;

  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/Issue3399798.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getAtomWithIdx(0)->getChiralTag() == Atom::CHI_UNSPECIFIED);
    TEST_ASSERT(m->getAtomWithIdx(3)->getChiralTag() == Atom::CHI_UNSPECIFIED);

    delete m;
  }

  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/Issue3399798.2.mol2";
    RWMol *m = Mol2FileToMol(fName);
    TEST_ASSERT(m);
    TEST_ASSERT(m->getAtomWithIdx(0)->getChiralTag() == Atom::CHI_UNSPECIFIED);
    TEST_ASSERT(m->getAtomWithIdx(3)->getChiralTag() != Atom::CHI_UNSPECIFIED);

    delete m;
  }

  BOOST_LOG(rdInfoLog) << "------------------------------------" << std::endl;
  BOOST_LOG(rdInfoLog) << "-- DONE                           --" << std::endl;
  BOOST_LOG(rdInfoLog) << "------------------------------------" << std::endl;
}

// Test the following GitHub issue: https://github.com/rdkit/rdkit/issues/114
void testIssue114(std::string rdbase) {
  BOOST_LOG(rdInfoLog) << "-----------------------------------" << std::endl;
  BOOST_LOG(rdInfoLog) << "-- testing GitHub issue #114     --" << std::endl;
  BOOST_LOG(rdInfoLog) << "-----------------------------------" << std::endl;

  std::string fName =
      rdbase + "/Code/GraphMol/FileParsers/test_data/EZ_mol2_issue114.mol2";
  RWMol *mol = Mol2FileToMol(fName);
  TEST_ASSERT(mol);
  TEST_ASSERT(mol->getBondWithIdx(1)->getStereo() == Bond::STEREOZ);
  delete mol;

  BOOST_LOG(rdInfoLog) << "------------------------------------" << std::endl;
  BOOST_LOG(rdInfoLog) << "-- DONE                           --" << std::endl;
  BOOST_LOG(rdInfoLog) << "------------------------------------" << std::endl;
}

void testGithub438(std::string rdbase) {
  BOOST_LOG(rdInfoLog) << "-----------------------------------" << std::endl;
  BOOST_LOG(rdInfoLog)
      << "-- testing GitHub issue #438: problems with metals in mol2 files  --"
      << std::endl;
  BOOST_LOG(rdInfoLog) << "-----------------------------------" << std::endl;

  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/github438_1.mol2";
    RWMol *mol = Mol2FileToMol(fName);
    TEST_ASSERT(mol);
    TEST_ASSERT(mol->getAtomWithIdx(0)->getFormalCharge() == 1);
    delete mol;
  }
  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/github438_2.mol2";
    RWMol *mol = Mol2FileToMol(fName);
    TEST_ASSERT(mol);
    TEST_ASSERT(mol->getAtomWithIdx(0)->getFormalCharge() == 2);
    delete mol;
  }
  BOOST_LOG(rdInfoLog) << "------------------------------------" << std::endl;
  BOOST_LOG(rdInfoLog) << "-- DONE                           --" << std::endl;
  BOOST_LOG(rdInfoLog) << "------------------------------------" << std::endl;
}

void testDisableCleanup(std::string rdbase) {
  BOOST_LOG(rdInfoLog) << "-----------------------------------" << std::endl;
  BOOST_LOG(rdInfoLog)
      << "-- testing disabling cleanup of substructures in Mol2 parsing  --"
      << std::endl;
  BOOST_LOG(rdInfoLog) << "-----------------------------------" << std::endl;

  {
    std::string fName =
        rdbase + "/Code/GraphMol/FileParsers/test_data/3505.mol2";
    bool sanitize = true, removeHs = true;
    Mol2Type variant = CORINA;
    {
      bool cleanupSubstructures = true;  // the default
      std::unique_ptr<ROMol> mol(Mol2FileToMol(fName, sanitize, removeHs,
                                               variant, cleanupSubstructures));
      TEST_ASSERT(mol);
      TEST_ASSERT(mol->getBondBetweenAtoms(3, 12));
      TEST_ASSERT(mol->getBondBetweenAtoms(3, 12)->getBondType() ==
                  Bond::SINGLE);
      TEST_ASSERT(mol->getAtomWithIdx(12)->getFormalCharge() == 0);
    }
    {
      bool cleanupSubstructures = false;
      std::unique_ptr<ROMol> mol(Mol2FileToMol(fName, sanitize, removeHs,
                                               variant, cleanupSubstructures));
      TEST_ASSERT(mol);
      TEST_ASSERT(mol->getBondBetweenAtoms(3, 12));
      TEST_ASSERT(mol->getBondBetweenAtoms(3, 12)->getBondType() ==
                  Bond::DOUBLE);
      TEST_ASSERT(mol->getAtomWithIdx(12)->getFormalCharge() == 1);
    }
  }
  BOOST_LOG(rdInfoLog) << "------------------------------------" << std::endl;
  BOOST_LOG(rdInfoLog) << "-- DONE                           --" << std::endl;
  BOOST_LOG(rdInfoLog) << "------------------------------------" << std::endl;
}

// FIX still missing chirality by 3D structure
//  still missing input std::string

int main(int argc, char *argv[]) {
  (void)argc;
  (void)argv;
  RDLog::InitLogs();

  std::string rdbase = getenv("RDBASE");

  testGeneral(rdbase);
  testAromaticChargedFail(rdbase);
  testIssue3399798(rdbase);
  testIssue114(rdbase);
  testGithub438(rdbase);
  testDisableCleanup(rdbase);

  return 0;
}
