/**
   * Adds a "grass" component to the map, aka a null component Useful if the user wants to delete a
   * map component they placed in the map
   *
   * @param x the x coordinate where to add the grass
   * @param y the y coordinate where to add the grass
   * @param map the map to add the grass to
   * @param mapGridGUIDecorator the GUI decorator associated with this map
   * @param mapGridPane the gridPane that would need to be updated with the new view
   * @param imgView the associated image to place in the x,y cell
   */
  private void addGrass(
      int x,
      int y,
      Map map,
      MapGridGUIDecorator mapGridGUIDecorator,
      GridPane mapGridPane,
      ImageView imgView) {
    Coordinate coord = new Coordinate(x, y);
    map.clearCell(coord);

    StackPane sp = mapGridGUIDecorator.redrawCell(x, y, mapGridPane);

    sp.setOnMouseClicked(
        click -> {
          ComponentType currentFocused = MapMakerController.getCurrentFocused();
          if (currentFocused == ComponentType.INTERSECTION) {
            addIntersection(x, y, map, mapGridGUIDecorator, mapGridPane, intersectionImgView);
          } else if (currentFocused == ComponentType.ROADNS) {
            addRoadNS(x, y, map, mapGridGUIDecorator, mapGridPane, roadNSImgView);
          } else if (currentFocused == ComponentType.ROADEW) {
            addRoadEW(x, y, map, mapGridGUIDecorator, mapGridPane, roadEWImgView);
          }
        });

    // put focus back on Grass
    MapMakerController.setPreviousFocused(MapMakerController.getCurrentFocused());
    MapMakerController.setCurrentFocused(ComponentType.GRASS);
    imgView.requestFocus();
  }
  /**
   * Adds a section of road with 2 lanes that travels in the directions east and west
   *
   * @param x the x coordinate where to add the road
   * @param y the y coordinate where to add the road
   * @param map the map to add the road to
   * @param mapGridGUIDecorator the GUI decorator associated with this map
   * @param mapGridPane the gridPane that would need to be updated with the new view
   * @param imgView the associated image to place in the x,y cell
   */
  private void addRoadEW(
      int x,
      int y,
      Map map,
      MapGridGUIDecorator mapGridGUIDecorator,
      GridPane mapGridPane,
      ImageView imgView) {
    Coordinate coord = new Coordinate(x, y);
    Road road = new Road(coord, coord);
    try {
      road.addLane(new Lane(coord, coord, MapDirection.EAST));
      road.addLane(new Lane(coord, coord, MapDirection.WEST));
      map.addRoad(road);
      StackPane sp = mapGridGUIDecorator.redrawCell(x, y, mapGridPane);

      sp.setOnMouseClicked(
          click -> {
            ComponentType currentFocused = MapMakerController.getCurrentFocused();
            if (currentFocused == ComponentType.INTERSECTION) {
              addIntersection(x, y, map, mapGridGUIDecorator, mapGridPane, intersectionImgView);
            } else if (currentFocused == ComponentType.ROADNS) {
              addRoadNS(x, y, map, mapGridGUIDecorator, mapGridPane, roadNSImgView);
            } else if (currentFocused == ComponentType.GRASS) {
              addGrass(x, y, map, mapGridGUIDecorator, mapGridPane, grassImgView);
            }
          });

      // put focus back on RoadEW
      MapMakerController.setPreviousFocused(MapMakerController.getCurrentFocused());
      MapMakerController.setCurrentFocused(ComponentType.ROADEW);
      imgView.requestFocus();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  @FXML
  public void initialize() {

    StackPane.setAlignment(tfAmountDue, Pos.CENTER_LEFT);
    StackPane.setAlignment(tfAmountEntry, Pos.CENTER_RIGHT);

    cancelOption.setOnMouseClicked(
        event ->
            App.confirm.showAndWait(
                "Are you sure? Any changes will not be saved.", App.main::backRefresh));

    sendOption.setOnMouseClicked(
        event -> {
          if (getItem().getProductEntries().isEmpty()) {
            Platform.runLater(() -> App.notify.showAndWait("Nothing to send."));

          } else if (getItem().changeProperty().get().compareTo(BigDecimal.ZERO) < 0) {
            Platform.runLater(
                () -> App.notify.showAndWait("Payment Due : " + getItem().dueProperty().get()));

          } else if (getItem()
              .getProductEntries()
              .stream()
              .anyMatch(
                  pe ->
                      pe.hasStatus(ProductEntryStatus.HOLD)
                          || pe.hasStatus(ProductEntryStatus.REQUEST_HOLD))) {
            Platform.runLater(() -> App.notify.showAndWait("Can't close orders with held items."));

          } else {
            SalesOrderStatus prevStatus = getItem().getStatus();
            getItem().setStatus(SalesOrderStatus.REQUEST_CLOSE);
            App.apiProxy.postSalesOrder(
                getItem(),
                new RunLaterCallback<Long>() {
                  @Override
                  public void laterSuccess(Long aLong) {
                    getItem().setId(aLong);
                    App.main.setSwapRefresh(App.changeDuePresenter, getItem());

                    // Print takeout receipt on pay
                    if (App.properties.getBool("print-pay-takeout")
                        && getItem().hasType(SalesOrderType.TAKEOUT))
                      App.dispatcher.requestPrint("receipt", App.jobBuilder.check(getItem(), true));

                    // Print dinein receipt on pay
                    else if (App.properties.getBool("print-pay-dinein")
                        && getItem().hasType(SalesOrderType.DINEIN))
                      App.dispatcher.requestPrint("receipt", App.jobBuilder.check(getItem(), true));

                    // TODO : Remove hardcoded scenario - print receipt for take out orders that pay
                    // immediately
                    else if (getItem().hasType(SalesOrderType.TAKEOUT)
                        && getItem().hasStatus(SalesOrderStatus.REQUEST_CLOSE)
                        && prevStatus == SalesOrderStatus.REQUEST_OPEN)
                      App.dispatcher.requestPrint("receipt", App.jobBuilder.check(getItem(), true));
                  }

                  @Override
                  public void laterFailure(ErrorInfo error) {
                    super.laterFailure(error);
                    getItem().setStatus(prevStatus);
                  }
                });
          }
        });

    gratuityOption.setOnMouseClicked(
        event -> {
          if (getItem().canHaveGratuity() && !getItem().hasGratuity()) {
            getItem().setGratuityPercent(App.properties.getBd("gratuity-percent"));
          } else {
            getItem().setGratuityPercent(BigDecimal.ZERO);
          }
        });

    printOption.setOnMouseClicked(
        event ->
            Platform.runLater(
                () -> {
                  if (getItem().canPrint()) {
                    App.main.backRefresh();
                    App.dispatcher.requestPrint("receipt", App.jobBuilder.check(getItem(), false));
                  } else {
                    App.notify.showAndWait("Changes must be sent before printing.");
                  }
                }));

    voidOption.setOnMouseClicked(
        event -> {
          if (getItem().hasStatus(SalesOrderStatus.VOID)
              || getItem().hasStatus(SalesOrderStatus.CLOSED)) {
            App.confirm.showAndWait(
                "Reopen Sales Order?",
                () -> {
                  SalesOrderStatus prevStatus = getItem().getStatus();
                  getItem().setStatus(SalesOrderStatus.REQUEST_OPEN);

                  App.apiProxy.postSalesOrder(
                      getItem(),
                      new RunLaterCallback<Long>() {
                        @Override
                        public void laterSuccess(Long aLong) {
                          App.notify.showAndWait("Sales Order " + aLong + " Reopened.");
                          App.main.backRefresh();
                        }

                        @Override
                        public void laterFailure(ErrorInfo error) {
                          super.laterFailure(error);
                          getItem().setStatus(prevStatus);
                        }
                      });
                });
          } else {
            App.confirm.showAndWait(
                "Void Sales Order?",
                () -> {
                  SalesOrderStatus prevStatus = getItem().getStatus();
                  getItem().setStatus(SalesOrderStatus.REQUEST_VOID);

                  App.apiProxy.postSalesOrder(
                      getItem(),
                      new RunLaterCallback<Long>() {
                        @Override
                        public void laterSuccess(Long aLong) {
                          Platform.runLater(
                              () -> {
                                App.main.backRefresh();
                                App.notify.showAndWait("Sales Order " + aLong + " Voided.");
                              });
                        }

                        @Override
                        public void laterFailure(ErrorInfo error) {
                          super.laterFailure(error);
                          getItem().setStatus(prevStatus);
                        }
                      });
                });
          }
        });

    lvChargeEntries.setCellFactory(
        param -> {
          ViewChargeEntry presenter = Presenter.load("/view/order/view_charge_entry.fxml");
          presenter.fixWidth(lvChargeEntries);
          presenter.onClick(
              event ->
                  Platform.runLater(
                      () -> {
                        ChargeEntry ce = presenter.getItem();

                        if (ce.hasStatus(ChargeEntryStatus.REQUEST_APPLY)) {
                          getItem().chargeEntriesProperty().remove(ce);

                        } else if (ce.hasStatus(ChargeEntryStatus.APPLIED)
                            && App.employee.hasPermission(Permission.VOID_CHARGE_ENTRY)) {
                          ce.setStatus(ChargeEntryStatus.REQUEST_VOID);

                        } else if (ce.hasStatus(ChargeEntryStatus.REQUEST_VOID)) {
                          ce.setStatus(ChargeEntryStatus.APPLIED);
                        }
                      }));
          return new PresenterCellAdapter<>(presenter);
        });

    lvPaymentEntries.setCellFactory(
        param -> {
          ViewPaymentEntry presenter = Presenter.load("/view/order/view_payment_entry.fxml");
          presenter.fixWidth(lvPaymentEntries);
          presenter.onClick(
              event ->
                  Platform.runLater(
                      () -> {
                        PaymentEntry pe = presenter.getItem();

                        if (pe.hasStatus(PaymentEntryStatus.REQUEST_PAID)) {
                          getItem().paymentEntriesProperty().remove(pe);

                        } else if (pe.hasStatus(PaymentEntryStatus.PAID)
                            && App.employee.hasPermission(Permission.VOID_PAYMENT_ENTRY)) {
                          pe.setStatus(PaymentEntryStatus.REQUEST_VOID);

                        } else if (pe.hasStatus(PaymentEntryStatus.REQUEST_VOID)) {
                          pe.setStatus(PaymentEntryStatus.PAID);
                        }
                      }));
          return new PresenterCellAdapter<>(presenter);
        });

    lvCharges.setCellFactory(
        param -> {
          ViewCharge presenter = Presenter.load("/view/order/view_charge.fxml");
          presenter.fixWidth(lvCharges);
          presenter.onClick(
              event ->
                  Platform.runLater(
                      () -> {
                        ChargeEntry entry = new ChargeEntry(presenter.getItem());
                        lvChargeEntries.getItems().add(entry);
                      }));
          return new PresenterCellAdapter<>(presenter);
        });
  }
 private void disableKeypad() {
   unsetTextProperty(tfAmountEntry.textProperty(), "Disabled");
   btnEvenTender.setOnMouseClicked(null);
   btnClear.setOnMouseClicked(null);
   btn00.setOnMouseClicked(null);
   btn0.setOnMouseClicked(null);
   btn1.setOnMouseClicked(null);
   btn2.setOnMouseClicked(null);
   btn3.setOnMouseClicked(null);
   btn4.setOnMouseClicked(null);
   btn5.setOnMouseClicked(null);
   btn6.setOnMouseClicked(null);
   btn7.setOnMouseClicked(null);
   btn8.setOnMouseClicked(null);
   btn9.setOnMouseClicked(null);
   btnCash.setOnMouseClicked(null);
   btnCredit.setOnMouseClicked(null);
   btnCheck.setOnMouseClicked(null);
   btnGiftCard.setOnMouseClicked(null);
 }
 private void enableKeypad() {
   setTextProperty(tfAmountEntry.textProperty(), amountFormatter);
   btnClear.setOnMouseClicked(event -> rawAmount.set(""));
   btn00.setOnMouseClicked(event -> rawAmount.set(rawAmount.get() + "00"));
   btn0.setOnMouseClicked(event -> rawAmount.set(rawAmount.get() + "0"));
   btn1.setOnMouseClicked(event -> rawAmount.set(rawAmount.get() + "1"));
   btn2.setOnMouseClicked(event -> rawAmount.set(rawAmount.get() + "2"));
   btn3.setOnMouseClicked(event -> rawAmount.set(rawAmount.get() + "3"));
   btn4.setOnMouseClicked(event -> rawAmount.set(rawAmount.get() + "4"));
   btn5.setOnMouseClicked(event -> rawAmount.set(rawAmount.get() + "5"));
   btn6.setOnMouseClicked(event -> rawAmount.set(rawAmount.get() + "6"));
   btn7.setOnMouseClicked(event -> rawAmount.set(rawAmount.get() + "7"));
   btn8.setOnMouseClicked(event -> rawAmount.set(rawAmount.get() + "8"));
   btn9.setOnMouseClicked(event -> rawAmount.set(rawAmount.get() + "9"));
   btnCash.setOnMouseClicked(event -> addPaymentEntry(PaymentEntryType.CASH));
   btnCredit.setOnMouseClicked(event -> addPaymentEntry(PaymentEntryType.CREDIT));
   btnCheck.setOnMouseClicked(event -> addPaymentEntry(PaymentEntryType.CHECK));
   btnGiftCard.setOnMouseClicked(event -> addPaymentEntry(PaymentEntryType.GIFTCARD));
   btnEvenTender.setOnMouseClicked(
       event -> rawAmount.set(getItem().dueProperty().get().toString().replace(".", "")));
 }