#include "StdAfx.h"

using namespace VWFC::Tools;
using namespace VWFC::Math;

const char* VWTaggedObj::TagOrientationStrings[] = {
    "None",
    "Left",
    "Right",
    "Floating"
};

VWTaggedObj::~VWTaggedObj()
{
}

WorldPt VWTaggedObj::GetTagControlPoint(EPointID controlPointID)
{
    WorldPt point;

    VWParametricObj taggedObject(objectHandle);

    switch(controlPointID) {
        case kTagPoint:
            point.x = taggedObject.GetParamReal(tagPointIdx);
            point.y = taggedObject.GetParamReal(tagPointIdx+1);
            break;
        case kShoulderPoint:
            point.x = taggedObject.GetParamReal(shoulderPointIdx);
            point.y = taggedObject.GetParamReal(shoulderPointIdx+1);
            break;
        case kEndPoint:
            point.x = taggedObject.GetParamReal(endPointIdx);
            point.y = taggedObject.GetParamReal(endPointIdx+1);
            break;
    }
    return(point);
}

void VWTaggedObj::DrawTag(InternalIndex classID)
{
    ASSERTN(kBAtwood, objectHandle != nil);

    ETagOrientation orientation = GetTagOrientation();
    VWParametricObj taggedObject(objectHandle);

    if(orientation != kTagNone) {
        WorldPt shoulderPoint = GetTagControlPoint(kShoulderPoint);
        WorldPt tagPoint = GetTagControlPoint(kTagPoint);
        
        bool isRightOriented = (GetTagOrientation() != kTagLeft);
        if(kTagFloating != orientation) {
            DrawLeaderLine(classID);
        }
        double angle = 0;

        if(useShoulder && kTagFloating != orientation) {
            if(shoulderPoint != tagPoint) {
                angle = isRightOriented ? GetAngle(shoulderPoint, tagPoint) : GetAngle(shoulderPoint, tagPoint)+180;
            } else {
                angle = 0;
            }
        } else {
            // If there is no shoulder, the tag should be drawn horizontally, always.
            // Thus it is necessary to counteract the tagged object's rotation.
            VWTransformMatrix matrix;
            taggedObject.GetObjectMatrix(matrix);
            VWPoint2D pt1 = VWPoint2D(0,0);
            VWPoint2D pt2 = VWPoint2D(1,0);
            pt1 = matrix.PointTransform(pt1);
            pt2 = matrix.PointTransform(pt2);
            angle = -1*(pt2-pt1).CalcAng360();
        }
        DrawTagBody(tagPoint, angle, isRightOriented);
    }
}

