/*--------------------------------------------------------------------------------------+
|
|     $Source: sdk/example/RebarSDKPierVase.cpp $
|
|  $Copyright: (c) 2020 Bentley Systems, Incorporated. All rights reserved. $
|
+--------------------------------------------------------------------------------------*/
#include "_ustation.h"
#include "RebarSDKExample.h"
#include "BeXMLNode.h"
#include "SelectionRebar.h"
#include "rebarSDK_PierVase.h"


bool RebarSDKPierVase::Setup(CPoint3D& origin, CPoint3D& X, const RebarSDKPierVaseData &data)
{
	CVector3D xVec(origin, X);
	CVector3D yVec = CVector3D::kZaxis.CrossProduct(xVec);

	m_Placement = CMatrix3D::Ucs(origin, xVec, yVec, false);

    (RebarSDKPierVaseData &)*this = data;

	m_bSetup = true;
	return true;
}

static BentleyStatus elementFromDgnExtrusion(EditElementHandleR eeh, ISolidPrimitiveCR solid, DgnModelRefR modelRef)
{
	DgnExtrusionDetail  detail;

	if (!solid.TryGetDgnExtrusionDetail(detail))
		return ERROR;

	EditElementHandle   profileElement;
	ElementHandle       profileTemplate;

	if (SUCCESS != DraftingElementSchema::ToElement(profileElement, *detail.m_baseCurve, profileTemplate.IsValid() ? &profileTemplate : NULL, true, modelRef))
		return ERROR;

	bool        capped = solid.GetCapped();
	DVec3d      extrusion = detail.m_extrusionVector;
	DPoint3d    origin;

	detail.m_baseCurve->GetStartPoint(origin); // humbug. The origin arg for CreateProjectionElement is unused, but make it accurate.

	return SurfaceOrSolidHandler::CreateProjectionElement(eeh, nullptr, profileElement, origin, extrusion, NULL, capped, modelRef);
}

ISolidPrimitivePtr RebarSDKPierVase::CreatePierVaseGraphics(EditElementHandleR tagEeh, DgnModelRefR modelRef)
{
    if (!m_bSetup)
        return false;

    CPoint3D p1(-GetCapWidth() / 2, 0, 0);
    CPoint3D p2(GetCapWidth() / 2, 0, 0);
    CPoint3D p3(-GetCapWidth() / 2, 0, -GetCapHeight());
    CPoint3D p4(GetCapWidth() / 2, 0, -GetCapHeight());
    CPoint3D p5(-GetFootWidth() / 2, 0, GetFootHeight() - GetHeight());
    CPoint3D p6(GetFootWidth() / 2, 0, GetFootHeight() - GetHeight());
    CPoint3D p7(-GetFootWidth() / 2, 0, -GetHeight());
    CPoint3D p8(GetFootWidth() / 2, 0, -GetHeight());

    CMatrix3D transform = m_Placement;

    p1 = p1.Transform(transform);
    p2 = p2.Transform(transform);
    p3 = p3.Transform(transform);
    p4 = p4.Transform(transform);
    p5 = p5.Transform(transform);
    p6 = p6.Transform(transform);
    p7 = p7.Transform(transform);
    p8 = p8.Transform(transform);

    CurveVectorPtr  cv1 = CurveVector::Create(CurveVector::BOUNDARY_TYPE_Outer);

    //cap line
    DSegment3d          segment;
    segment.Init(p1, p2);
    cv1->push_back(ICurvePrimitive::CreateLine(segment));

    //cap height line
    segment.Init(p2, p4);
    cv1->push_back(ICurvePrimitive::CreateLine(segment));

    //right arc
    DEllipse3d      ellipse;
    CVector3D tagent1(p8, p6);
    DVec3d t;
    t.InitFromArray((double*)&tagent1);
    ellipse.InitArcFromPointTangentPoint(p6, t, p4);
    ellipse = DEllipse3d::FromReversed(ellipse);
    cv1->push_back(ICurvePrimitive::CreateArc(ellipse));

    segment.Init(p6, p8);
    cv1->push_back(ICurvePrimitive::CreateLine(segment));

    segment.Init(p8, p7);
    cv1->push_back(ICurvePrimitive::CreateLine(segment));

    segment.Init(p7, p5);
    cv1->push_back(ICurvePrimitive::CreateLine(segment));

    //left arc
    CVector3D tagent2(p7, p5);
    t.InitFromArray((double*)&tagent2);
    ellipse.InitArcFromPointTangentPoint(p5, t, p3);
    cv1->push_back(ICurvePrimitive::CreateArc(ellipse));

    segment.Init(p3, p1);
    cv1->push_back(ICurvePrimitive::CreateLine(segment));

    CPoint3D origin;
    origin.Transform(transform);
    CPoint3D yPt(0.0, GetDepth(), 0.0);
    yPt.Transform(transform);

    DVec3d yVec = DVec3d::FromStartEnd(origin, yPt);

    DgnExtrusionDetail extrusionData(cv1, yVec, true);

    ISolidPrimitivePtr extrusion = ISolidPrimitive::CreateDgnExtrusion(extrusionData);

    return extrusion;
}

