// Copyright (c) 1995-1999 Matra Datavision
// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#ifndef _PrsDim_DiameterDimension_HeaderFile
#define _PrsDim_DiameterDimension_HeaderFile

#include <PrsDim_Dimension.hxx>
#include <gp_Pnt.hxx>
#include <gp_Circ.hxx>
#include <Standard.hxx>
#include <Standard_Macro.hxx>
#include <Standard_Type.hxx>

//! Diameter dimension. Can be constructed:
//! - On generic circle.
//! - On generic circle with user-defined anchor point on that circle
//!   (dimension plane is oriented to follow the anchor point).
//! - On generic circle in the specified plane.
//! - On generic shape containing geometry that can be measured
//!   by diameter dimension: circle wire, circular face, etc.
//! The anchor point is the location of the left attachment point of
//! dimension on the circle.
//! The anchor point computation is processed after dimension plane setting
//! so that positive flyout direction stands with normal of the circle and
//! the normal of the plane.
//! If the plane is user-defined the anchor point was computed as intersection
//! of the plane and the basis circle. Among two intersection points
//! the one is selected so that positive flyout direction vector and
//! the circle normal on the one side form the circle plane.
//! (corner between positive flyout directio nand the circle normal is acute.)
//! If the plane is computed automatically (by default it is the circle plane),
//! the anchor point is the zero parameter point of the circle.
//!
//! The dimension is considered as invalid if the user-defined plane
//! does not include th enachor point and th ecircle center,
//! if the diameter of the circle is less than Precision::Confusion().
//! In case if the dimension is built on the arbitrary shape, it can be considered
//! as invalid if the shape does not contain circle geometry.
class PrsDim_DiameterDimension : public PrsDim_Dimension
{
  DEFINE_STANDARD_RTTIEXT(PrsDim_DiameterDimension, PrsDim_Dimension)
public:
  //! Construct diameter dimension for the circle.
  //! @param[in] theCircle  the circle to measure.
  Standard_EXPORT PrsDim_DiameterDimension(const gp_Circ& theCircle);

  //! Construct diameter dimension for the circle and orient it correspondingly
  //! to the passed plane.
  //! @param[in] theCircle  the circle to measure.
  //! @param[in] thePlane  the plane defining preferred orientation
  //!        for dimension.
  Standard_EXPORT PrsDim_DiameterDimension(const gp_Circ& theCircle, const gp_Pln& thePlane);

  //! Construct diameter on the passed shape, if applicable.
  //! @param[in] theShape  the shape to measure.
  Standard_EXPORT PrsDim_DiameterDimension(const TopoDS_Shape& theShape);

  //! Construct diameter on the passed shape, if applicable - and
  //! define the preferred plane to orient the dimension.
  //! @param[in] theShape  the shape to measure.
  //! @param[in] thePlane  the plane defining preferred orientation
  //!        for dimension.
  Standard_EXPORT PrsDim_DiameterDimension(const TopoDS_Shape& theShape, const gp_Pln& thePlane);

public:
  //! @return measured geometry circle.
  const gp_Circ& Circle() const { return myCircle; }

  //! @return anchor point on circle for diameter dimension.
  Standard_EXPORT gp_Pnt AnchorPoint();

  //! @return the measured shape.
  const TopoDS_Shape& Shape() const { return myShape; }

public:
  //! Measure diameter of the circle.
  //! The actual dimension plane is used for determining anchor points
  //! on the circle to attach the dimension lines to.
  //! The dimension will become invalid if the diameter of the circle
  //! is less than Precision::Confusion().
  //! @param[in] theCircle  the circle to measure.
  Standard_EXPORT void SetMeasuredGeometry(const gp_Circ& theCircle);

  //! Measure diameter on the passed shape, if applicable.
  //! The dimension will become invalid if the passed shape is not
  //! measurable or if measured diameter value is less than Precision::Confusion().
  //! @param[in] theShape  the shape to measure.
  Standard_EXPORT void SetMeasuredGeometry(const TopoDS_Shape& theShape);

  //! @return the display units string.
  Standard_EXPORT const TCollection_AsciiString& GetDisplayUnits() const override;

  //! @return the model units string.
  Standard_EXPORT const TCollection_AsciiString& GetModelUnits() const override;

  Standard_EXPORT void SetDisplayUnits(const TCollection_AsciiString& theUnits) override;

  Standard_EXPORT void SetModelUnits(const TCollection_AsciiString& theUnits) override;

  Standard_EXPORT void SetTextPosition(const gp_Pnt& theTextPos) override;

  Standard_EXPORT gp_Pnt GetTextPosition() const override;

protected:
  //! Override this method to change logic of anchor point computation.
  //! Computes anchor point. Its computation is based on the current
  //! dimension plane. Therefore, anchor point is an intersection of plane
  //! and circle.
  //! ATTENTION!
  //! 1) The plane should be set or computed before.
  //! 2) The plane should inclide th ecircle center to be valid.
  Standard_EXPORT virtual void ComputeAnchorPoint();

  Standard_EXPORT virtual void ComputePlane();

  //! Checks if the center of the circle is on the plane.
  Standard_EXPORT bool CheckPlane(const gp_Pln& thePlane) const override;

  Standard_EXPORT double ComputeValue() const override;

  Standard_EXPORT void Compute(const occ::handle<PrsMgr_PresentationManager>& thePrsMgr,
                               const occ::handle<Prs3d_Presentation>&         thePresentation,
                               const int                                      theMode) override;

  Standard_EXPORT void ComputeFlyoutSelection(
    const occ::handle<SelectMgr_Selection>&   theSelection,
    const occ::handle<SelectMgr_EntityOwner>& theEntityOwner) override;

protected:
  //! Compute points on the circle sides for the dimension plane.
  //! Program error exception is raised if the dimension plane "x" direction
  //! is orthogonal to plane (the "impossible" case). The passed dimension plane
  //! is the one specially computed to locate dimension presentation in circle.
  //! @param[in] theCircle  the circle.
  //! @param[out] theFirstPnt  the first point.
  //! @param[out] theSecondPnt  the second point.
  Standard_EXPORT void ComputeSidePoints(const gp_Circ& theCircle,
                                         gp_Pnt&        theFirstPnt,
                                         gp_Pnt&        theSecondPnt);

  Standard_EXPORT bool IsValidCircle(const gp_Circ& theCircle) const;

  Standard_EXPORT bool IsValidAnchor(const gp_Circ& theCircle, const gp_Pnt& thePnt) const;

private:
  gp_Circ      myCircle;
  gp_Pnt       myAnchorPoint;
  TopoDS_Shape myShape;
};

#endif // _PrsDim_DiameterDimension_HeaderFile
