<%@page contentType="image/svg+xml;charset=UTF-8" pageEncoding="ISO-8859-1" session="true"
%><%@page errorPage="Error.jsp"
%><%@page import="com.fujitsu.iflow.model.bpm.BPMAdmin"
%><%@page import="com.fujitsu.iflow.model.bpm.BPMSession"
%><%@page import="com.fujitsu.iflow.model.workflow.WFSession"
%><%@page import="com.fujitsu.iflow.common.UdaNameSanitizer"
%><%@page import="com.fujitsu.iflow.model.util.AEServers"
%><%@page import="com.fujitsu.iflow.model.util.QuickForm"
%><%@page import="com.fujitsu.iflow.model.util.URLEncoder"
%><%@page import="com.fujitsu.iflow.model.workflow.*"
%><%@page import="com.fujitsu.iflow.model.wfcore.*"
%><%@page import="java.awt.Point"
%><%@page import="java.util.*"
%><%@page import="java.io.*"
%><%request.setCharacterEncoding("UTF-8");request.getParameterNames(); //Need dummy call for WL9.1
%><%
    WFSession wfSession = (WFSession)session.getAttribute("ModelsessionID");
    if (wfSession == null) {
        response.sendRedirect(goToLogin(request, "Continue to Process Instance Graphic"));
        return;
    }

    String piId = request.getParameter("piId");
    out.clearBuffer();

    long piIdLong;
    try {
         piIdLong = Long.parseLong(piId);
    }
    catch (Exception e4) {
        throw new Exception("The piID variable had a problem being parsed as a long value: "+e4.toString());
    }

    ProcessInstance pi = null;
    String isArchived = request.getParameter("isArchived");
    if(isArchived != null && isArchived.equals("true")){
        BPMSession bps = (BPMSession) session.getAttribute("BPMSession");
        BPMAdmin bpas = bps.getAdmin();
        pi = bpas.getArchivedProcess(piIdLong);
    }
    else {
        pi = WFObjectFactory.getProcessInstance(piIdLong, wfSession);
    }
    Plan plan = pi.getPlan();
    int xmax = 290;
    int ymax = 290;

    if (bias == null) {
        bias = new Point[24];
        bias[Node.TYPE_START] = new Point(0,0);
        bias[Node.TYPE_EXIT] = new Point(0,0);
        bias[Node.TYPE_CONDITION] = new Point(18,18);
        bias[Node.TYPE_AND] = new Point(18,18);
        bias[Node.TYPE_OR] = new Point(18,18);
        bias[Node.TYPE_DELAY] = new Point(18,18);
        bias[Node.TYPE_SUB_PROCESS] = new Point(40,25);
        bias[Node.TYPE_CHAINED_PROCESS] = new Point(40,25);
        bias[Node.TYPE_REMOTE_SUB_PROCESS] = new Point(40,25);
        bias[Node.TYPE_VOTING_ACTIVITY] = new Point(0,0);
        bias[Node.TYPE_ACTIVITY] = new Point(0,0);
    }


    Node[] allNodes = plan.getNodes();
    int last = allNodes.length;
    for (int i=0; i<last; i++) {

        Node aNode = allNodes[i];
        Point p = aNode.getPosition();
        if (p.x+60>xmax) xmax = p.x+60;
        if (p.y+45>ymax) ymax = p.y+45;

    }


%><?xml version="1.0" encoding="UTF-8"?>
<svg width="<%=xmax+100%>px" height="<%=ymax+100%>px" overflow="scroll" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <marker id="Triangle" viewBox="0 0 16 16" refX="16" refY="8"
      markerUnits="strokeWidth" markerWidth="6" markerHeight="4"
      orient="auto">
      <path d="M 0 0 L 16 8 L 0 16 z"/>
    </marker>
  </defs>