bool RebarSDKPierVase::CreatePierVaseSolid(EditElementHandleR tagEeh, DgnModelRefR modelRef)
{
    ISolidPrimitivePtr extrusion = CreatePierVaseGraphics(tagEeh, modelRef);
	elementFromDgnExtrusion(tagEeh, *extrusion, modelRef);
	
	return true;
}

int RebarSDKPierVase::MakeStirrup(RebarCurve &rebar, double z, double width, double depth, double cover, double diameter, double bendRadius, double bendLen, const RebarEndTypes &endTypes) const
{
    CPoint3D wid(width, depth, 0.0);
    CPoint3D off(diameter / 2.0, diameter / 2.0, 0.0);

    rebar.CalculateRectangular(wid, off, bendRadius);

    rebar.EvaluateEndTypes(endTypes, bendRadius, bendLen + bendRadius);

    CMatrix3D mat;
    CVector3D vec(0.0, cover, -z);
    mat.SetTranslation(vec);
    rebar.DoMatrix(mat);
    rebar.DoMatrix(m_Placement);

    return SUCCESS;
}

RebarSetTag *RebarSDKPierVase::MakeStirrups(DgnModelRefP modelRef)
{
    //NewRebarAssembly(modelRef);

    RebarSetP rebar_set = RebarSet::Fetch(m_stirrup_set_id, modelRef);
    RebarSetTag *rebar_set_tag = nullptr;

    if (rebar_set != nullptr)
    {
        rebar_set->SetRebarDisplayMode(RebarDisplayMode::kRebarCylinderMode);
        rebar_set->SetCallerId(GetElementId());     

        rebar_set->StartUpdate(modelRef);

        double uor_per_mm = modelRef->GetModelInfoCP()->GetUorPerMeter() / 1000.0;

        double cover = GetCover();
        double spacing = GetShearSpacing();
        BrString sizeKey = GetShearBarSize();

        double diameter = RebarCode::GetBarDiameter(sizeKey, modelRef);
        double bendRadius = RebarCode::GetPinRadius(sizeKey, modelRef, true);
        RebarEndType endType;
        switch (GetTieType())
        {
        case kBend:
            endType.SetType(RebarEndType::kBend);
            break;
        case kCog:
            endType.SetType(RebarEndType::kCog);
            break;
        case kHook:
            endType.SetType(RebarEndType::kHook);
            break;
        default:
            break;
        }
        double bendLen = RebarCode::GetBendLength(sizeKey, endType, modelRef);

        double h = GetHeight() - 2.0 * cover - diameter;

        RebarShapeData shape;
        shape.SetSizeKey((LPCTSTR)sizeKey);
        shape.SetIsStirrup(true);

        int num_stirrups = (int)floor(h / spacing + 0.5) + 1;

        RebarSetData setdata;
        setdata.SetNumber(num_stirrups);
        setdata.SetNominalSpacing(spacing / uor_per_mm);
        if (num_stirrups > 1)
            spacing = h / (num_stirrups - 1);
        setdata.SetAverageSpacing(spacing / uor_per_mm);

        double stirrup_z = cover + diameter / 2.0;
        RebarSymbology symb;
        symb.SetRebarColor(-1);
        symb.SetRebarLevel(L"PC_REBAR");

        RebarEndTypes endTypes = { endType, endType };

        for (int i = 0; i < num_stirrups; i++)
        {
            RebarCurve rebar;
            double width, depth;
            GetRectFromHeight(width, depth, -stirrup_z, cover);
            
            MakeStirrup(rebar, stirrup_z, width, depth, cover, diameter, bendRadius, bendLen, endTypes);
			
			RebarElementP rebar_elem = rebar_set->AssignRebarElement(i, num_stirrups, symb, modelRef);
            if (rebar_elem != nullptr)
            {
                shape.SetLength(rebar.GetLength() / uor_per_mm);
                rebar_elem->Update(rebar, diameter, endTypes, shape, modelRef, false);
                //AddRebarElement(*rebar_elem, modelRef);
            }
            
            stirrup_z += spacing;
        }
        //int barSetTag = 0;          // this is an identifier of this RebarSet in ProConcrete RebarBeam object
                                    // for every different RebarSet give a unique identifier.
                                    // if you give the same identifier to another set, it will replace the existing one
        //AddRebarSet(*rebar_set, barSetTag, true, modelRef);        // add the RebarSet to the RebarAssembly after all its RebarElements have been assigned. Better performance

        rebar_set->FinishUpdate(setdata, modelRef);

        rebar_set_tag = new RebarSetTag;
        rebar_set_tag->SetRset(rebar_set);
        rebar_set_tag->SetIsStirrup(true);
        rebar_set_tag->SetBarSetTag(0);
    }

    return rebar_set_tag;
}