void VWTaggedObj::InitTag(  MCObjectHandle taggedObjectHandle,
                            short tagControlPointIdx, 
                            short shoulderControlPointIdx, 
                            short endControlPointIdx,
                            short approachLineAngleIdx,
                            short shoulderLineAngleIdx,
                            short tagOrientationIdx,
                            bool useEndMarker,
                            bool useLeaderLineShoulder)
{
    objectHandle = taggedObjectHandle;
    tagPointIdx = tagControlPointIdx;
    shoulderPointIdx = shoulderControlPointIdx;
    endPointIdx = endControlPointIdx;
    approachAngleIdx = approachLineAngleIdx;
    shoulderAngleIdx = shoulderLineAngleIdx;
    orientationIdx = tagOrientationIdx;
    useMarker = useEndMarker;
    useShoulder = useLeaderLineShoulder;

    ASSERTN(kBAtwood, objectHandle != nil);

    VWParametricObj taggedObject(objectHandle);

    bool duringFileRead;
    GS_GetProgramVariable(gCBP, varIsResettingPlugInsDuringFileRead, &duringFileRead);

    taggedObject.EnableParameter(approachAngleIdx, useLeaderLineShoulder);
    taggedObject.EnableParameter(shoulderAngleIdx, useLeaderLineShoulder);

    if(!duringFileRead) {
        // Rectify the tag to account for changes in the plant data.
        WorldPt tagPoint = GetTagControlPoint(kTagPoint);
        WorldPt shoulderPoint = GetTagControlPoint(kShoulderPoint);
        WorldPt endPoint = GetTagControlPoint(kEndPoint);
        double prevApproachAngle = GetAngle(endPoint, shoulderPoint);
        double prevShoulderAngle = GetAngle(shoulderPoint, tagPoint);
        double approachAngle = taggedObject.GetParamReal(approachAngleIdx);
        double shoulderAngle = taggedObject.GetParamReal(shoulderAngleIdx);

        bool approachChanged = !DoublesAreNearlyEqual(prevApproachAngle, approachAngle);
        bool shoulderChanged = !DoublesAreNearlyEqual(prevShoulderAngle, shoulderAngle);
        ETagOrientation orientation = GetTagOrientation();

        if(approachChanged) {

            WorldPt newShoulder;
            newShoulder.SetAngle(approachAngle*(PI/180.0));
            newShoulder += endPoint;
            WorldPt perpPoint;
            perpPoint.SetAngle((shoulderAngle+90)*(PI/180));
            VWLine2D perpLine(VWPoint2D(tagPoint), VWPoint2D(tagPoint+perpPoint));

            VWPoint2D intersection;
            bool parallel;
            bool onShoulder;
            bool onApproach;
            VWLine2D::IntersectLines(VWPoint2D(endPoint), 
                                     VWPoint2D(newShoulder), 
                                     VWPoint2D(shoulderPoint), 
                                     VWPoint2D(tagPoint), 
                                     intersection, parallel, onShoulder, onApproach);

            if(parallel || 
               !perpLine.AreOnSameSide(shoulderPoint.x, shoulderPoint.y, intersection.x, intersection.y) ||
               !DoublesAreNearlyEqual(GetAngle(endPoint, intersection), approachAngle)) {
                // If we can't find a point along the shoulder line to place the new shoulder,
                // we fall through and use the shoulderChanged case.
                
                shoulderChanged = true;
            } else {
                shoulderPoint = intersection;
            }

        }
        
        if(shoulderChanged) {
            //Apply changes to shoulder angle, if any
            double approachLength = sqrt(pow(shoulderPoint.x-endPoint.x, 2)+ 
                                         pow(shoulderPoint.y-endPoint.y, 2));
            double shoulderLength = sqrt(pow(tagPoint.x-shoulderPoint.x, 2)+ 
                                         pow(tagPoint.y-shoulderPoint.y, 2));
            shoulderPoint.x = endPoint.x + (cos(approachAngle*(PI/180.0)) * approachLength);
            shoulderPoint.y = endPoint.y + (sin(approachAngle*(PI/180.0)) * approachLength);
            tagPoint.x = shoulderPoint.x + (cos(shoulderAngle*(PI/180.0)) * shoulderLength);
            tagPoint.y = shoulderPoint.y + (sin(shoulderAngle*(PI/180.0)) * shoulderLength);

            if(orientation == kTagRight && tagPoint.x < shoulderPoint.x) {
                SetTagOrientation(kTagLeft);
            } else if(orientation == kTagLeft && tagPoint.x > shoulderPoint.x) {
                SetTagOrientation(kTagRight);
            }
        } 
        
        if(!(shoulderChanged || approachChanged)){
            // If the angles were not changed, we should flip the tag around to respect
            // the chosen orientation
            if((orientation == kTagRight && tagPoint.x < shoulderPoint.x) ||
                (orientation == kTagLeft && tagPoint.x > shoulderPoint.x)) {

                if(useShoulder && orientation != kTagFloating) {
                    tagPoint.x += 2*(shoulderPoint.x-tagPoint.x);
                } else {
                    shoulderPoint.x += 2*(tagPoint.x-shoulderPoint.x);
                }
                taggedObject.SetParamReal(shoulderAngleIdx, GetAngle(shoulderPoint, tagPoint));
                taggedObject.SetParamReal(approachAngleIdx, GetAngle(endPoint, shoulderPoint));

            }
        }

        taggedObject.SetParamReal(shoulderPointIdx, shoulderPoint.x);
        taggedObject.SetParamReal(shoulderPointIdx+1, shoulderPoint.y);
        taggedObject.SetParamReal(tagPointIdx, tagPoint.x);
        taggedObject.SetParamReal(tagPointIdx+1, tagPoint.y);
        
    }
}

