dojo.provide("parus.statusModelRouteDialog");

dojo.require("dijit.Dialog");
dojo.require("dijit.form.Button");

dojo.require("parus.parus8");
dojo.require("parus.dialog");
dojo.require("parus.gridData");


dojo.declare("parus.statusModelRouteDialog", [parus.Dialog], {

  documentRN: null,

  event: null,

  _nodes: [],
  _nodesByPoint: [],
  _edges: [],

  selected: null,

  postCreate: function() {
    this.inherited(arguments);
    parus.balance._createActionsButton(this.popupMenu, this.headerPane);
    var settings = parus.userdata.get(this.declaredClass);
    if (settings) {
      parus.gridSettingsDialog.mergeMenuSettings(this.popupMenu, settings.actionSettings, settings.userProcsActions, settings.userReportsActions, settings.userAppsActions);
    };
  },

  onShow: function() {
    this.popupMenu.bindDomNode(this.dialog.containerNode);

    var canvas = dojo.query("canvas", this.dialog.containerNode)[0];
    this.popupMenu.bindDomNode(canvas);

    var graph = dojo.query("*[role='graph']", this.dialog.containerNode)[0];
    this.popupMenu.bindDomNode(graph);

    dojo.connect(this.statusModelGraphContentPane.domNode, "onmousedown", this, this.onMouseDown);

    this.refresh(false);
  },

  refresh: function(refreshEvent) {
    var canvas = dojo.query("canvas", this.dialog.containerNode)[0];
    var graph = dojo.query("*[role='graph']", this.dialog.containerNode)[0];

    //cleanup
    var ctx = canvas.getContext("2d");
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    dojo.forEach(this._nodes, function(node) {
      node.domNode.parentNode.removeChild(node.domNode);
    });
    this._nodes = [];
    this._nodesByPoint = [];
    this._edges = [];

    var passes = null;
    var points = null;

    if (refreshEvent) {
      this.event = parus.balance.getStatusModelEventInfo(this.documentRN);
    }

    (new dojo.DeferredList([

    parus.database.query("BalanceStatusModelRoutePoints", {
      "EVENT": this.event.event
    }, function(response) {
      points = parus.parus8.rowNodes(response);
    }),

    parus.database.query("BalanceStatusModelRoutePasses", {
      "EVENT": this.event.event
    }, function(response) {
      passes = parus.parus8.rowNodes(response);
    })

    ])).addCallback(dojo.hitch(this, function() {

      var bounds = {
        minx: null,
        miny: null,
        maxx: null,
        maxy: null
      };

      /* test:
       points = [{
       NPOINT: "A",
       ORIGIN_X: 800,
       ORIGIN_Y: 800,
       EVNSTAT_CODE: "Point"
       }];
       var n = 50;
       for (var i = 0; i < n; i++) {
       var LAngle = i * 2 * Math.PI / n;
       points.push({
       NPOINT: "A" + i,
       ORIGIN_X: 800 + 400 * Math.cos(LAngle),
       ORIGIN_Y: 800 + 400 * Math.sin(LAngle),
       EVNSTAT_CODE: "Point" + i
       });
       };
       passes = [];
       for (var i = 0; i < n; i++) {
       passes.push({
       NPOINT: "A",
       NEXT_POINT: "A" + i
       });
       passes.push({
       NPOINT: "A" + i,
       NEXT_POINT: "A"
       });
       };
       */

      dojo.forEach(points, dojo.hitch(this, function(row) {
        var x = parseInt(parus.parus8.nodeField(row, "ORIGIN_X"));
        var y = parseInt(parus.parus8.nodeField(row, "ORIGIN_Y"));
        if (bounds.minx == null || (bounds.minx > x)) bounds.minx = x;
        if (bounds.miny == null || (bounds.miny > y)) bounds.miny = y;

        var node = {
          dialogObject: this,
          id: parus.parus8.nodeField(row, "NPOINT").toString(),
          info: row
        };

        this._nodes.push(node);
        this._nodesByPoint[node.id] = node;

      }));

      dojo.forEach(passes, dojo.hitch(this, function(row) {
        var fromNode = this._nodesByPoint[parus.parus8.nodeField(row, "NPOINT").toString()];
        var toNode = this._nodesByPoint[parus.parus8.nodeField(row, "NEXT_POINT").toString()];

        if (fromNode) fromNode.hasOutput = true;

        if (toNode) fromNode.hasInput = true;

        var edge = {
          dialogObject: this,
          id: parus.parus8.nodeField(row, "NPASS").toString(),
          info: row,
          fromPoint: parus.parus8.nodeField(row, "NPOINT").toString(),
          toPoint: parus.parus8.nodeField(row, "NEXT_POINT").toString()
        };

        this._edges.push(edge);
      }));


      dojo.forEach(this._nodes, dojo.hitch(this, function(node) {

        var row = node.info;

        var divNode = dojo.place(dojo.trim(dojo.cache("parus", "statusModelRoutePoint.html")), graph, "last");

        dojo.style(divNode, {
          left: (parseInt(parus.parus8.nodeField(row, "ORIGIN_X")) - bounds.minx + 20) + "px",
          top: (parseInt(parus.parus8.nodeField(row, "ORIGIN_Y")) - bounds.miny + 20) + "px"
        });

        /*
         *statusModelSingle/ statusModelDouble/statusModelTriple /statusModelHexaple
         *statusModelSimpleText / ""
         *statusModelPointerImage / ""
         *statusModelPointBlackText / statusModelPointGreyText
         statusModelPointGreenBackground/statusModelPointGreyBackground/statusModelPointYellowBackground
         *statusModelPointOrangeBorder / statusModelPointGreyBorder / statusModelPointBlackBorder?
         */

        var subText = String.fromCharCode(160);
        var classes = [];


        if ((parus.parus8.nodeField(row, "NPASSED_POINT") == "1") && (parus.parus8.nodeField(row, "NCURRENT_POINT") == "0")) subText = "(точка пройдена)";
        else if ((parus.parus8.nodeField(row, "ROUTE_START") == "1") && (parus.parus8.nodeField(row, "NCURRENT_POINT") == "1")) subText = "(начало маршрута)";
        else if ((!node.hasOutput) && (parus.parus8.nodeField(row, "NCURRENT_POINT") == "1")) subText = "(маршрут завершен)";
        else classes.push("statusModelSimpleText");

        if (parus.parus8.nodeField(row, "NCURRENT_POINT") == "1") {
          classes.push("statusModelPointerImage");
          classes.push("statusModelPointBlackText");
          classes.push("statusModelPointOrangeBorder");
        }
        else {
          classes.push("statusModelPointGreyText");
          classes.push("statusModelPointGreyBorder");
        }

        if (parus.parus8.nodeField(row, "TOGETHER_SIGN") == "1") {
          if (parus.parus8.nodeField(row, "ROUTE_START") == "1") classes.push("statusModelHexaple");
          else if (!node.hasOutput) classes.push("statusModelHexaple");
          else classes.push("statusModelTriple");
        }
        else {
          if (parus.parus8.nodeField(row, "ROUTE_START") == "1") classes.push("statusModelDouble");
          else if (!node.hasOutput) classes.push("statusModelDouble");
          else classes.push("statusModelSingle");
        }

        if ((parus.parus8.nodeField(row, "TOGETHER_SIGN") == "1")) {

          if (parus.parus8.nodeField(row, "NCURRENT_POINT") == "1") {

            if (parus.parus8.nodeField(row, "ROUTE_START") == "1") classes.push("statusModelPointGreenBackground"); //131*
            else if (!node.hasOutput) classes.push("statusModelPointGreenBackground"); //132
            else classes.push("statusModelPointGreenBackground"); //133*


          }
          else if (parus.parus8.nodeField(row, "NPASSED_POINT") == "1") {

            if (parus.parus8.nodeField(row, "ROUTE_START") == "1") classes.push("statusModelPointGreenBackground"); //111*
            else if (!node.hasOutput) classes.push("statusModelPointGreyBackground"); //112
            else classes.push("statusModelPointGreenBackground"); //113*

          }
          else {

            if (parus.parus8.nodeField(row, "ROUTE_START") == "1") classes.push("statusModelPointGreyBackground"); //121
            else if (!node.hasOutput) classes.push("statusModelPointGreyBackground"); //122
            else classes.push("statusModelPointGreyBackground"); //123*

          }

        }
        else {

          if (parus.parus8.nodeField(row, "NCURRENT_POINT") == "1") {

            if (parus.parus8.nodeField(row, "ROUTE_START") == "1") classes.push("statusModelPointYellowBackground"); //231*
            else if (!node.hasOutput) classes.push("statusModelPointYellowBackground"); //232*
            else classes.push("statusModelPointYellowBackground"); //233*


          }
          else if (parus.parus8.nodeField(row, "NPASSED_POINT") == "1") {

            if (parus.parus8.nodeField(row, "ROUTE_START") == "1") classes.push("statusModelPointYellowBackground"); //211*
            else if (!node.hasOutput) classes.push("statusModelPointGreyBackground"); //212
            else classes.push("statusModelPointYellowBackground"); //213*

          }
          else {

            if (parus.parus8.nodeField(row, "ROUTE_START") == "1") classes.push("statusModelPointGreyBackground"); //221
            else if (!node.hasOutput) classes.push("statusModelPointGreyBackground"); //222*
            else classes.push("statusModelPointGreyBackground"); //223*

          }

        };

        dojo.query("span.pointText", divNode)[0].innerText = parus.parus8.nodeField(row, "EVNSTAT_CODE");
        dojo.query("span.pointSubText", divNode)[0].innerText = subText;

        dojo.forEach(classes, function(className) {
          dojo.addClass(divNode, className);
        });

        parus.database.execute("BalanceStatusModelRoutePointTitle", {
          "UNITCODE": "BalanceReports",
          "DOCUMENT": this.documentRN,
          "EVENT": this.event.event,
          "POINT": node.id
        }, function(response) {
          var s = parus.parus8.nodeField(parus.parus8.rowNodes(response)[0], "clobRESULT");
          if (s) divNode.title = s.split(String.fromCharCode(160) + String.fromCharCode(160)).join(String.fromCharCode(9745));
        });

        var size = dojo.getMarginBox(divNode);

        if (bounds.maxx == null || (bounds.maxx < (size.l + size.w + 20))) bounds.maxx = size.l + size.w + 20;

        if (bounds.maxy == null || (bounds.maxy < (size.t + size.h + 20))) bounds.maxy = size.t + size.h + 20;

        var point = parus.parus8.nodeField(row, "NPOINT").toString();
        divNode.id = point;

        this.popupMenu.bindDomNode(divNode);

        dojo.query("*", divNode).forEach(function(childNode) {
          childNode.setAttribute("pointid", node.id);
        });

        dojo.mixin(node, {
          domNode: divNode,
          size: size
        });

      }));

      dojo.style(graph, {
        width: bounds.maxx + "px",
        height: bounds.maxy + "px"
      });

      var tempNode = dojo.place("<div class=\"statusModelEdge\"></div>", graph, "last");
      var edgeColor = dojo.getComputedStyle(tempNode).color;
      tempNode.parentNode.removeChild(tempNode);

      tempNode = dojo.place("<div class=\"statusModelPassedEdge\"></div>", graph, "last");
      var passedEdgeColor = dojo.getComputedStyle(tempNode).color;
      tempNode.parentNode.removeChild(tempNode);


      canvas.width = bounds.maxx;
      canvas.height = bounds.maxy;

      var ctx = canvas.getContext("2d");

      dojo.forEach(this._edges, dojo.hitch(this, function(edge, idx) {

        var LIndex = 0;
        var LCount = 0;

        var Source = edge.fromPoint;
        var Destination = edge.toPoint;
        var Index = idx;

        dojo.forEach(this._edges, function(edge, idx) {
          if (((Source == edge.fromPoint) && (Destination == edge.toPoint)) || ((Destination == edge.fromPoint) && (Source == edge.toPoint))) {
            LCount++;
            if (Index == idx) LIndex = LCount;
          };
        });

        var LSourceRect = this._nodesByPoint[edge.fromPoint].size;
        var LDestinationRect = this._nodesByPoint[edge.toPoint].size;

        var RectToPoint = function(r) {
          return {
            X: r.l + Math.floor(r.w / (LCount + 1)) * LIndex,
            Y: r.t + Math.floor(r.h / (LCount + 1)) * LIndex
          }
        };

        var LSourcePoint = RectToPoint(LSourceRect);
        var LDestinationPoint = RectToPoint(LDestinationRect);

        var TryIntersect = function(P1, P2, A, B, C, D1, D2) {

          var PX, PY;

          if (A == 0) {
            PY = Math.round(-C / B);

            if (P2.Y == P1.Y) {
              if (((D1 >= P1.X) && (D1 <= P2.X)) || ((D1 >= P2.X) && (D1 <= P1.X))) {
                PX = D1;
              }
              else {
                PX = D2;
              }
            }
            else {
              PX = Math.round(P1.X + (-C / B - P1.Y) * (P2.X - P1.X) / (P2.Y - P1.Y));
            };

            if ((PX >= D1) && (PX <= D2) && (((PY >= P1.Y) && (PY <= P2.Y)) || ((PY >= P2.Y) && (PY <= P1.Y)))) {
              return {
                X: PX,
                Y: PY
              };
            }
            else return null;
          }
          else if (B == 0) {
            PX = Math.round(-C / A);

            if (P2.X == P1.X) {
              if (((D1 >= P1.Y) && (D1 <= P2.Y)) || ((D1 >= P2.Y) && (D1 <= P1.Y))) {
                PY = D1;
              }
              else {
                PY = D2;
              }
            }
            else {
              PY = Math.round(P1.Y + (-C / A - P1.X) * (P2.Y - P1.Y) / (P2.X - P1.X));
            }

            if ((PY >= D1) && (PY <= D2) && (((PX >= P1.X) && (PX <= P2.X)) || ((PX >= P2.X) && (PX <= P1.X)))) {
              return {
                X: PX,
                Y: PY
              };
            }
            else return null;
          }
          else {
            return null;
          };
        };

        var tl = TryIntersect(LSourcePoint, LDestinationPoint, 0, 1, -LSourceRect.t, LSourceRect.l, LSourceRect.l + LSourceRect.w);
        if (!tl) {
          tl = TryIntersect(LSourcePoint, LDestinationPoint, 0, 1, -(LSourceRect.t + LSourceRect.h), LSourceRect.l, LSourceRect.l + LSourceRect.w);
          if (!tl) {
            tl = TryIntersect(LSourcePoint, LDestinationPoint, 1, 0, -LSourceRect.l, LSourceRect.t, LSourceRect.t + LSourceRect.h);
            if (!tl) {
              tl = TryIntersect(LSourcePoint, LDestinationPoint, 1, 0, -(LSourceRect.l + LSourceRect.w), LSourceRect.t, LSourceRect.t + LSourceRect.h);
              if (!tl) tl = LSourcePoint;
            }
          }
        };

        var br = TryIntersect(LSourcePoint, LDestinationPoint, 0, 1, -LDestinationRect.t, LDestinationRect.l, LDestinationRect.l + LDestinationRect.w);
        if (!br) {
          br = TryIntersect(LSourcePoint, LDestinationPoint, 0, 1, -(LDestinationRect.t + LDestinationRect.h), LDestinationRect.l, LDestinationRect.l + LDestinationRect.w);
          if (!br) {
            br = TryIntersect(LSourcePoint, LDestinationPoint, 1, 0, -LDestinationRect.l, LDestinationRect.t, LDestinationRect.t + LDestinationRect.h);
            if (!br) {
              br = TryIntersect(LSourcePoint, LDestinationPoint, 1, 0, -(LDestinationRect.l + LDestinationRect.w), LDestinationRect.t, LDestinationRect.t + LDestinationRect.h);
              if (!br) br = LDestinationPoint;
            }
          }
        };

        var LSin, LCos;

        var L = 8;
        if (tl.X >= br.X) L = -L;

        if (tl.X == br.X) {
          br.X++;
          LSin = 1;
          LCos = 0;
        }
        else {
          var LAngle = Math.atan((br.Y - tl.Y) / (br.X - tl.X));
          LSin = Math.sin(LAngle);
          LCos = Math.cos(LAngle);
        };

        if (tl.Y == br.Y) br.Y++;

        ctx.beginPath();

        if (parus.parus8.nodeField(edge.info, "NPASSED") == "1") {
          ctx.strokeStyle = passedEdgeColor;
          ctx.fillStyle = passedEdgeColor;
          ctx.setLineDash([]);
          ctx.lineWidth = 2;
        }
        else {
          ctx.strokeStyle = edgeColor;
          ctx.fillStyle = edgeColor;
          ctx.setLineDash([10, 5]);
          ctx.lineWidth = 2;
        }

        ctx.moveTo(tl.X, tl.Y);
        ctx.lineTo(br.X, br.Y);
        ctx.stroke();

        ctx.beginPath();
        ctx.lineWidth = 1;
        ctx.moveTo(br.X, br.Y);
        ctx.lineTo(
        br.X - Math.round(L * (Math.sqrt(3) * LCos - LSin) / 2), br.Y - Math.round(L * (Math.sqrt(3) * LSin + LCos) / 2));
        ctx.lineTo(
        br.X - Math.round(L * (Math.sqrt(3) * LCos + LSin) / 2), br.Y - Math.round(L * (Math.sqrt(3) * LSin - LCos) / 2));
        ctx.fill();

        if (parus.parus8.nodeField(edge.info, "NAUTOMATED") == "1") {
          var cx = (tl.X + br.X + 16) / 2,
               // center x
              
              cy = (tl.Y + br.Y + 16) / 2,
               // center y
              
              notches = 7,
               // num. of notches
              
              radiusO = 8,
               // outer radius
              
              radiusI = 5,
               // inner radius
              
              radiusH = 3,
              taperO = 50,
               // outer taper %
              
              taperI = 35,
               // inner taper %
              
              
              
              
              // pre-calculate values for loop
              
              
              
              pi2 = 2 * Math.PI,
               // cache 2xPI (360deg)
              
              angle = pi2 / (notches * 2),
               // angle between notches
              
              taperAI = angle * taperI * 0.005,
               // inner taper offset (100% = half notch)
              
              taperAO = angle * taperO * 0.005,
               // outer taper offset
              
              a = angle,
               // iterator (angle)
              
              toggle = false; // notch radius level (i/o)

          // move to starting point
          ctx.moveTo(cx + radiusO * Math.cos(taperAO), cy + radiusO * Math.sin(taperAO));

          // loop
          for (; a <= pi2; a += angle) {

            // draw inner to outer line
            if (toggle) {
              ctx.lineTo(cx + radiusI * Math.cos(a - taperAI), cy + radiusI * Math.sin(a - taperAI));
              ctx.lineTo(cx + radiusO * Math.cos(a + taperAO), cy + radiusO * Math.sin(a + taperAO));
            }

            // draw outer to inner line
            else {
              ctx.lineTo(cx + radiusO * Math.cos(a - taperAO), // outer line
              cy + radiusO * Math.sin(a - taperAO));
              ctx.lineTo(cx + radiusI * Math.cos(a + taperAI), // inner line
              cy + radiusI * Math.sin(a + taperAI));
            }

            // switch level
            toggle = !toggle;
          }

          // close the final line
          ctx.closePath();


          // Punch hole
          ctx.moveTo(cx + radiusH, cy);
          ctx.arc(cx, cy, radiusH, 0, pi2);

          // now fill using even-odd rule
          //ctx.fillStyle = '#aaa';
          ctx.fill("evenodd");

          // stroke
          ctx.lineWidth = 2;
          //ctx.strokeStyle = '#000';
          ctx.stroke();

        };


      }));

      this.checkMenu();

    }));
  },

  onMouseDown: function(e) {
    if (!e.target) return;

    dojo.query(".statusModelPointSelected", this.dialog.containerNode).removeClass("statusModelPointSelected");

    var point = e.target.getAttribute("pointid");
    var node = point ? this._nodesByPoint[point] : null;

    if (node) {
      dojo.addClass(node.domNode, "statusModelPointSelected");
      this.selected = node;
    }
    else {
      this.selected = null;
    }

    this.checkMenu();
  },

  checkMenu: function() {
    var isPassed = !! this.selected && parus.parus8.nodeField(this.selected.info, "NPASSED_POINT") == "1";
    var isCurrentStatus = !! this.selected && parus.parus8.nodeField(this.selected.info, "NCURRENT_POINT") == "1";
    var isNextStatus = !! this.selected && parus.parus8.nodeField(this.selected.info, "NIS_NEXT_POINT") == "1";
    var isTogetherSign = !! this.selected && parus.parus8.nodeField(this.selected.info, "TOGETHER_SIGN") == "1";

    this.passMenuItem.attr("disabled", !! !this.selected || !(isCurrentStatus || isNextStatus));
    this.sendMenuItem.attr("disabled", !! !this.selected || !isCurrentStatus);
    this.setMarkMenuItem.attr("disabled", !! !this.selected || !isCurrentStatus || !isTogetherSign);
    this.removeMarkMenuItem.attr("disabled", !! !this.selected || !isCurrentStatus || !isTogetherSign);
    this.executorsMenuItem.attr("disabled", !! !this.selected);
    this.сoWorkingMenuItem.attr("disabled", !! !this.selected || !isTogetherSign);

    parus.parus8.checkMenuSeparators(this.popupMenu);

  },

  pass: function() {
    if (!this.selected) return;

    var statusCode = parus.parus8.nodeField(this.selected.info, "EVNSTAT_CODE");
    var isCurrentStatus = parus.parus8.nodeField(this.selected.info, "NCURRENT_POINT") == "1";
    var isNextStatus = parus.parus8.nodeField(this.selected.info, "NIS_NEXT_POINT") == "1";

    if (!isCurrentStatus && !isNextStatus) return;

    if (isCurrentStatus) {
      parus.balance.statusModelPass(this.documentRN, dojo.hitch(this, function() {
        this.refresh(true);
      }));
    }
    else {
      var rows = parus.parus8.rowNodes(parus.database.query("BalanceStatusModelPassSpecifiedStatus", {
        NPOINT: this.event.point,
        SEVENT_STATUS: statusCode
      }));

      //assert
      if (rows.length == 0) {
        parus.parus8.errorMessage("Нельзя Перейти в выбранную точку - она не не имеет связей с текущим статусом.");
        return;
      }

      //var needNoteOnReturn = parus.parus8.nodeField(rows[0], "ADDNOTE_ONRET") == "1";???
      var needNoteOnChangeState = parus.parus8.nodeField(rows[0], "ADDNOTE_ONCHST") == "1";
      var needNoteOnChangeStateOfPoint = parus.parus8.nodeField(rows[0], "NEXT_ADDNOTE_ONCHST") == "1";
      var needNote = /*needNoteOnReturn ||*/
      needNoteOnChangeState || needNoteOnChangeStateOfPoint;

      var sendToDefaultExecutor = parus.parus8.nodeField(rows[0], "DEFAULT_EXEC") == "1";

      parus.balance._statusModelPassToSpecifiedStatus(this.documentRN, this.event, statusCode, needNote, sendToDefaultExecutor, dojo.hitch(this, function() {
        this.refresh(true);
      }));
    }
  },

  send: function() {
    if (!this.selected) return;

    var isCurrentStatus = parus.parus8.nodeField(this.selected.info, "NCURRENT_POINT") == "1";

    if (!isCurrentStatus) return;

    parus.balance.statusModelSend(this.documentRN, this.event, dojo.hitch(this, function() {
      this.refresh(true);
    }));
  },

  addNote: function() {
    parus.balance.statusModelAddNote(this.documentRN, this.event);
  },

  setMark: function() {
    if (!this.selected) return;

    var isCurrentStatus = parus.parus8.nodeField(this.selected.info, "NCURRENT_POINT") == "1";

    if (!isCurrentStatus) return;

    parus.balance.statusModelSetMark(this.documentRN, this.event);
  },

  removeMark: function() {
    if (!this.selected) return;

    var isCurrentStatus = parus.parus8.nodeField(this.selected.info, "NCURRENT_POINT") == "1";

    if (!isCurrentStatus) return;

    parus.balance.statusModelRemoveMark(this.documentRN, this.event, true);
  },

  notes: function() {
    parus.balance.statusModelNotes(this.documentRN, this.event);
  },

  executors: function() {
    if (!this.selected) return;

    var statusCode = parus.parus8.nodeField(this.selected.info, "EVNSTAT_CODE");

    parus.balance._selectStatusModelPointExecutors(this.event, statusCode, false, null);
  },

  сoWorking: function() {
    if (!this.selected) return;

    var isTogetherSign = parus.parus8.nodeField(this.selected.info, "TOGETHER_SIGN") == "1";

    if (!isTogetherSign) return;

    parus.balance.statusModelCoWorking(this.documentRN, this.event, true);
  },

  history: function() {
    parus.balance.statusModelHistory(this.documentRN);
  }

});