bool RebarSDKPierVase::GetRectFromHeight(double& width, double& depth, double height, double cover)
{
    depth = GetDepth() - cover * 2;;

	double capWidth = GetCapWidth() - cover * 2;
	double capHeight = GetCapHeight();
	double footHeight = GetFootHeight();
	double footWidth = GetFootWidth() - cover * 2;

	//line 1
	DPoint3d startL1 = DPoint3d::From(capWidth / 2, GetHeight(), 0);
	DPoint3d endL1 = DPoint3d::From(capWidth / 2, GetFootHeight(), 0);

	//arc
	DPoint3d startArc = DPoint3d::From(GetFootWidth() / 2, GetFootHeight(), 0);
	DPoint3d endArc = DPoint3d::From(GetCapWidth() / 2, GetHeight() - GetCapHeight(), 0);
	DEllipse3d offsetArc;
	offsetArc.InitArcFromPointTangentPoint(startArc, DVec3d::From(0.0, 1.0, 0.0), endArc);
	offsetArc = DEllipse3d::FromScaledVectors(offsetArc, cover / offsetArc.vector0.Magnitude() + 1);

	DPoint3d localCenter[2];
	double   lineFraction[2];
	double   ellipseAngle[2];
	DPoint3d cosineSineZ[2];

	int numIntersection = offsetArc.IntersectXYLine(localCenter, lineFraction, cosineSineZ, ellipseAngle,
		startL1, endL1);
	capHeight = lineFraction[0] * (GetHeight() - GetFootHeight());

    // < 0 && >= -GetCapHeight()
    if (height < 0 && height >= -capHeight)
    {
        width = capWidth;

        return true;
    }
    // < -GetCapHeight() && > GetFootHeight() - GetHeight()
    else if (height < -capHeight && height > footHeight - GetHeight())
    {
        double yHeight = GetHeight() - footHeight - capHeight;
		double pHeight = GetHeight() - footHeight + height;

        //line
        DPoint3d start = DPoint3d::From(0.0, pHeight, 0);
        DPoint3d end = DPoint3d::From(capWidth / 2, pHeight, 0);

        //arc
        DPoint3d startArc = DPoint3d::From(footWidth / 2, 0, 0);
        DPoint3d endArc = DPoint3d::From(capWidth / 2, yHeight, 0);


        DEllipse3d arc;
        arc.InitArcFromPointTangentPoint(startArc, DVec3d::From(0.0, 1.0, 0.0), endArc);

        int numIntersection = arc.IntersectXYLine(localCenter, lineFraction, cosineSineZ, ellipseAngle,
            start, end);

        width = lineFraction[0] * capWidth ;

        return true;
    }
    // <= GetFootHeight() - GetHeight() && > -GetHeight()
    else if (height <= footHeight - GetHeight() && height > -GetHeight())
    {
        width = footWidth;

        return true;
    }

    return false;
}