void VWTaggedObj::DrawTagInteractive(EPointID movedPointID, VWPoint2D* cursorOffset)
{        
    ASSERTN(kBAtwood, objectHandle != nil);

    if(GetTagOrientation() != kTagFloating) {

        VWParametricObj taggedObject(objectHandle);

        WorldPt movedPoint;
        WorldPt tagPoint = GetTagControlPoint(kTagPoint);
        WorldPt shoulderPoint = GetTagControlPoint(kShoulderPoint);
        WorldPt endPoint = GetTagControlPoint(kEndPoint);

        if((movedPointID == kShoulderPoint && tagPoint == shoulderPoint) ||
           (movedPointID == kEndPoint && endPoint == tagPoint)) {
            movedPointID = kTagPoint;
        } else if(movedPointID == kEndPoint && endPoint == shoulderPoint) {
            movedPointID = kShoulderPoint;
        }

        VWTransformMatrix matrix;
        taggedObject.GetObjectMatrix(matrix);

        tagPoint = matrix.PointTransform(tagPoint);
        shoulderPoint = matrix.PointTransform(shoulderPoint);
        endPoint = matrix.PointTransform(endPoint);

        if(nil == cursorOffset) {
            GS_GetToolPtCurrent2D(gCBP, movedPoint);
        } else {
            movedPoint = kTagPoint == movedPointID ? tagPoint : (kShoulderPoint == movedPointID ? shoulderPoint : endPoint);
            movedPoint += *cursorOffset;
        }

        switch(movedPointID) {
            case kTagPoint:
                if(shoulderPoint == tagPoint && endPoint == tagPoint) {
                    shoulderPoint.x = (movedPoint.x+endPoint.x)/2;
                    shoulderPoint.y = movedPoint.y;
                }
                tagPoint += movedPoint - tagPoint;
                break;
            case kShoulderPoint:
                tagPoint += movedPoint - shoulderPoint;
                shoulderPoint += movedPoint - shoulderPoint;
                break;
            case kEndPoint:
                endPoint += movedPoint - endPoint;
                break;
        }

        if(useShoulder) {
            GS_DrawCoordLine(gCBP, tagPoint, shoulderPoint);
            GS_DrawCoordLine(gCBP, shoulderPoint, endPoint);
        } else {
            GS_DrawCoordLine(gCBP, tagPoint, endPoint);
        }
    }
}

void VWTaggedObj::OnMoveTagControlPoint(EPointID movedPointID, VWPoint2D* cursorOffset)
{
    ASSERTN(kBAtwood, objectHandle != nil);

    WorldPt endPoint = GetTagControlPoint(kEndPoint);
    WorldPt shoulderPoint = GetTagControlPoint(kShoulderPoint);
    WorldPt tagPoint = GetTagControlPoint(kTagPoint);
    ETagOrientation orientation  = GetTagOrientation();

    if((movedPointID == kShoulderPoint && tagPoint == shoulderPoint) ||
       (movedPointID == kEndPoint && endPoint == tagPoint)) {
        movedPointID = kTagPoint;
    } else if(movedPointID == kEndPoint && endPoint == shoulderPoint) {
        movedPointID = kShoulderPoint;
    }

    VWParametricObj taggedObject(objectHandle);

    WorldPt movedPoint;
    VWTransformMatrix matrix;
    taggedObject.GetObjectMatrix(matrix);
        
    if(nil == cursorOffset) {
        GS_GetToolPtCurrent2D(gCBP, movedPoint);
        movedPoint = matrix.InversePointTransform(movedPoint);
    } else {
        movedPoint = kTagPoint == movedPointID ? tagPoint : (kShoulderPoint == movedPointID ? shoulderPoint : endPoint);
        movedPoint = matrix.PointTransform(movedPoint);
        movedPoint += *cursorOffset;
        movedPoint = matrix.InversePointTransform(movedPoint);
    }    

    switch(movedPointID) {
        case kTagPoint:
            if(shoulderPoint == tagPoint && endPoint == tagPoint) {
                shoulderPoint.x = (movedPoint.x + endPoint.x)/2;
                shoulderPoint.y = movedPoint.y;
            } else if(!useShoulder || orientation == kTagFloating) {
                //if the shoulder point is disabled, move it along with the tag.
                shoulderPoint += (movedPoint-tagPoint);
            }
            tagPoint = movedPoint;
            break;
        case kShoulderPoint:
            tagPoint += movedPoint-shoulderPoint;
            shoulderPoint = movedPoint;
            break;
        case kEndPoint:
            endPoint = movedPoint;
            break;
    }

    //update the tag orientation to match the new location.
    if(orientation == kTagRight && tagPoint.x < shoulderPoint.x) {
        SetTagOrientation(kTagLeft);        
    } else if(orientation == kTagLeft && tagPoint.x > shoulderPoint.x) {
        SetTagOrientation(kTagRight);
    }

    taggedObject.SetParamReal(tagPointIdx, tagPoint.x);
    taggedObject.SetParamReal(tagPointIdx+1, tagPoint.y);
    taggedObject.SetParamReal(shoulderPointIdx, shoulderPoint.x);
    taggedObject.SetParamReal(shoulderPointIdx+1, shoulderPoint.y);
    taggedObject.SetParamReal(endPointIdx, endPoint.x);
    taggedObject.SetParamReal(endPointIdx+1, endPoint.y);
    taggedObject.SetParamReal(approachAngleIdx, GetAngle(endPoint, shoulderPoint));
    taggedObject.SetParamReal(shoulderAngleIdx, GetAngle(shoulderPoint, tagPoint));
}

