/******************************************************************************
  This source file is part of the Avogadro project.
  This source code is released under the 3-Clause BSD License, (see "LICENSE").
******************************************************************************/

#include "iotests.h"

#include <gtest/gtest.h>

#include <avogadro/core/atom.h>
#include <avogadro/core/molecule.h>
#include <avogadro/core/unitcell.h>
#include <avogadro/core/vector.h>

#include <avogadro/io/lammpsformat.h>

#include <fstream>
#include <sstream>
#include <string>

using Avogadro::Vector3;
using Avogadro::Core::Atom;
using Avogadro::Core::Molecule;
using Avogadro::Core::UnitCell;
using Avogadro::Io::FileFormat;
using Avogadro::Io::LammpsDataFormat;
using Avogadro::Io::LammpsTrajectoryFormat;

TEST(LammpsTest, read)
{
  LammpsTrajectoryFormat multi;
  multi.open(AVOGADRO_DATA "/data/silicon_bulk.dump",
             FileFormat::Read | FileFormat::MultiMolecule);
  Molecule molecule, molecule2;

  // Read in the structure.
  EXPECT_TRUE(multi.readMolecule(molecule));
  ASSERT_EQ(multi.error(), "");

  // First, let's check the unit cell
  UnitCell* uc = molecule.unitCell();
  bool status = true;

  EXPECT_EQ(uc->aVector(),
            Vector3(2.7155000000000001e+01, 0.00000000, 0.00000000));
  EXPECT_EQ(uc->bVector(),
            Vector3(0.00000000, 2.7155000000000001e+01, 0.00000000));
  EXPECT_EQ(uc->cVector(),
            Vector3(0.00000000, 0.00000000, 2.7155000000000001e+01));

  // Check that the number of atoms per step and number of steps in the
  // trajectory were read correctly
  EXPECT_EQ(molecule.atomCount(), 1000);
  EXPECT_EQ(molecule.coordinate3dCount(), 11);

  // First frame
  EXPECT_EQ(molecule.timeStep(0, status), 0);

  // Check a couple of positions to make sure they were read correctly
  EXPECT_EQ(molecule.atom(1).position3d().x(), 1.35775);
  EXPECT_EQ(molecule.atom(1).position3d().y(), 1.35775);
  EXPECT_EQ(molecule.atom(1).position3d().z(), 1.35775);
  EXPECT_EQ(molecule.atom(4).position3d().x(), 10.862);
  EXPECT_EQ(molecule.atom(4).position3d().y(), 0);
  EXPECT_EQ(molecule.atom(4).position3d().z(), 0);

  // Switching to second frame
  EXPECT_TRUE(molecule.setCoordinate3d(1));
  EXPECT_EQ(molecule.timeStep(1, status), 10);

  // Check a couple of positions to make sure they were read correctly
  EXPECT_EQ(molecule.atom(1).position3d().x(), 1.34317);
  EXPECT_EQ(molecule.atom(1).position3d().y(), 1.30464);
  EXPECT_EQ(molecule.atom(1).position3d().z(), 1.42722);
  EXPECT_EQ(molecule.atom(4).position3d().x(), 10.7867);
  EXPECT_EQ(molecule.atom(4).position3d().y(), -0.0484348);
  EXPECT_EQ(molecule.atom(4).position3d().z(), -0.0809766);

  // Switching to last frame
  EXPECT_TRUE(molecule.setCoordinate3d(10));
  EXPECT_EQ(molecule.timeStep(10, status), 100);

  // Check a couple of positions to make sure they were read correctly
  EXPECT_EQ(molecule.atom(1).position3d().x(), 1.37614);
  EXPECT_EQ(molecule.atom(1).position3d().y(), 1.34302);
  EXPECT_EQ(molecule.atom(1).position3d().z(), 1.39375);
  EXPECT_EQ(molecule.atom(4).position3d().x(), 10.6846);
  EXPECT_EQ(molecule.atom(4).position3d().y(), -0.0479311);
  EXPECT_EQ(molecule.atom(4).position3d().z(), -0.0770097);
}

TEST(LammpsTest, modes)
{
  // This tests some of the mode setting/checking code
  LammpsTrajectoryFormat format;
  format.open(AVOGADRO_DATA "/data/silicon_bulk.dump", FileFormat::Read);
  EXPECT_TRUE(format.isMode(FileFormat::Read));
  EXPECT_TRUE(format.mode() & FileFormat::Read);
  EXPECT_FALSE(format.isMode(FileFormat::Write));

  // Try some combinations now.
  format.open(AVOGADRO_DATA "/data/silicon_bulk.dump",
              FileFormat::Read | FileFormat::MultiMolecule);
  EXPECT_TRUE(format.isMode(FileFormat::Read));
  EXPECT_TRUE(format.isMode(FileFormat::Read | FileFormat::MultiMolecule));
  EXPECT_TRUE(format.isMode(FileFormat::MultiMolecule));
}

TEST(LammpsTest, write)
{
  Molecule molecule;
  molecule.addAtom(5).setPosition3d(Vector3(1.2, 1.6, 2.0));
  molecule.addAtom(7).setPosition3d(Vector3(3.0, 2.4, 2.5));

  {
    LammpsDataFormat lmpdat;
    molecule.setData("name", "left-handed system [j, i, k]");
    auto* const uc =
      new UnitCell({ 0.0, 4.0, 0.0 }, { 5.0, 0.0, 0.0 }, { 0.0, 0.0, 6.0 });
    molecule.setUnitCell(uc);

    std::string output;
    ASSERT_TRUE(lmpdat.writeString(output, molecule));
    EXPECT_EQ(output, R"(left-handed system [j, i, k]
2 atoms
0 bonds
2 atom types
  0.000000   4.000000 xlo xhi
  0.000000   5.000000 ylo yhi
  0.000000   6.000000 zlo zhi
  0.000000   0.000000   0.000000 xy xz yz


Masses

1   10.81
2   14.007



Atoms

1 1   1.600000   1.200000   2.000000
2 2   2.400000   3.000000   2.500000


)");
  }

  {
    LammpsDataFormat lmpdat;
    molecule.setData("name", "right-handed silicon primitive cell");
    auto* const uc =
      new UnitCell({ 0.0, 2.0, 2.0 }, { 2.0, 0.0, 2.0 }, { 2.0, 2.0, 0.0 });
    molecule.setUnitCell(uc);

    std::string output;
    ASSERT_TRUE(lmpdat.writeString(output, molecule));
    EXPECT_EQ(output, R"(right-handed silicon primitive cell
2 atoms
0 bonds
2 atom types
  0.000000   2.828427 xlo xhi
  0.000000   2.449490 ylo yhi
  0.000000   2.309401 zlo zhi
  1.414214   1.414214   0.816497 xy xz yz


Masses

1   10.81
2   14.007



Atoms

1 1   2.545584   1.143095   0.461880
2 2   3.464823   2.490315   1.674316


)");
  }
}