int RebarSDKPierVase::MakeRebars (DgnModelRefP modelRef)
    {
    NewRebarAssembly(modelRef);     // you need to do this once at the start of creating/modifying the RebarAssembly
    RebarSetTagArray rset_tags;

    RebarSetTag *rebar_set_tag = MakeStirrups(modelRef);
    if (rebar_set_tag != nullptr)
        rset_tags.Add(rebar_set_tag);

    rebar_set_tag = MakeVerticalXDirBars (m_front_set_id, true, modelRef);
    if (rebar_set_tag != nullptr)
        rset_tags.Add(rebar_set_tag);

    rebar_set_tag = MakeVerticalXDirBars (m_back_set_id, false, modelRef);
    if (rebar_set_tag != nullptr)
        rset_tags.Add(rebar_set_tag);

    rebar_set_tag = MakeVerticalYDirBars (m_right_set_id, true, modelRef);
    if (rebar_set_tag != nullptr)
        rset_tags.Add(rebar_set_tag);

    rebar_set_tag = MakeVerticalYDirBars (m_left_set_id, false, modelRef);
    if (rebar_set_tag != nullptr)
        rset_tags.Add(rebar_set_tag);

    AddRebarSets(rset_tags, modelRef);

    return SUCCESS;
    }

RebarSetTag *RebarSDKPierVase::MakeVerticalYDirBars (ElementId& setId, bool isRight, DgnModelRefP modelRef)
    {
    //NewRebarAssembly(modelRef);           // ypu only need this once

    bool const isStirrup = false;
    RebarSetP rebar_set = RebarSet::Fetch(setId, modelRef);
    RebarSetTag *rebar_set_tag = nullptr;

    if (NULL != rebar_set)
        {
        rebar_set->SetRebarDisplayMode(RebarDisplayMode::kRebarCylinderMode);
        rebar_set->SetCallerId(GetCallerId());

        rebar_set->StartUpdate(modelRef);

        double uor_per_mm = modelRef->GetModelInfoCP()->GetUorPerMeter() / 1000.0;
        double spacing = GetLongitudinalSpacing();
        BrString sizeKey = GetLongitudinalBarSize();

        double diameter = RebarCode::GetBarDiameter(sizeKey, modelRef);
        double bendRadius = RebarCode::GetPinRadius(sizeKey, modelRef, false);

        RebarEndType endType;
        switch (GetTieType())
        {
        case kBend:
            endType.SetType(RebarEndType::kBend);
            break;
        case kCog:
            endType.SetType(RebarEndType::kCog);
            break;
        case kHook:
            endType.SetType(RebarEndType::kHook);
            break;
        default:
            break;
        }
        RebarEndTypes endTypes = { endType, endType };
        double bendLen = RebarCode::GetBendLength(sizeKey, endType, modelRef);

        RebarShapeData shape;
        shape.SetSizeKey((LPCTSTR)sizeKey);
        shape.SetIsStirrup(isStirrup);

        double depth = GetDepth() - 2.0 * GetCover() - diameter;
        int num_rebars = (int)floor(depth / spacing + 0.5) + 1;

        RebarSetData setdata;
        setdata.SetNumber(num_rebars);
        setdata.SetNominalSpacing(spacing / uor_per_mm);

        if (num_rebars > 1)
            spacing = depth / (num_rebars - 1);
        setdata.SetAverageSpacing(spacing / uor_per_mm);

        RebarSymbology symb;
        symb.SetRebarColor (-1);
        symb.SetRebarLevel(L"PC_REBAR");

        double yPos = GetCover() + diameter / 2.0;
        for (int i = 0; i < num_rebars; i++)
            {
            RebarCurve rebar;
            MakeVerticalYDirBar (rebar, yPos, diameter, bendRadius, bendLen, endTypes, isRight);
 
            RebarElementP rebar_elem = rebar_set->AssignRebarElement(i, num_rebars, symb, modelRef);
            if (NULL != rebar_elem)
                {
                shape.SetLength(rebar.GetLength() / uor_per_mm);
                rebar_elem->Update(rebar, diameter, endTypes, shape, modelRef, false);
                }

            yPos += spacing;
            }

        int barSetTag = isRight ? 3 : 4;
        //AddRebarSet(*rebar_set, barSetTag, isStirrup, modelRef);

        rebar_set->FinishUpdate(setdata, modelRef);

        rebar_set_tag = new RebarSetTag;
        rebar_set_tag->SetRset(rebar_set);
        rebar_set_tag->SetIsStirrup(isStirrup);
        rebar_set_tag->SetBarSetTag(barSetTag);
    }
    return rebar_set_tag;
    }

