// --------------------------------------------------------------------------
//                   OpenMS -- Open-Source Mass Spectrometry               
// --------------------------------------------------------------------------
// Copyright The OpenMS Team -- Eberhard Karls University Tuebingen,
// ETH Zurich, and Freie Universitaet Berlin 2002-2018.
// 
// This software is released under a three-clause BSD license:
//  * 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 any author or any participating institution 
//    may be used to endorse or promote products derived from this software 
//    without specific prior written permission.
// For a full list of authors, refer to the file AUTHORS. 
// --------------------------------------------------------------------------
// 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 ANY OF THE AUTHORS OR THE CONTRIBUTING 
// INSTITUTIONS 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.
// 
// --------------------------------------------------------------------------
// $Maintainer: Timo Sachsenberg $
// $Authors: $
// --------------------------------------------------------------------------

#include <OpenMS/CONCEPT/ClassTest.h>
#include <OpenMS/test_config.h>

///////////////////////////

#include <OpenMS/KERNEL/Feature.h>

///////////////////////////

START_TEST(Feature, "$Id$")

/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////

using namespace OpenMS;
using namespace std;

typedef OpenMS::BaseFeature::QualityType QualityType;

Feature* d_ptr = nullptr;
Feature* nullPointer = nullptr;
START_SECTION((Feature()))
{
	d_ptr = new Feature;
  TEST_NOT_EQUAL(d_ptr, nullPointer);
}
END_SECTION

START_SECTION((~Feature()))
{
	delete d_ptr;
}
END_SECTION

START_SECTION((QualityType getOverallQuality() const))
	Feature p;
	TEST_REAL_SIMILAR(p.getOverallQuality(), 0.0)
	p.setOverallQuality((QualityType)123.456);
	TEST_REAL_SIMILAR(p.getOverallQuality(), 123.456)
	p.setOverallQuality((QualityType)-0.12345);
	TEST_REAL_SIMILAR(p.getOverallQuality(), -0.12345)
	p.setOverallQuality( (QualityType)0.0);
	TEST_REAL_SIMILAR(p.getOverallQuality(), 0.0)
END_SECTION

START_SECTION((void setOverallQuality(QualityType q)))
	Feature p;
	p.setOverallQuality((QualityType)123.456);
	TEST_REAL_SIMILAR(p.getOverallQuality(), 123.456)
	p.setOverallQuality( (QualityType)-0.12345);
	TEST_REAL_SIMILAR(p.getOverallQuality(), -0.12345)
	p.setOverallQuality( (QualityType)0.0);
	TEST_REAL_SIMILAR(p.getOverallQuality(), 0.0)
END_SECTION

START_SECTION((QualityType getQuality(Size index) const ))
	Feature p;
	TEST_REAL_SIMILAR(p.getQuality(0), 0.0)
	p.setQuality( 0, (QualityType)123.456);
	TEST_REAL_SIMILAR(p.getQuality(0), 123.456)
	p.setQuality( 0, (QualityType)-0.12345);
	TEST_REAL_SIMILAR(p.getQuality(0), -0.12345)
	p.setQuality( 0, (QualityType)0.0);
	TEST_REAL_SIMILAR(p.getQuality(0), 0.0)
	TEST_REAL_SIMILAR(p.getQuality(1), 0.0)
  TEST_PRECONDITION_VIOLATED(p.getQuality(10))
END_SECTION

START_SECTION((void setQuality(Size index, QualityType q)))
	Feature p;
	p.setQuality( 1, (QualityType)123.456);
	TEST_REAL_SIMILAR(p.getQuality(1), 123.456)
	p.setQuality( 1, (QualityType)-0.12345);
	TEST_REAL_SIMILAR(p.getQuality(1), -0.12345)
	p.setQuality( 1, (QualityType)0.0);
	TEST_REAL_SIMILAR(p.getQuality(0), 0.0)
	TEST_REAL_SIMILAR(p.getQuality(1), 0.0)
  TEST_PRECONDITION_VIOLATED(p.setQuality( 10, (QualityType)1.0))
END_SECTION

//do not change these datastructures, they are used in the following tests...
std::vector< ConvexHull2D > hulls(2);
hulls[0].addPoint(DPosition<2>(1.0,2.0));
hulls[0].addPoint(DPosition<2>(3.0,4.0));
hulls[1].addPoint(DPosition<2>(0.5,0.0));
hulls[1].addPoint(DPosition<2>(1.0,1.0));