void VWTaggedObj::OnControlPointSelected(EPointID movedPointID)
{
    ASSERTN(kBAtwood, objectHandle != nil);

    WorldPt snapStartPoint;
    VWParametricObj taggedObject(objectHandle);

    switch(movedPointID) {
        case kTagPoint:
            if(useShoulder) {
                snapStartPoint = GetTagControlPoint(kShoulderPoint);
            } else {
                snapStartPoint = GetTagControlPoint(kEndPoint);
            }            
            break;
        case kShoulderPoint:
            snapStartPoint = GetTagControlPoint(kEndPoint);
            break;
        case kEndPoint:
            if(useShoulder) {
                snapStartPoint = GetTagControlPoint(kShoulderPoint);
            } else {
                snapStartPoint = GetTagControlPoint(kTagPoint);
            }
            break;
            
    }

    VWTransformMatrix matrix;
    taggedObject.GetObjectMatrix(matrix);
    snapStartPoint = matrix.PointTransform(snapStartPoint);

    GS_SetProgramVariable(gCBP, varSmartCursorStartPt2DEx, &snapStartPoint);
}

double VWTaggedObj::GetAngle(WorldPt a, WorldPt b)
{
    double angle = b.DegreeAngleFrom(a);
    
    // Force tiny angles to be zero.
    if(DoubleIsNearlyZero(angle)) {
        angle = 0.0;
    }
    return(angle);
}

void VWTaggedObj::DrawLeaderLine(InternalIndex classID)
{
    MCObjectHandle leaderLine;

    WorldPt tagPoint = GetTagControlPoint(kTagPoint);
    WorldPt shoulderPoint = GetTagControlPoint(kShoulderPoint);
    WorldPt endPoint = GetTagControlPoint(kEndPoint);

    if(useShoulder) {
        //Create the shoulder line
        leaderLine = GS_CreateLine(gCBP, tagPoint, shoulderPoint);
        
        //Set the leader line properties
        if (leaderLine != nil) {
	        GS_SetObjectClass(gCBP, leaderLine, classID);
	        GS_SetFColorsByClass(gCBP, leaderLine);
	        GS_SetFPatByClass(gCBP, leaderLine);
	        GS_SetLWByClass(gCBP, leaderLine);
	        GS_SetPColorsByClass(gCBP, leaderLine);
	        GS_SetPPatByClass(gCBP, leaderLine);
        }
    } else {
        shoulderPoint = tagPoint;
    }

    //Create the approach line
    leaderLine = GS_CreateLine(gCBP, shoulderPoint, endPoint);

    //Set the leader line properties
    SMarkerStyle mstyle;
    GS_GetClassEndMarker(gCBP, classID, mstyle);
    if (leaderLine != nil) {
	    GS_SetObjectClass(gCBP, leaderLine, classID);
	    GS_SetFColorsByClass(gCBP, leaderLine);
	    GS_SetFPatByClass(gCBP, leaderLine);
	    GS_SetLWByClass(gCBP, leaderLine);
	    GS_SetPColorsByClass(gCBP, leaderLine);
	    GS_SetPPatByClass(gCBP, leaderLine);
        GS_SetObjEndMarker(gCBP, leaderLine, mstyle, useMarker);
    }
}

VWTaggedObj::ETagOrientation VWTaggedObj::GetTagOrientation()
{
    VWParametricObj taggedObject(objectHandle);
    TXString text = taggedObject.GetParamString(orientationIdx);

    for(int i=0; i<= kTagOrientationMax; i++) {
        if(text == TagOrientationStrings[i]) {
            return((ETagOrientation)i);
        }
    }
    return(kTagNone);
}

void VWTaggedObj::SetTagOrientation(ETagOrientation orientation)
{
    VWParametricObj taggedObject(objectHandle);
    VERIFYN(kBAtwood, orientation <= kTagOrientationMax && orientation >= (ETagOrientation)0);
    taggedObject.SetParamString(orientationIdx, TagOrientationStrings[(int)orientation]); 
}