<%
    for (int i=0; i<last; i++) {

        Node aNode = allNodes[i];

        Arrow[] az = aNode.getOutgoingArrows();
        int last2 = az.length;
        for (int j=0; j<last2; j++) {
            Arrow aArrow = az[j];

            Point startPoint = aArrow.getStartPoint();
            Point endPoint = aArrow.getEndPoint();
            Point[] pz = aArrow.getPoints();
            if (pz == null) {
                pz = new Point[0];
            }

            boolean acceptableStartPoint = acceptablePoint(startPoint, aNode);
            boolean acceptableEndPoint = acceptablePoint(endPoint, aArrow.getTargetNode());

            if (!acceptableStartPoint || !acceptableEndPoint) {
                // covering up an old bug.  Some process diagrams has incorrect arrow end points
                // stored.  Fixed in latest version, but some old processes will not display correctly
                // problem was that same value was stored for start and end, so calculate a new start
                // and end if the start and end are exactly equal.
                startPoint = aNode.getPosition();
                if (pz.length>0) {
                    endPoint = calcEndPoint(pz[pz.length-1], aArrow.getTargetNode());
                    startPoint = calcEndPoint(pz[0], aNode);
                }
                else {
                    startPoint = calcEndPoint(aArrow.getTargetNode().getPosition(), aNode);
                    endPoint = calcEndPoint(startPoint, aArrow.getTargetNode());
                }
            }

            StringBuffer b = new StringBuffer();
            addPoint(b, "M", startPoint);

            int last3 = pz.length;
            for (int k=0; k<last3; k++) {
                Point midPoint = pz[k];
                addPoint(b, "L", midPoint);
                startPoint = midPoint;
            }
            addPoint(b, "L", endPoint);

            drawPath(b.toString(), "fill:none; stroke:lightskyblue; stroke-width:2", out);

            //now pick the middle-most line segment, and put text in middle of it
            if (pz.length>0) {
                startPoint = pz[pz.length/2];
            }
            if (pz.length>2) {
                endPoint = pz[pz.length/2+1];
            }
            drawText((startPoint.x+endPoint.x)/2,(startPoint.y+endPoint.y)/2,aArrow.getName(), out);
        }
    }

    // now write out all the nodes.
    for (int i=0; i<last; i++) {

        Node aNode = allNodes[i];
        int nodeType = aNode.getType();
        Point bp = bias[nodeType];
        Point ap = aNode.getPosition();
        Point aPoint = new Point(ap.x+bp.x, ap.y+bp.y);

        NodeInstance ni = pi.getNodeInstance(aNode);
        String color = "white";
        switch (ni.getState()) {
            case NodeInstance.STATE_INITIAL:
                color = "white";
                break;
            case NodeInstance.STATE_RUNNING:
                color = "springgreen; stroke-width:2";
                break;
            case NodeInstance.STATE_WAITING_ON_SUB_PROCESS:
                color = "yellow";
                break;
            case NodeInstance.STATE_COMPLETED:
                color = "lightcyan";
                break;
            case NodeInstance.STATE_ERROR:
                color = "crimson";
                break;
            default:
                color = "orange";
                break;
        }

        switch (nodeType) {
            case Node.TYPE_START:
                %><circle cx="<%= aPoint.x %>" cy="<%= aPoint.y %>" r="30" style="fill:<%=color%>; stroke:black"/>
                <text x="<%=aPoint.x-15%>" y="<%=aPoint.y+5%>" style="fill:black">Start</text><%
                break;
            case Node.TYPE_EXIT:
                %><circle cx="<%= aPoint.x %>" cy="<%= aPoint.y %>" r="30" style="fill:white; stroke-width:5; stroke:black"/>
                <text x="<%=aPoint.x-15%>" y="<%=aPoint.y+5%>" style="fill:black"><% QuickForm.writeHtml(out,aNode.getName()); %></text><%
                break;
            case Node.TYPE_CONDITION:
                if(aNode.isCustomNodeType()) 
                {
                    	String type = aNode.getCustomNodeType();
			if(type.equals("com.fujitsu.iflow.gui.planview.UIComplexCondition"))
			{
				%><path d="M <%=aPoint.x%> <%=aPoint.y-20%> l 20 20 l -20 20 l -20 -20 z" style="fill:none; stroke:black"/>
				<path d="M <%=aPoint.x-12%> <%=aPoint.y%> l 24 0" style="fill:none; stroke:black; stroke-width:4" />
				<path d="M <%=aPoint.x%> <%=aPoint.y-12%> l 0 24" style="fill:none; stroke:black; stroke-width:4" />
				<path d="M <%=aPoint.x-8%> <%=aPoint.y-8%> l 16 16" style="fill:none; stroke:black; stroke-width:4" />
				<path d="M <%=aPoint.x+8%> <%=aPoint.y-8%> l -16 16" style="fill:none; stroke:black; stroke-width:4" />
				<%
			}                    	
                }
                else 
                {
                	%><path d="M <%=aPoint.x-27%> <%=aPoint.y%> l 27 18 l 27 -18 l -27 -18 l -27 18 z"  style="fill:powderblue; stroke:black"/><%                
                }
                break;
            case Node.TYPE_AND:
                %><path d="M <%=aPoint.x-18%> <%=aPoint.y-18%> l 9 0 l 9 5 l 9 -5 l 9 0 l 0 9 l -5 9 l 5 9 l 0 9 l -9 0 l -9 -5 l -9 5 l -9 0 l 0 -9 l 5 -9 l -5 -9 l 0 -9 z"  style="fill:<%=color%>; stroke:black"/><%
                break;
            case Node.TYPE_OR:
	            if (aNode.isCustomNodeType())
	            {
                    	String type = aNode.getCustomNodeType();
                    	if(type.equals("com.fujitsu.iflow.gui.planview.UICustomEmail"))
                    	{
				%><path d="M <%=aPoint.x-18%> <%=aPoint.y-12%> l 0 40 l 40 0 l 0 -40 l -40 0 l 20 20 l 20 -20 " style="fill:none; stroke:black"/><%                        
			}
			else if(type.equals("com.fujitsu.iflow.gui.planview.UICustomDB"))
			{
				%>
				<ellipse rx="20" ry="5" cx="<%=aPoint.x%>" cy="<%=aPoint.y-20%>" style="fill:none; stroke:black" />
				<ellipse rx="20" ry="5" cx="<%=aPoint.x%>" cy="<%=aPoint.y+20%>" style="fill:none; stroke:black" />
				<line x1="<%=aPoint.x-20%>" y1="<%=aPoint.y+20%>" x2="<%=aPoint.x-20%>" y2="<%=aPoint.y-20%>" style="fill:none; stroke:black" />
				<line x1="<%=aPoint.x+20%>" y1="<%=aPoint.y+20%>" x2="<%=aPoint.x+20%>" y2="<%=aPoint.y-20%>" style="fill:none; stroke:black" /><%				
			}			

			else if(type.equals("com.fujitsu.iflow.gui.planview.UICustomWebService"))
			{
				%>
                                <circle cx="<%= aPoint.x - 5%>" cy="<%= aPoint.y%>" r="15" style="fill:powderblue; stroke:black"/>
				<path d="M <%=aPoint.x-8%> <%=aPoint.y-4%> l 20 0 l 0 15 l -20 0 z" style="fill:lightgray; stroke:black"/>
				<path d="M <%=aPoint.x-2%> <%=aPoint.y+4%> l 10 0" style="fill:none; stroke:black; stroke-width:2" />
				<path d="M <%=aPoint.x+3%> <%=aPoint.y-1%> l 0 10" style="fill:none; stroke:black; stroke-width:2" />
				<path d="M <%=aPoint.x-1%> <%=aPoint.y%> l 8 8" style="fill:none; stroke:black; stroke-width:2" />
				<path d="M <%=aPoint.x+7%> <%=aPoint.y%> l -8 8" style="fill:none; stroke:black; stroke-width:2" />
				<%
			}			
		    }
		    else
		    {
                	%><path d="M <%=aPoint.x-18%> <%=aPoint.y-12%> l 6 0 l 0 -6 l 24 0 l 0 6 l 6 0 l 0 24 l -6 0 l 0 6 l -24 0 l 0 -6 l -6 0 z"  style="fill:powderblue; stroke:black"/><%
                    }
                    break;
            case Node.TYPE_DELAY:
                %><path d="M <%=aPoint.x-20%> <%=aPoint.y-13%> l 40 0 l -13 25 l -14 0 z"  style="fill:<%=color%>; stroke:black"/><%
                break;
            case Node.TYPE_SUB_PROCESS:
                %><path d="M <%=aPoint.x-40%> <%=aPoint.y-12%> l 16 -13 l 48 0 l 16 13 l 0 24 l -16 13 l -48 0 l -16 -13 z"  style="fill:<%=color%>; stroke:black"/><%
                drawTextBox(aPoint.x-30,aPoint.y-5,100,35,aNode.getSubPlanName(), out);
                break;
            case Node.TYPE_CHAINED_PROCESS:
                %><path d="M <%=aPoint.x-40%> <%=aPoint.y-12%> l 16 -13 l 48 0 l 16 13 l 0 24 l -16 13 l -48 0 l -16 -13 z"  style="fill:<%=color%>; stroke:black"/><%
                %><path d="M <%=aPoint.x-24%> <%=aPoint.y-15%> l 48 0 l -10 -4 l 3 4 l -3 4 l 10 -4 z"  style="fill:black; stroke:black"/><%
                drawTextBox(aPoint.x-30,aPoint.y-5,100,35,aNode.getChainedPlanName(), out);
                break;
            case Node.TYPE_REMOTE_SUB_PROCESS:
                %><path d="M <%=aPoint.x-40%> <%=aPoint.y-12%> l 16 -13 l 48 0 l 16 13 l 0 24 l -16 13 l -48 0 l -16 -13 z"  style="fill:<%=color%>; stroke:black"/><%
                drawTextBox(aPoint.x-30,aPoint.y-5,100,35,aNode.getName(), out);
                break;
            case Node.TYPE_VOTING_ACTIVITY:
                %><rect x="<%=aPoint.x-51%>" y="<%=aPoint.y-44%>" width="102" height="10" rx="5" ry="5" style="fill:powderblue; stroke:black"/><%
                // fall through here, don't break
            case Node.TYPE_ACTIVITY:
            default:
                %><rect x="<%=aPoint.x-51%>" y="<%=aPoint.y-34%>" width="102" height="68" rx="5" ry="5" style="fill:<%=color%>; stroke:black"/><%
                drawTextBox(aPoint.x-50,aPoint.y,100,35,aNode.getName(), out);
                if (aNode.supportRole()) {
                    drawTextBox(aPoint.x-50,aPoint.y-35,100,35,aNode.getRole(), out);
                }
                break;
         }
    }