START_SECTION((const vector<ConvexHull2D>& getConvexHulls() const))
	Feature tmp;
	TEST_EQUAL(tmp.getConvexHulls().size(),0)
END_SECTION

START_SECTION((vector<ConvexHull2D>& getConvexHulls()))
	Feature tmp;
	tmp.setConvexHulls(hulls);
	TEST_EQUAL(tmp.getConvexHulls().size(),2)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[0].getHullPoints()[0][0],1.0)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[0].getHullPoints()[0][1],2.0)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[0].getHullPoints()[1][0],3.0)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[0].getHullPoints()[1][1],4.0)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[1].getHullPoints()[0][0],0.5)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[1].getHullPoints()[0][1],0.0)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[1].getHullPoints()[1][0],1.0)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[1].getHullPoints()[1][1],1.0)
END_SECTION

START_SECTION((void setConvexHulls(const vector<ConvexHull2D>& hulls)))
	Feature tmp;
	tmp.setConvexHulls(hulls);
	TEST_EQUAL(tmp.getConvexHulls().size(),2)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[0].getHullPoints()[0][0],1.0)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[0].getHullPoints()[0][1],2.0)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[0].getHullPoints()[1][0],3.0)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[0].getHullPoints()[1][1],4.0)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[1].getHullPoints()[0][0],0.5)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[1].getHullPoints()[0][1],0.0)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[1].getHullPoints()[1][0],1.0)
	TEST_REAL_SIMILAR(tmp.getConvexHulls()[1].getHullPoints()[1][1],1.0)
END_SECTION

START_SECTION((ConvexHull2D& getConvexHull() const))
	Feature tmp;
	tmp.setConvexHulls(hulls);

	//check if the bounding box is ok
	DBoundingBox<2> bb = tmp.getConvexHull().getBoundingBox();
	TEST_REAL_SIMILAR(bb.minPosition()[0],0.5)
	TEST_REAL_SIMILAR(bb.minPosition()[1],0.0)
	TEST_REAL_SIMILAR(bb.maxPosition()[0],3.0)
	TEST_REAL_SIMILAR(bb.maxPosition()[1],4.0)

	//check the convex hull points
	TEST_EQUAL(tmp.getConvexHull().getHullPoints().size(),4)
	TEST_REAL_SIMILAR(tmp.getConvexHull().getHullPoints()[0][0],0.5)
	TEST_REAL_SIMILAR(tmp.getConvexHull().getHullPoints()[0][1],0.0)
	TEST_REAL_SIMILAR(tmp.getConvexHull().getHullPoints()[1][0],3.0)
	TEST_REAL_SIMILAR(tmp.getConvexHull().getHullPoints()[1][1],0.0)
	TEST_REAL_SIMILAR(tmp.getConvexHull().getHullPoints()[2][0],3.0)
	TEST_REAL_SIMILAR(tmp.getConvexHull().getHullPoints()[2][1],4.0)
	TEST_REAL_SIMILAR(tmp.getConvexHull().getHullPoints()[3][0],0.5)
	TEST_REAL_SIMILAR(tmp.getConvexHull().getHullPoints()[3][1],4.0)
END_SECTION

hulls[0].addPoint(DPosition<2>(3.0,2.0));
hulls[1].addPoint(DPosition<2>(2.0,1.0));

START_SECTION((bool encloses(double rt, double mz) const))
	Feature tmp;
	TEST_EQUAL(tmp.getConvexHull().getBoundingBox().isEmpty(), true)
	tmp.setConvexHulls(hulls);

	TEST_EQUAL(tmp.encloses(0.0,0.0), false);
	TEST_EQUAL(tmp.encloses(1.0,1.0), true);
	TEST_EQUAL(tmp.encloses(2.0,0.5), false);
	TEST_EQUAL(tmp.encloses(2.0,3.001), false);
	TEST_EQUAL(tmp.encloses(2.0,2.999), true);
	TEST_EQUAL(tmp.encloses(2.0,3.5), false);
	TEST_EQUAL(tmp.encloses(4.0,3.0), false);
	TEST_EQUAL(tmp.encloses(1.5,1.5), false);
	TEST_EQUAL(tmp.encloses(2.0,1.0), true);
	TEST_EQUAL(tmp.encloses(0.5,0.0), true);
	TEST_EQUAL(tmp.encloses(3.0,3.2), true);
END_SECTION