bool RebarSDKPierVase::GetVerticalYDirPoints (bvector<BePoint3D>& points, double& vaseRadius, double yPos, double barDiameter, double bendRadius) const
    {
    double      capWidth = GetCapWidth() - 2.0 * GetCover() - barDiameter;
    double      footWidth = GetFootWidth() - 2.0 * GetCover() - barDiameter;
    double      coverOffset = GetCover() + barDiameter / 2.0;

    DSegment3d  capLine2d;
    capLine2d.point[0] = DPoint3d::From (capWidth / 2.0, 0.0, 0.0);
    capLine2d.point[1] = DPoint3d::From (capWidth / 2.0, -GetHeight(), 0.0);

    DSegment3d  footLine2d;
    footLine2d.point[0] = DPoint3d::From (footWidth / 2.0, 0.0, 0.0);
    footLine2d.point[1] = DPoint3d::From (footWidth / 2.0, -GetHeight(), 0.0);

    DEllipse3d  vaseArc2d;
    DPoint3d    arcStartPt2d = DPoint3d::From (GetFootWidth() / 2.0, -(GetHeight() - GetFootHeight()), 0.0);
    DPoint3d    arcEndPt2d   = DPoint3d::From (GetCapWidth() / 2.0, -GetCapHeight(), 0.0);
    vaseArc2d.InitArcFromPointTangentPoint(arcStartPt2d, DVec3d::From(0.0, 1.0, 0.0), arcEndPt2d);
    vaseArc2d = DEllipse3d::FromScaledVectors(vaseArc2d, coverOffset / vaseArc2d.vector0.Magnitude() + 1.0);

    DEllipse3d  offsetVaseArc2d  = DEllipse3d::FromScaledVectors(vaseArc2d, bendRadius / vaseArc2d.vector0.Magnitude() + 1.0);
    DSegment3d  offsetCapLine2d = DSegment3d::From (capLine2d.point[0], capLine2d.point[1]);
    offsetCapLine2d.point[0].x -= bendRadius;
    offsetCapLine2d.point[1].x -= bendRadius;

    DPoint3d    intersectPts2d[2];
    double      lineParams[2];
    DPoint3d    ellipseCoffs2d[2];
    double      ellipseAngle[2];
    int         nIntersections = offsetVaseArc2d.IntersectXYLine (intersectPts2d, lineParams, ellipseCoffs2d, ellipseAngle, offsetCapLine2d.point[0], offsetCapLine2d.point[1]);
    if (nIntersections <= 0)
        {
        assert (false);
        return false;
        }
    DPoint3d    bendArcCenter2d = intersectPts2d[0];

    nIntersections = vaseArc2d.IntersectXYLine (intersectPts2d, lineParams, ellipseCoffs2d, ellipseAngle, bendArcCenter2d, vaseArc2d.center);
    if (nIntersections <= 0)
        {
        assert (false);
        return false;
        }

    DPoint3d    vaseArcPt2d[3];
    vaseArcPt2d[2] = intersectPts2d[0];
    vaseArcPt2d[0] = vaseArc2d.FractionToPoint (0.0);
    vaseArc2d.InitArcFromPointTangentPoint(vaseArcPt2d[0], DVec3d::From(0.0, 1.0, 0.0), vaseArcPt2d[2]);
    vaseArcPt2d[1] = vaseArc2d.FractionToPoint (0.5);

    DPoint3d    bendArcPt2d[3];
    bendArcPt2d[0] = vaseArcPt2d[2];
    bendArcPt2d[2] = DPoint3d::From (capWidth / 2.0, bendArcCenter2d.y, 0.0);

    DEllipse3d  bendArc2d;
    bendArc2d.InitArcFromPointTangentPoint(bendArcPt2d[2], DVec3d::From(0.0, -1.0, 0.0), bendArcPt2d[0]);
    bendArcPt2d[1] = bendArc2d.FractionToPoint (0.5);

    DSegment3d  tanentLine2d;
    DVec3d      tangent2d = DVec3d::FromStartEnd (bendArc2d.center, vaseArc2d.center);
    tangent2d.Normalize ();
    tangent2d.RotateXY (PI / 2.0);
    tanentLine2d.InitFromOriginAndDirection (vaseArcPt2d[2], tangent2d);

    DPoint3d    vaseIpPt2d, bendIpPt2d;
    if (!DSegment3d::IntersectXY (lineParams[0], lineParams[1], vaseIpPt2d, intersectPts2d[0], footLine2d, tanentLine2d) ||
        !DSegment3d::IntersectXY (lineParams[0], lineParams[1], bendIpPt2d, intersectPts2d[0], capLine2d, tanentLine2d))
        {
        assert (false);
        return false;
        }

    CPoint3D    ipPts[4];
    ipPts[0].Reset (footWidth /2.0,     yPos, -(GetHeight() - GetCover() - barDiameter / 2.0));
    ipPts[1].Reset (vaseIpPt2d.x,       yPos, vaseIpPt2d.y);
    ipPts[2].Reset (bendIpPt2d.x,       yPos, bendIpPt2d.y);
    ipPts[3].Reset (capWidth /2.0,      yPos, - (GetCover() + barDiameter / 2.0));

    points.push_back (ipPts[0]);
    points.push_back (ipPts[1]);
    points.push_back (ipPts[2]);
    points.push_back (ipPts[3]);

    vaseRadius = vaseArc2d.vector0.Magnitude();

    return true;
    }