%>
</svg>

<%!
    public
    String
    goToLogin(HttpServletRequest request, String pageName)
        throws Exception
    {
        StringBuffer s1 = request.getRequestURL();
        s1.append("?");
        s1.append(request.getQueryString());

        String loginUrl = "Login.jsp?nextPage="
                      +URLEncoder.encode(s1.toString(), "UTF-8")
                      +"&nextLink="
                      +URLEncoder.encode(pageName, "UTF-8");
        return loginUrl;
    }


    public
    void
    drawLine(Point a, Point b, String style, Writer out)
        throws Exception
    {
        out.write("<line x1=\"");
        out.write(Integer.toString(a.x));
        out.write("\" y1=\"");
        out.write(Integer.toString(a.y));
        out.write("\" x2=\"");
        out.write(Integer.toString(b.x));
        out.write("\" y2=\"");
        out.write(Integer.toString(b.y));
        out.write("\" style=\"");
        out.write(style);
        out.write("\" marker-end=\"url(#Triangle)\"/>\n");
    }
    public
    void
    drawPath(String path, String style, Writer out)
        throws Exception
    {
        out.write("<path d=\"");
        out.write(path);
        out.write("\" style=\"");
        out.write(style);
        out.write("\" marker-end=\"url(#Triangle)\"/>\n");
    }
    public
    void
    drawText(int x, int y, String text, Writer out)
        throws Exception
    {
        if (text != null && text.length()>0) {
            out.write("<text x=\"");
            out.write(Integer.toString(x));
            out.write("\" y=\"");
            out.write(Integer.toString(y));
            out.write("\" text-anchor=\"middle\">");
            QuickForm.writeHtml(out, text);
            out.write("</text>\n");
        }
    }
    public
    void
    drawTextBox(int x, int y, int width, int height, String text, Writer out)
        throws Exception
    {
        if (text != null && text.length()>0) {
            out.write("<svg x=\"");
            out.write(Integer.toString(x));
            out.write("\" y=\"");
            out.write(Integer.toString(y));
            out.write("\" width=\"");
            out.write(Integer.toString(width));
            out.write("\" height=\"");
            out.write(Integer.toString(height));
            out.write("\"><text x=\"1\" y=\"12\">");
            QuickForm.writeHtml(out, text);
            out.write("</text></svg>\n");
        }
    }


    // figure out whether the point given is in the acceptable range for an
    // end point opinting to or from a given node.  If so, the point might be
    // used in the drawing, if not, the position will have to be calculated again.
    public
    boolean
    acceptablePoint(Point a, Node aNode)
    {
        //if (true) return true;
        int nodeType = aNode.getType();
        Point bp = bias[nodeType];
        Point ap = aNode.getPosition();
        Point nodeCenter = new Point(ap.x+bp.x, ap.y+bp.y);


        if (nodeType == Node.TYPE_START || nodeType == Node.TYPE_EXIT ||
            nodeType == Node.TYPE_DELAY || nodeType == Node.TYPE_OR ||
            nodeType == Node.TYPE_AND || nodeType == Node.TYPE_CONDITION) {
            // just a circle, distance from the center of the node

            int hdist = a.x-nodeCenter.x;
            int vdist = a.y-nodeCenter.y;
            int distanceFromCenter = (int) Math.sqrt((hdist*hdist)+(vdist*vdist));

            if (distanceFromCenter>=36) {
                //debugStr = debugStr + " distance="+distanceFromCenter;
                return false;
            }
            //as long as the end point is within 36 pixels of the center, use it
            return true;
        }

        //in this case we have an activity, subprocess, or votingnode.  Points are
        //placed around a box which is 2x36 high, 2x53 wide.  As long as the point
        //is in the box, that should be fine.
        if (a.x>nodeCenter.x+59) {
            //debugStr = debugStr+" x too high ("+nodeCenter.x+")&("+a.x+")";
            return false;
        }
        if (a.x<nodeCenter.x-59) {
            //debugStr = debugStr+" x too low ("+nodeCenter.x+")&("+a.x+")";
            return false;
        }
        if (a.y>nodeCenter.y+40) {
            //debugStr = debugStr+" y too high ("+nodeCenter.y+")&("+a.y+")";
            return false;
        }
        if (a.y<nodeCenter.y-40) {
            //debugStr = debugStr+" y too low ("+nodeCenter.y+")&("+a.y+")";
            return false;
        }
        return true;
    }



    // figure out how the
    public
    Point
    calcEndPoint(Point a, Node aNode)
    {
        int nodeType = aNode.getType();
        Point bp = bias[nodeType];
        Point ap = aNode.getPosition();
        Point b = new Point(ap.x+bp.x, ap.y+bp.y);


        Point newEnd = new Point(b.x, b.y);
        // avoid divide by zero
        if (b.x == a.x) {
            if (a.y>b.y) {
                return new Point(b.x, b.y+35);
            }
            else {
                return new Point(b.x, b.y-35);
            }
        }

        float slope = ((float)(b.y-a.y))/(b.x-a.x);
        if (nodeType == Node.TYPE_START || nodeType == Node.TYPE_EXIT ||
            nodeType == Node.TYPE_DELAY || nodeType == Node.TYPE_OR ||
            nodeType == Node.TYPE_AND || nodeType == Node.TYPE_CONDITION) {
            // just a circle
            double angle = Math.atan(slope);
            if (a.x>b.x) {
                newEnd.y = b.y+(int)(32.0*Math.sin(angle));
                newEnd.x = b.x+(int)(32.0*Math.cos(angle));
            }
            else {
                newEnd.y = b.y-(int)(32.0*Math.sin(angle));
                newEnd.x = b.x-(int)(32.0*Math.cos(angle));
            }
            return newEnd;
        }

        if (slope > 36.0/53 || slope < -36.0/53) {
            // if it hits the top
            if (a.y<b.y) {
                newEnd.y = b.y-36;
                newEnd.x = b.x - (int) (36/slope);
            }
            // it hit the bottom
            else {
                newEnd.y = b.y+36;
                newEnd.x = b.x + (int) (36/slope);
            }
        }
        else {
            // hits right side
            if (a.x>b.x) {
                newEnd.x = b.x+53;
                newEnd.y = b.y + (int) (53*slope);
            }
            // it hit left side
            else {
                newEnd.x = b.x-53;
                newEnd.y = b.y - (int) (53*slope);
            }
        }
        return newEnd;
    }

    public
    void
    addPoint(StringBuffer b, String comm, Point p)
    {
        b.append(comm);
        b.append(" ");
        b.append(Integer.toString(p.x));
        b.append(" ");
        b.append(Integer.toString(p.y));
        b.append(" ");
    }

    static Point[] bias = null;

%>