START_SECTION((Feature(const Feature &feature)))
	Feature::PositionType pos;
	pos[0] = 21.21;
	pos[1] = 22.22;
	Feature p;
	p.setIntensity(123.456f);
	p.setPosition(pos);
	p.setMetaValue("cluster_id",4711);
  p.setOverallQuality( (QualityType)0.9);
  p.setQuality( 0, (QualityType)0.1);
  p.setQuality( 1, (QualityType)0.2);
  p.setConvexHulls(hulls);
	p.getConvexHull(); //this pre-calculates the overall convex hull

	Feature::PositionType pos2;
	Feature::IntensityType i2;

	Feature copy_of_p(p);
	i2 = copy_of_p.getIntensity();
	pos2 = copy_of_p.getPosition();

	TEST_REAL_SIMILAR(i2, 123.456)

	TEST_REAL_SIMILAR(pos2[0], 21.21)
	TEST_REAL_SIMILAR(pos2[1], 22.22)

	TEST_EQUAL(p.getMetaValue("cluster_id"),DataValue(4711));

  Feature::QualityType q2;
	q2 = copy_of_p.getOverallQuality();
	TEST_REAL_SIMILAR(q2, 0.9)
	q2 = copy_of_p.getQuality(0);
	TEST_REAL_SIMILAR(q2, 0.1)
	q2 = copy_of_p.getQuality(1);
	TEST_REAL_SIMILAR(q2, 0.2)
	TEST_EQUAL(copy_of_p.getConvexHull().getHullPoints().size(),p.getConvexHull().getHullPoints().size())
	TEST_EQUAL(copy_of_p.getConvexHulls().size(),p.getConvexHulls().size())
END_SECTION

START_SECTION((Feature& operator = (const Feature& rhs)))
	Feature::PositionType pos;
	pos[0] = 21.21;
	pos[1] = 22.22;
	Feature p;
	p.setIntensity(123.456f);
	p.setPosition(pos);
  p.setOverallQuality( (QualityType)0.9);
  p.setQuality( 0, (QualityType)0.1);
  p.setQuality( 1, (QualityType)0.2);
	p.setMetaValue("cluster_id",4712);
  p.setConvexHulls(hulls);

	Feature::PositionType pos2;
	Feature::IntensityType i2;

	Feature copy_of_p;
	copy_of_p.getConvexHull(); //this pre-calculates the overall convex hull in order to check that the recalculation flag is copied correctly
	copy_of_p = p;

	i2 = copy_of_p.getIntensity();
	pos2 = copy_of_p.getPosition();

  Feature::QualityType q2;

	TEST_REAL_SIMILAR(i2, 123.456)
	TEST_REAL_SIMILAR(pos2[0], 21.21)
	TEST_REAL_SIMILAR(pos2[1], 22.22)
	q2 = copy_of_p.getOverallQuality();
	TEST_REAL_SIMILAR(q2, 0.9)
	q2 = copy_of_p.getQuality(0);
	TEST_REAL_SIMILAR(q2, 0.1)
	q2 = copy_of_p.getQuality(1);
	TEST_REAL_SIMILAR(q2, 0.2)
	TEST_EQUAL(copy_of_p.getConvexHull().getHullPoints().size(),p.getConvexHull().getHullPoints().size())
	TEST_EQUAL(copy_of_p.getConvexHulls().size(),p.getConvexHulls().size())
END_SECTION

START_SECTION((bool operator==(const Feature &rhs) const))
	Feature p1;
	Feature p2(p1);
	TEST_EQUAL(p1==p2, true)

	p1.setIntensity(5.0f);
  p1.setOverallQuality( (QualityType)0.9);
  p1.setQuality(0, (QualityType)0.1);
	TEST_EQUAL(p1==p2, false)
	p2.setIntensity(5.0f);
  p2.setOverallQuality( (QualityType)0.9);
  p2.setQuality(0, (QualityType)0.1);
	TEST_EQUAL(p1==p2, true)

	p1.getPosition()[0]=5;
	TEST_EQUAL(p1==p2, false)
	p2.getPosition()[0]=5;
	TEST_EQUAL(p1==p2, true)
END_SECTION

START_SECTION([EXTRA](Feature& operator != (const Feature& rhs)))
	Feature p1;
	Feature p2(p1);
	TEST_EQUAL(p1!=p2, false)

	p1.setIntensity(5.0f);
	TEST_EQUAL(p1!=p2, true)
	p2.setIntensity(5.0f);
	TEST_EQUAL(p1!=p2, false)

	p1.getPosition()[0]=5;
	TEST_EQUAL(p1!=p2, true)
	p2.getPosition()[0]=5;
	TEST_EQUAL(p1!=p2, false)