int RebarSDKPierVase::MakeVerticalYDirBar (RebarCurve &rebar, double yPos, double barDiameter, double bendRadius, double bendLen, RebarEndTypes const& endTypes, bool isRight) const
    {
    double              vaseRadius = 0.0;
    bvector<BePoint3D>  ipPts;
    if (!GetVerticalYDirPoints (ipPts, vaseRadius, yPos, barDiameter, bendRadius) || ipPts.size () < 4)
        return ERROR;

    if (!isRight)
        {
        ipPts[0].x  *= -1.0;
        ipPts[1].x  *= -1.0;
        ipPts[2].x  *= -1.0;
        ipPts[3].x  *= -1.0;
        }

    RebarVertexP vex;
    vex = &rebar.PopVertices ().NewElement ();
    vex->SetIP (ipPts[0]);
    vex->SetType (RebarVertex::kStart);

    vex = &rebar.PopVertices ().NewElement ();
    vex->SetIP (ipPts[1]);
    vex->SetRadius (vaseRadius);
    vex->SetType (RebarVertex::kIP);

    vex = &rebar.PopVertices ().NewElement ();
    vex->SetIP (ipPts[2]);
    vex->SetRadius (bendRadius);
    vex->SetType (RebarVertex::kIP);

    vex = &rebar.PopVertices ().NewElement ();
    vex->SetIP (ipPts[3]);
    vex->SetType (RebarVertex::kEnd);

    rebar.PopVertices ()[1]->EvaluateBend (*rebar.PopVertices ()[0], *rebar.PopVertices ()[2]);
    rebar.PopVertices ()[2]->EvaluateBend (*rebar.PopVertices ()[1], *rebar.PopVertices ()[3]);

    CVector3D   endNormal = isRight ? CVector3D (0.0, -1.0, 0.0) : CVector3D (0.0, 1.0, 0.0);
    rebar.EvaluateEndTypes (endTypes, bendRadius, bendLen + bendRadius, endNormal);

    rebar.DoMatrix (m_Placement);
    return SUCCESS;
    }

RebarSetTag *RebarSDKPierVase::MakeVerticalXDirBars(ElementId& setId, bool isFront, DgnModelRefP modelRef)
{
    //NewRebarAssembly(modelRef);

    bool const isStirrup = false;
    RebarSetP rebar_set = RebarSet::Fetch(setId, modelRef);
    RebarSetTag *rebar_set_tag = nullptr;

    if (NULL != rebar_set)
        {
        rebar_set->SetRebarDisplayMode(RebarDisplayMode::kRebarCylinderMode);
        rebar_set->SetCallerId(GetCallerId());

        rebar_set->StartUpdate(modelRef);

        double uor_per_mm = modelRef->GetModelInfoCP()->GetUorPerMeter() / 1000.0;
        double spacing = GetLongitudinalSpacing();
        BrString sizeKey = GetLongitudinalBarSize();

        double diameter = RebarCode::GetBarDiameter(sizeKey, modelRef);
        double bendRadius = RebarCode::GetPinRadius(sizeKey, modelRef, false);

        RebarEndType endType;
        switch (GetTieType())
        {
        case kBend:
            endType.SetType(RebarEndType::kBend);
            break;
        case kCog:
            endType.SetType(RebarEndType::kCog);
            break;
        case kHook:
            endType.SetType(RebarEndType::kHook);
            break;
        default:
            break;
        }
        RebarEndTypes endTypes = { endType, endType };
        double bendLen = RebarCode::GetBendLength(sizeKey, endType, modelRef);

        RebarShapeData shape;
        shape.SetSizeKey((LPCTSTR)sizeKey);
        shape.SetIsStirrup(isStirrup);

        double width = GetCapWidth() - 2.0 * spacing - 2.0 * GetCover() - diameter;
        int num_rebars = (int)floor(width / spacing + 0.5) + 1;

        RebarSetData setdata;
        setdata.SetNumber(num_rebars);
        setdata.SetNominalSpacing(spacing / uor_per_mm);

        if (num_rebars > 1)
            spacing = width / (num_rebars - 1);
        setdata.SetAverageSpacing(spacing / uor_per_mm);

        RebarSymbology symb;
        symb.SetRebarColor (-1);
        symb.SetRebarLevel(L"PC_REBAR");

        double xPos = -width /2.0;
        for (int i = 0; i < num_rebars; i++)
            {
            double height;
            RebarCurve rebar;
            GetHeightFromWidth (height, xPos, diameter);
            MakeVerticalXDirBar (rebar, xPos, height, diameter, bendRadius, bendLen, endTypes, isFront);
 
            RebarElementP rebar_elem = rebar_set->AssignRebarElement(i, num_rebars, symb, modelRef);
            if (NULL != rebar_elem)
                {
                shape.SetLength(rebar.GetLength() / uor_per_mm);
                rebar_elem->Update(rebar, diameter, endTypes, shape, modelRef, false);
                }

            xPos += spacing;
            }
 
        int barSetTag = isFront ? 1 : 2;
        //AddRebarSet(*rebar_set, barSetTag, isStirrup, modelRef);

        rebar_set->FinishUpdate(setdata, modelRef);

        rebar_set_tag = new RebarSetTag;
        rebar_set_tag->SetRset(rebar_set);
        rebar_set_tag->SetIsStirrup(isStirrup);
        rebar_set_tag->SetBarSetTag(barSetTag);
    }
    return rebar_set_tag;
}

int RebarSDKPierVase::MakeVerticalXDirBar (RebarCurve &rebar, double xPos, double height, double diameter, double bendRadius, double bendLen, RebarEndTypes const& endTypes, bool isFront) const
{
    double yBack   =  GetDepth() - (GetCover() + diameter / 2.0);
    double yFront  =  GetCover() + diameter / 2.0;
    double zTop    = -GetCover() - diameter / 2.0;
    double zBottom =  height  + diameter / 2.0;

    CPoint3D    ipPts[2];
    if (isFront)
        {
        ipPts[0].Reset (xPos, yFront, zBottom);
        ipPts[1].Reset (xPos, yFront, zTop);
        }
    else
        {
        ipPts[0].Reset (xPos, yBack, zBottom);
        ipPts[1].Reset (xPos, yBack, zTop);
        }

    RebarVertexP vex;
    vex = &rebar.PopVertices ().NewElement ();
    vex->SetIP (ipPts[0]);
    vex->SetType (RebarVertex::kStart);

    vex = &rebar.PopVertices ().NewElement ();
    vex->SetIP (ipPts[1]);
    vex->SetType (RebarVertex::kEnd);

    CVector3D   endNormal = isFront ? CVector3D (-1.0, 0.0, 0.0) : CVector3D (1.0, 0.0, 0.0);

    rebar.EvaluateBend (bendRadius);
    rebar.EvaluateEndTypes (endTypes, bendRadius, bendLen + bendRadius, endNormal);
    rebar.DoMatrix (m_Placement);

    return SUCCESS;
}