END_SECTION

START_SECTION(([EXTRA]meta info with copy constructor))
	Feature p;
	p.setMetaValue(2,String("bla"));
 	Feature p2(p);
	TEST_EQUAL(p.getMetaValue(2), "bla")
	TEST_EQUAL(p2.getMetaValue(2), "bla")
 	p.setMetaValue(2,String("bluff"));
	TEST_EQUAL(p.getMetaValue(2), "bluff")
	TEST_EQUAL(p2.getMetaValue(2), "bla")
END_SECTION

START_SECTION(([EXTRA]meta info with assignment))
	Feature p;
	p.setMetaValue(2,String("bla"));
 	Feature p2 = p;
	TEST_EQUAL(p.getMetaValue(2), "bla")
	TEST_EQUAL(p2.getMetaValue(2), "bla")
 	p.setMetaValue(2,String("bluff"));
	TEST_EQUAL(p.getMetaValue(2), "bluff")
	TEST_EQUAL(p2.getMetaValue(2), "bla")
END_SECTION

	
START_SECTION((std::vector<Feature>& getSubordinates()))
{
	// see below
	NOT_TESTABLE;
}
END_SECTION

START_SECTION((void setSubordinates(const std::vector<Feature>& rhs)))
{
	// see below
	NOT_TESTABLE;
}
END_SECTION

START_SECTION((const std::vector<Feature>& getSubordinates() const))
{
	Feature f1;
	f1.setRT(1001);
	f1.setMZ(1002);
	f1.setCharge(1003);
	Feature f1_cpy(f1);
	Feature f11;
	f11.setRT(1101);
	f11.setMZ(1102);
	Feature f12;
	f12.setRT(1201);
	f12.setMZ(1202);
	Feature f13;
	f13.setRT(1301);
	f13.setMZ(1302);
	TEST_EQUAL(f1.getSubordinates().empty(),true);
	f1.getSubordinates().push_back(f11);
	TEST_EQUAL(f1.getSubordinates().size(),1);
	f1.getSubordinates().push_back(f12);
	TEST_EQUAL(f1.getSubordinates().size(),2);
	f1.getSubordinates().push_back(f13);
	TEST_EQUAL(f1.getSubordinates().size(),3);
	TEST_EQUAL(f1.getRT(),1001);
	TEST_EQUAL(f1.getSubordinates()[0].getRT(),1101);
	TEST_EQUAL(f1.getSubordinates()[1].getRT(),1201);
	TEST_EQUAL(f1.getSubordinates()[2].getRT(),1301);
	const Feature &f1_cref = f1;
	TEST_EQUAL(f1_cref.getMZ(),1002);
	TEST_EQUAL(f1_cref.getSubordinates()[0].getMZ(),1102);
	TEST_EQUAL(f1_cref.getSubordinates()[1].getMZ(),1202);
	TEST_EQUAL(f1_cref.getSubordinates()[2].getMZ(),1302);
	TEST_NOT_EQUAL(f1_cref,f1_cpy);
	Feature f1_cpy2(f1);
	TEST_EQUAL(f1_cpy2,f1);
	f1.getSubordinates().clear();
	TEST_EQUAL(f1_cref,f1_cpy);

	Feature f2;
	f2.setRT(1001);
	f2.setMZ(1002);
	f2.setCharge(1003);
	TEST_NOT_EQUAL(f1_cpy2.getSubordinates().empty(),true);
	f2.setSubordinates(f1_cpy2.getSubordinates());
	TEST_EQUAL(f2,f1_cpy2);
}
END_SECTION

START_SECTION((template < typename Type > Size applyMemberFunction( Size (Type::*member_function)() )))
{
	Feature f;
	TEST_EQUAL(f.applyMemberFunction(&UniqueIdInterface::hasInvalidUniqueId), 1);
	
	f.setUniqueId();
	TEST_EQUAL(f.applyMemberFunction(&UniqueIdInterface::hasInvalidUniqueId), 0);
}
END_SECTION

START_SECTION((template < typename Type > Size applyMemberFunction( Size (Type::*member_function)() const) const))
{
	Feature f;
	TEST_EQUAL(f.applyMemberFunction(&UniqueIdInterface::hasInvalidUniqueId), 1);
	
	f.setUniqueId();
	TEST_EQUAL(f.applyMemberFunction(&UniqueIdInterface::hasInvalidUniqueId), 0);
}
END_SECTION


/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
END_TEST