bool RebarSDKPierVase::GetHeightFromWidth (double& height, double width, double diameter) const
    {
    double capWidth     = GetCapWidth() - 2.0 * GetCover() - diameter;
    double footWidth    = GetFootWidth() - 2.0 * GetCover() - diameter;
    double coverOffset  = GetCover() + diameter / 2.0;

    DSegment3d  capLine2d;
    capLine2d.point[0]  = DPoint3d::From (width, 0.0, 0.0);
    capLine2d.point[1]  = DPoint3d::From (width, -GetHeight(), 0.0);

    if (DoubleCompare (width, footWidth / 2.0) < 0 && DoubleCompare (width, -footWidth / 2.0) > 0)
        {
        height = - (GetHeight() - GetCover());
        return true;
        }
    else if (DoubleCompare (width, footWidth / 2.0) > 0 && DoubleCompare (width, capWidth / 2.0) < 0)
        {
        DPoint3d    arcStartPt2d = DPoint3d::From (GetFootWidth() / 2.0, -(GetHeight() - GetFootHeight()), 0.0);
        DPoint3d    arcEndPt2d   = DPoint3d::From (GetCapWidth() / 2.0, -GetCapHeight(), 0.0);

        DEllipse3d  vaseArc2d;
        vaseArc2d.InitArcFromPointTangentPoint(arcStartPt2d, DVec3d::From(0.0, 1.0, 0.0), arcEndPt2d);
        vaseArc2d = DEllipse3d::FromScaledVectors(vaseArc2d, coverOffset / vaseArc2d.vector0.Magnitude() + 1.0);

        DPoint3d    intersectPt2d[2];
        double      lineParams[2];
        DPoint3d    ellipseCoffs2d[2];
        double      ellipseAngle[2];
        int         nIntersections = vaseArc2d.IntersectXYLine (intersectPt2d, lineParams, ellipseCoffs2d, ellipseAngle, capLine2d.point[0], capLine2d.point[1]);
        if (nIntersections > 0)
            {
            height = intersectPt2d[0].y;
            return true;
            }
        }
    else if (DoubleCompare (width, -footWidth / 2.0) < 0 && DoubleCompare (width, -capWidth / 2.0) > 0)
        {
        DPoint3d    arcStartPt2d = DPoint3d::From (-GetFootWidth() / 2.0, -(GetHeight() - GetFootHeight()), 0.0);
        DPoint3d    arcEndPt2d   = DPoint3d::From (-GetCapWidth() / 2.0, -GetCapHeight(), 0.0);

        DEllipse3d  vaseArc2d;
        vaseArc2d.InitArcFromPointTangentPoint(arcStartPt2d, DVec3d::From(0.0, 1.0, 0.0), arcEndPt2d);
        vaseArc2d = DEllipse3d::FromScaledVectors(vaseArc2d, coverOffset / vaseArc2d.vector0.Magnitude() + 1.0);

        DPoint3d    intersectPt2d[2];
        double      lineParams[2];
        DPoint3d    ellipseCoffs2d[2];
        double      ellipseAngle[2];
        int         nIntersections = vaseArc2d.IntersectXYLine (intersectPt2d, lineParams, ellipseCoffs2d, ellipseAngle, capLine2d.point[0], capLine2d.point[1]);
        if (nIntersections > 0)
            {
            height = intersectPt2d[0].y;
            return true;
            }
        }
    assert (false);
    return false;
    }

BentleyStatus RebarSDKPierVase::GetPresentation(EditElementHandleR eeh, ViewContextR viewContext)
{
    BentleyStatus ret = ERROR;
    if (eeh.IsValid())
    {
        DgnModelRefP modelRef = eeh.GetModelRef();
        ISolidPrimitivePtr extrusion = CreatePierVaseGraphics(eeh, *modelRef);
        if (extrusion != nullptr)
        {
            viewContext.GetIDrawGeom().DrawSolidPrimitive(*extrusion);
            ret = SUCCESS;
        }
    }
    return ret;
}

long RebarSDKPierVase::GetStreamMap(BeStreamMap &map, int typeof /* = 0 */, int versionof /* = -1 */)
{
    switch (typeof)
    {
        case 0:
        {
            return RebarExtendedElement::GetStreamMap(map, typeof, versionof);
        }
        case 1:
        {
            return RebarAssembly::GetStreamMap(map, typeof, versionof);
        }
        case 2:
        {
            map << m_stirrup_set_id
                << m_front_set_id
                << m_back_set_id
                << m_left_set_id
                << m_right_set_id
                << m_Placement
                << PopHeight()
                << PopDepth()
                << PopCapWidth()
                << PopCapHeight()
                << PopFootWidth()
                << PopFootHeight()
                << PopCover()
                << PopShearSpacing()
                << PopShearBarSize()
                << PopLongitudinalSpacing()
                << PopLongitudinalBarSize()
                << (char &)PopTieType();
            return 0;
        }
        default:
            break;
    }

    return -1;
}

bool RebarSDKPierVase::OnDoubleClick()
{
    BrString cmd;
    cmd.Format(L"Selected=%I64d", GetElementId());
    RebarSDK_PierVase(cmd);
    return true;
}
