package com.ecep.contract.controller.project.cost;

import java.text.NumberFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import org.controlsfx.control.textfield.AutoCompletionBinding;
import org.controlsfx.control.textfield.TextFields;
import org.springframework.util.StringUtils;

import com.ecep.contract.Desktop;
import com.ecep.contract.controller.inventory.InventoryWindowController;
import com.ecep.contract.controller.tab.TabSkin;
import com.ecep.contract.controller.table.AbstEntityTableTabSkin;
import com.ecep.contract.controller.table.cell.EmployeeTableCell;
import com.ecep.contract.controller.table.cell.LocalDateTimeTableCell;
import com.ecep.contract.converter.EntityStringConverter;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.Inventory;
import com.ecep.contract.model.Price;
import com.ecep.contract.model.ProjectCost;
import com.ecep.contract.model.ProjectCostItem;
import com.ecep.contract.service.ContractItemService;
import com.ecep.contract.service.ContractService;
import com.ecep.contract.service.InventoryService;
import com.ecep.contract.service.ProjectCostItemService;
import com.ecep.contract.service.ProjectCostService;
import com.ecep.contract.util.FxmlPath;
import com.ecep.contract.util.ProxyUtils;
import com.ecep.contract.util.UITools;
import com.ecep.contract.vm.InventoryViewModel;
import com.ecep.contract.vm.ProjectCostItemViewModel;
import com.ecep.contract.vm.ProjectCostViewModel;

import javafx.application.Platform;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.Property;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Cell;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.Tab;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.input.KeyCode;
import javafx.util.converter.CurrencyStringConverter;
import javafx.util.converter.NumberStringConverter;
import javafx.util.converter.PercentageStringConverter;
import lombok.Setter;

@FxmlPath("/ui/project/project-cost-tab-item.fxml")
public class ProjectCostTabSkinItems
        extends
        AbstEntityTableTabSkin<ProjectCostWindowController, ProjectCost, ProjectCostViewModel, ProjectCostItem, ProjectCostItemViewModel>
        implements TabSkin {

    public TableColumn<ProjectCostItemViewModel, Number> idColumn;
    public TableColumn<ProjectCostItemViewModel, String> titleColumn;
    public TableColumn<ProjectCostItemViewModel, String> specificationColumn;
    public TableColumn<ProjectCostItemViewModel, String> unitColumn;
    public TableColumn<ProjectCostItemViewModel, Number> inQuantityColumn;
    public TableColumn<ProjectCostItemViewModel, Number> inExclusiveTaxPriceColumn;
    public TableColumn<ProjectCostItemViewModel, Number> inExclusiveTaxAmountColumn;
    public TableColumn<ProjectCostItemViewModel, Number> inTaxRateColumn;
    public TableColumn<ProjectCostItemViewModel, Number> inTaxPriceColumn;
    public TableColumn<ProjectCostItemViewModel, Number> inTaxAmountColumn;
    public TableColumn<ProjectCostItemViewModel, Number> outQuantityColumn;
    public TableColumn<ProjectCostItemViewModel, Number> outExclusiveTaxPriceColumn;
    public TableColumn<ProjectCostItemViewModel, Number> outExclusiveTaxAmountColumn;
    public TableColumn<ProjectCostItemViewModel, Number> outTaxRateColumn;
    public TableColumn<ProjectCostItemViewModel, Number> outTaxPriceColumn;
    public TableColumn<ProjectCostItemViewModel, Number> outTaxAmountColumn;
    public TableColumn<ProjectCostItemViewModel, Number> grossProfitColumn;
    public TableColumn<ProjectCostItemViewModel, Number> grossProfitMarginColumn;
    public TableColumn<ProjectCostItemViewModel, Employee> creatorColumn;
    public TableColumn<ProjectCostItemViewModel, LocalDateTime> createDateColumn;
    public TableColumn<ProjectCostItemViewModel, Employee> updaterColumn;
    public TableColumn<ProjectCostItemViewModel, LocalDateTime> updateDateColumn;
    public TableColumn<ProjectCostItemViewModel, String> remarkColumn;
    public TableColumn<ProjectCostItemViewModel, Inventory> inventoryColumn;

    public Button importFromSalesContractBtn;
    public ToggleButton lockPriceMethodBtn;
    public CheckBox importLockCb;
    public ToggleButton switchUnTaxPriceColumnBtn;
    public ToggleButton switchTaxPriceColumnBtn;
    public Label inTaxAmountLabel;
    public Label inExclusiveTaxAmountLabel;
    public Label outTaxAmountLabel;
    public Label outExclusiveTaxAmountLabel;

    @Setter
    private ProjectCostItemService costItemService;
    @Setter
    private ContractService contractService;
    @Setter
    private ContractItemService contractItemService;
    @Setter
    private InventoryService inventoryService;

    public ProjectCostTabSkinItems(ProjectCostWindowController controller) {
        super(controller);
    }

    ProjectCostService getCostService() {
        return controller.getViewModelService();
    }

    ProjectCostItemService getCostItemService() {
        if (costItemService == null) {
            costItemService = getBean(ProjectCostItemService.class);
        }
        return costItemService;
    }

    ContractService getContractService() {
        if (contractService == null) {
            contractService = getBean(ContractService.class);
        }
        return contractService;
    }

    ContractItemService getContractItemService() {
        if (contractItemService == null) {
            contractItemService = getBean(ContractItemService.class);
        }
        return contractItemService;
    }

    InventoryService getInventoryService() {
        if (inventoryService == null) {
            inventoryService = getBean(InventoryService.class);
        }
        return inventoryService;
    }

    @Override
    public Tab getTab() {
        return controller.itemTab;
    }

    @Override
    protected ProjectCostItemService getViewModelService() {
        return getCostItemService();
    }

    @Override
    public List<ProjectCostItemViewModel> loadTableData(ProjectCost parent) {
        List<ProjectCostItemViewModel> models = super.loadTableData(parent);
        Platform.runLater(() -> up(models));
        return models;
    }

    @Override
    public Map<String, Object> getSpecification(ProjectCost parent) {
        Map<String, Object> params = getSpecification();
        params.put("cost", parent);
        return params;
    }

    static class AutoCompletionInventoryTableCell extends TableCell<ProjectCostItemViewModel, Inventory> {
        private TextField textField;
        @Setter
        private EntityStringConverter<Inventory> converter;

        public AutoCompletionInventoryTableCell() {
        }

        public AutoCompletionInventoryTableCell(EntityStringConverter<Inventory> converter) {
            this();
            this.converter = converter;
        }

        @Override
        public void startEdit() {
            super.startEdit();
            if (!isEditing()) {
                return;
            }

            if (textField == null) {
                textField = createTextField(this, converter);
                AutoCompletionBinding<Inventory> binding = TextFields.bindAutoCompletion(textField, converter::suggest,
                        converter);
                binding.setOnAutoCompleted(event -> {
                    commitEdit(event.getCompletion());
                });
            }

            startEdit(this, converter, textField);
        }

        @Override
        public void cancelEdit() {
            super.cancelEdit();
            cancelEdit(this, converter, null);
        }

        @Override
        public void updateItem(Inventory item, boolean empty) {
            super.updateItem(item, empty);
            if (empty || item == null) {
                setText("");
                setGraphic(null);
                return;
            }
            if (isEditing()) {
                if (textField != null) {
                    textField.setText(getItemText(this, converter));
                }
                setText(null);
                setGraphic(textField);
            } else {
                setText(getItemText(this, converter));
                setGraphic(null);
            }
        }

        private static String getItemText(Cell<Inventory> cell, EntityStringConverter<Inventory> converter) {
            Inventory inventory = converter.prefixObject(cell.getItem());
            return inventory == null ? "-"
                    : StringUtils.hasText(inventory.getCode()) ? inventory.getCode() : ("#" + inventory.getId());
        }

        static TextField createTextField(final Cell<Inventory> cell, final EntityStringConverter<Inventory> converter) {
            final TextField textField = new TextField(getItemText(cell, converter));
            textField.setOnKeyReleased(t -> {
                if (t.getCode() == KeyCode.ESCAPE) {
                    cell.cancelEdit();
                    t.consume();
                    return;
                }
                if (t.getCode() == KeyCode.ENTER) {
                    cell.commitEdit(converter.fromString(textField.getText()));
                    t.consume();
                }
            });

            return textField;
        }

        static void startEdit(final Cell<Inventory> cell,
                final EntityStringConverter<Inventory> converter,
                final TextField textField) {
            if (textField != null) {
                textField.setText(getItemText(cell, converter));
            }
            cell.setText(null);
            cell.setGraphic(textField);
            textField.selectAll();
            // requesting focus so that key input can immediately go into the
            // TextField (see RT-28132)
            textField.requestFocus();
        }

        static void cancelEdit(Cell<Inventory> cell, final EntityStringConverter<Inventory> converter, Node graphic) {
            cell.setText(getItemText(cell, converter));
            cell.setGraphic(graphic);
        }
    }

    @Override
    public void initializeTab() {
        super.initializeTab();
        table.setEditable(true);

        NumberFormat numberInstance = NumberFormat.getNumberInstance(getLocale());
        numberInstance.setMaximumFractionDigits(2);
        numberInstance.setMinimumFractionDigits(2);
        CurrencyStringConverter currencyStringConverter = new CurrencyStringConverter(numberInstance);
        Platform.runLater(() -> {

            importLockCb.selectedProperty().bindBidirectional(viewModel.getImportLock());
            importFromSalesContractBtn.disableProperty().bind(viewModel.getImportLock());
            lockPriceMethodBtn.textProperty()
                    .bind(lockPriceMethodBtn.selectedProperty().map(v -> v ? "锁定不含税单价" : "锁定含税单价"));
            table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

            BooleanBinding taxPriceVisible = switchTaxPriceColumnBtn.selectedProperty().not();
            inTaxPriceColumn.visibleProperty().bind(taxPriceVisible);
            inTaxAmountColumn.visibleProperty().bind(taxPriceVisible);
            outTaxPriceColumn.visibleProperty().bind(taxPriceVisible);
            outTaxAmountColumn.visibleProperty().bind(taxPriceVisible);
            BooleanBinding unTaxPriceVisible = switchUnTaxPriceColumnBtn.selectedProperty().not();
            outExclusiveTaxPriceColumn.visibleProperty().bind(unTaxPriceVisible);
            outExclusiveTaxAmountColumn.visibleProperty().bind(unTaxPriceVisible);
            inExclusiveTaxPriceColumn.visibleProperty().bind(unTaxPriceVisible);
            inExclusiveTaxAmountColumn.visibleProperty().bind(unTaxPriceVisible);

            inTaxAmountLabel.textProperty().bind(viewModel.getInTaxAmount().map(currencyStringConverter::toString));
            inExclusiveTaxAmountLabel.textProperty()
                    .bind(viewModel.getInExclusiveTaxAmount().map(currencyStringConverter::toString));
            outTaxAmountLabel.textProperty().bind(viewModel.getOutTaxAmount().map(currencyStringConverter::toString));
            outExclusiveTaxAmountLabel.textProperty()
                    .bind(viewModel.getOutExclusiveTaxAmount().map(currencyStringConverter::toString));
        });

        importFromSalesContractBtn.setOnAction(this::importFromSalesContractAction);

        idColumn.setCellValueFactory(param -> param.getValue().getId());
        titleColumn.setCellValueFactory(param -> param.getValue().getTitle());
        titleColumn.setCellFactory(TextFieldTableCell.forTableColumn());
        titleColumn.setOnEditCommit(event -> changed(event, ProjectCostItemViewModel::getTitle));

        specificationColumn.setCellValueFactory(param -> param.getValue().getSpecification());
        specificationColumn.setCellFactory(TextFieldTableCell.forTableColumn());
        specificationColumn.setOnEditCommit(event -> changed(event, ProjectCostItemViewModel::getSpecification));

        unitColumn.setCellValueFactory(param -> param.getValue().getUnit());
        unitColumn.setCellFactory(TextFieldTableCell.forTableColumn());
        unitColumn.setOnEditCommit(event -> changed(event, ProjectCostItemViewModel::getUnit));

        EntityStringConverter<Inventory> converter = new EntityStringConverter<>();
        converter.setInitialized(v -> getInventoryService().findById(v.getId()));
        converter.setSuggestion(getInventoryService()::search);
        // converter.setFormater(Inventory::getCode);
        converter.setFromString(code -> {
            return getInventoryService().findByCode(code);
        });
        inventoryColumn.setCellValueFactory(param -> param.getValue().getInventory());
        inventoryColumn.setCellFactory(param -> new AutoCompletionInventoryTableCell(converter));

        inventoryColumn.comparatorProperty()
                .set(Comparator.comparing(converter::toString, Comparator.nullsLast(Comparator.naturalOrder())));
        inventoryColumn.setOnEditCommit(this::inventoryColumnEditCommit);

        initializeNumberColumn(inQuantityColumn, ProjectCostItemViewModel::getInQuantity);
        inQuantityColumn.setOnEditCommit(event -> {
            ProjectCostItemViewModel row = event.getRowValue();
            row.updateInQuantity(event.getNewValue().doubleValue());
            changed(row);
        });
        initializeNumberColumn(inTaxRateColumn, ProjectCostItemViewModel::getInTaxRate);
        ContextMenu inTaxRateMenu = new ContextMenu();
        MenuItem menuItem1 = new MenuItem("全部修改为13%");
        menuItem1.setOnAction(event -> {
            for (ProjectCostItemViewModel row : dataSet) {
                row.updateInTaxRate(13);
                changed(row);
            }
        });

        MenuItem menuItem2 = new MenuItem("全部修改为11%");
        menuItem2.setOnAction(event -> {
            for (ProjectCostItemViewModel row : dataSet) {
                row.updateInTaxRate(11);
                changed(row);
            }
        });
        MenuItem menuItem3 = new MenuItem("全部修改为9%");
        menuItem3.setOnAction(event -> {
            for (ProjectCostItemViewModel row : dataSet) {
                row.updateInTaxRate(9);
                changed(row);
            }
        });
        MenuItem menuItem4 = new MenuItem("全部修改为6%");
        menuItem3.setOnAction(event -> {
            for (ProjectCostItemViewModel row : dataSet) {
                row.updateInTaxRate(6);
                changed(row);
            }
        });

        inTaxRateMenu.getItems().addAll(menuItem1, menuItem2, menuItem3, menuItem4);

        inTaxRateColumn.setContextMenu(inTaxRateMenu);
        inTaxRateColumn.setOnEditCommit(event -> {
            ProjectCostItemViewModel row = event.getRowValue();
            row.getUpdatePriceWay().set(lockPriceMethodBtn.isSelected());
            row.updateInTaxRate(event.getNewValue().doubleValue());
            changed(row);
        });
        initializeNumberColumn(inTaxPriceColumn, ProjectCostItemViewModel::getInTaxPrice);
        inTaxPriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
        inTaxPriceColumn.setOnEditCommit(event -> {
            ProjectCostItemViewModel row = event.getRowValue();
            row.updateInTaxPrice(event.getNewValue().doubleValue());
            changed(row);
        });

        inTaxAmountColumn.setCellValueFactory(param -> param.getValue().getInTaxAmount());
        inTaxAmountColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
        inTaxAmountColumn.setEditable(false);

        initializeNumberColumn(inExclusiveTaxPriceColumn, ProjectCostItemViewModel::getInExclusiveTaxPrice);
        inExclusiveTaxPriceColumn.setOnEditCommit(event -> {
            ProjectCostItemViewModel row = event.getRowValue();
            row.updateInExclusiveTaxPrice(event.getNewValue().doubleValue());
            changed(row);
        });

        inExclusiveTaxAmountColumn.setCellValueFactory(param -> param.getValue().getInExclusiveTaxAmount());
        inExclusiveTaxAmountColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
        inExclusiveTaxAmountColumn.setEditable(false);

        initializeNumberColumn(outQuantityColumn, ProjectCostItemViewModel::getOutQuantity);
        outQuantityColumn.setOnEditCommit(event -> {
            ProjectCostItemViewModel row = event.getRowValue();
            row.updateOutQuantity(event.getNewValue().doubleValue());
            changed(row);
        });
        initializeNumberColumn(outTaxRateColumn, ProjectCostItemViewModel::getOutTaxRate);
        outTaxRateColumn.setOnEditCommit(event -> {
            ProjectCostItemViewModel row = event.getRowValue();
            row.getUpdatePriceWay().set(lockPriceMethodBtn.isSelected());
            row.updateOutTaxRate(event.getNewValue().doubleValue());
            changed(row);
        });
        initializeNumberColumn(outTaxPriceColumn, ProjectCostItemViewModel::getOutTaxPrice);
        outTaxPriceColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
        outTaxPriceColumn.setOnEditCommit(event -> {
            ProjectCostItemViewModel row = event.getRowValue();
            row.updateOutTaxPrice(event.getNewValue().doubleValue());
            changed(row);
        });

        outTaxAmountColumn.setCellValueFactory(param -> param.getValue().getOutTaxAmount());
        outTaxAmountColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
        outTaxAmountColumn.setEditable(false);

        initializeNumberColumn(outExclusiveTaxPriceColumn, ProjectCostItemViewModel::getOutExclusiveTaxPrice);
        outExclusiveTaxPriceColumn.setOnEditCommit(event -> {
            ProjectCostItemViewModel row = event.getRowValue();
            row.updateOutExclusiveTaxPrice(event.getNewValue().doubleValue());
            changed(row);
        });
        outExclusiveTaxAmountColumn.setCellValueFactory(param -> param.getValue().getOutExclusiveTaxAmount());
        outExclusiveTaxAmountColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
        outExclusiveTaxAmountColumn.setEditable(false);

        grossProfitColumn.setCellValueFactory(param -> param.getValue().getGrossProfit());
        grossProfitColumn.setCellFactory(TextFieldTableCell.forTableColumn(currencyStringConverter));
        grossProfitColumn.setEditable(false);

        grossProfitMarginColumn.setCellValueFactory(param -> param.getValue().getGrossProfitMargin());
        grossProfitMarginColumn
                .setCellFactory(TextFieldTableCell.forTableColumn(new PercentageStringConverter(getLocale())));
        grossProfitMarginColumn.setEditable(false);

        creatorColumn.setCellValueFactory(param -> param.getValue().getCreator());
        creatorColumn.setCellFactory(cell -> new EmployeeTableCell<>(getEmployeeService()));
        updaterColumn.setCellValueFactory(param -> param.getValue().getUpdater());
        updaterColumn.setCellFactory(cell -> new EmployeeTableCell<>(getEmployeeService()));
        createDateColumn.setCellValueFactory(param -> param.getValue().getCreateDate());
        createDateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
        updateDateColumn.setCellValueFactory(param -> param.getValue().getUpdateDate());
        updateDateColumn.setCellFactory(param -> new LocalDateTimeTableCell<>());
        remarkColumn.setCellValueFactory(param -> param.getValue().getRemark());
        remarkColumn.setCellFactory(TextFieldTableCell.forTableColumn());
        remarkColumn.setOnEditCommit(event -> {
            ProjectCostItemViewModel row = event.getRowValue();
            row.getRemark().setValue(event.getNewValue());
            changed(row);
        });

    }

    private void inventoryColumnEditCommit(TableColumn.CellEditEvent<ProjectCostItemViewModel, Inventory> event) {
        ProjectCostItemViewModel row = event.getRowValue();
        Inventory inventory = event.getNewValue();
        row.getInventory().set(inventory);
        if (inventory != null) {
            row.getTitle().set(inventory.getName());
            row.getSpecification().set(inventory.getSpecification());
            row.getUnit().set(inventory.getUnit());

            Price salePrice = inventory.getSalePrice();
            if (row.getInTaxRate().get() == 0 && salePrice.getTaxRate() != 0) {
                row.getInTaxRate().set(salePrice.getTaxRate());
            }
            if (row.getInExclusiveTaxPrice().get() == 0 && salePrice.getPreTaxPrice() != 0) {
                row.getInExclusiveTaxPrice().set(salePrice.getPreTaxPrice());
            }
            if (row.getInTaxPrice().get() == 0 && salePrice.getPostTaxPrice() != 0) {
                row.getInTaxPrice().set(salePrice.getPostTaxPrice());
            }
            row.updateIn();

            Price purchasePrice = inventory.getPurchasePrice();
            if (row.getOutTaxRate().get() == 0 && purchasePrice.getTaxRate() != 0) {
                row.getOutTaxRate().set(purchasePrice.getTaxRate());
            }
            if (row.getOutExclusiveTaxPrice().get() == 0 && purchasePrice.getPreTaxPrice() != 0) {
                row.getOutExclusiveTaxPrice().set(purchasePrice.getPreTaxPrice());
            }
            if (row.getOutTaxPrice().get() == 0 && purchasePrice.getPostTaxPrice() != 0) {
                row.getOutTaxPrice().set(purchasePrice.getPostTaxPrice());
            }
            row.updateOut();
        }
        changed(row);
    }

    private void importFromSalesContractAction(ActionEvent event) {
        ProjectCostImportItemsFromContractsTasker tasker = new ProjectCostImportItemsFromContractsTasker();
        tasker.setCost(getEntity());
        UITools.showTaskDialogAndWait("从合同(销售和采购)导入项目成本", tasker, null);
        loadTableDataSet(getEntity());
    }

    private void initializeNumberColumn(TableColumn<ProjectCostItemViewModel, Number> column,
            Function<ProjectCostItemViewModel, Property<Number>> supplier) {
        column.setCellValueFactory(param -> supplier.apply(param.getValue()));
        column.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
        column.setOnEditCommit(event -> changed(event, supplier));
    }

    @Override
    protected void createContextMenu(ContextMenu contextMenu) {
        super.createContextMenu(contextMenu);
        MenuItem compositeInventory = new MenuItem("合并");
        compositeInventory.setOnAction(this::compositeRows);
        MenuItem showInventory = new MenuItem("查看存货");
        showInventory.setOnAction(event -> {
            ProjectCostItemViewModel selectedItem = getTableView().getSelectionModel().getSelectedItem();
            Inventory inventory = selectedItem.getInventory().get();
            if (inventory == null) {
                return;
            }
            if (!ProxyUtils.isInitialized(inventory)) {
                inventory = getInventoryService().findById(inventory.getId());
            }
            showInOwner(InventoryWindowController.class, InventoryViewModel.from(inventory));
        });
        contextMenu.getItems().addAll(compositeInventory, showInventory);
        contextMenu.setOnShowing(event -> {
            ProjectCostItemViewModel selectedItem = getTableView().getSelectionModel().getSelectedItem();
            if (selectedItem == null) {
                showInventory.setDisable(true);
                return;
            }
            Inventory inventory = selectedItem.getInventory().get();
            if (inventory == null) {
                showInventory.setDisable(true);
                return;
            }
            showInventory.setDisable(false);

            compositeInventory.visibleProperty().set(getTableView().getSelectionModel().getSelectedItems().size() > 1);
        });
    }

    /**
     * 合同不同行
     *
     * @param event
     */
    private void compositeRows(ActionEvent event) {
        List<ProjectCostItemViewModel> selectedItems = new ArrayList<>(
                getTableView().getSelectionModel().getSelectedItems());

        double inQuantity = selectedItems.stream().mapToDouble(v -> v.getInQuantity().get()).sum();
        double inAmount = selectedItems.stream().mapToDouble(v -> v.getInTaxAmount().get()).sum();
        double inPrice = inQuantity != 0 ? inAmount / inQuantity : 0;
        double outQuantity = selectedItems.stream().mapToDouble(v -> v.getOutQuantity().get()).sum();
        double outAmount = selectedItems.stream().mapToDouble(v -> v.getOutTaxAmount().get()).sum();
        double outPrice = outQuantity != 0 ? outAmount / outQuantity : 0;
        ProjectCostItemViewModel model = selectedItems.removeFirst();
        model.getInQuantity().set(inQuantity);
        model.updateInTaxPrice(inPrice);
        model.getOutQuantity().set(outQuantity);
        model.updateOutTaxPrice(outPrice);
        model.getUpdater().set(controller.getCurrentUser());
        model.getUpdateDate().set(LocalDateTime.now());
        // saveRow(model);

        for (ProjectCostItemViewModel selectedItem : selectedItems) {
            if (model.getInTaxRate().get() == 0) {
                model.getInTaxRate().set(selectedItem.getInTaxRate().get());
            }
            if (model.getOutTaxRate().get() == 0) {
                model.getOutTaxRate().set(selectedItem.getOutTaxRate().get());
            }
            selectedItem.updateInQuantity(0);
            selectedItem.updateOutQuantity(0);
            selectedItem.getUpdater().set(controller.getCurrentUser());
            selectedItem.getUpdateDate().set(LocalDateTime.now());
            selectedItem.getRemark().set("del");
            // saveRow(selectedItem);
        }
        setChanged(false);
    }

    @Override
    protected ProjectCostItemViewModel createNewViewModel() {
        ProjectCostItemViewModel model = super.createNewViewModel();
        ProjectCost projectCost = getParent();
        model.getCost().set(projectCost);
        model.getCreator().set(getEmployeeService().findById(Desktop.instance.getActiveEmployeeId()));
        model.getCreateDate().set(LocalDateTime.now());
        saveRow(model);
        return model;
    }

    @Override
    public void save() {
        if (!isChanged()) {
            return;
        }

        List<ProjectCostItemViewModel> changedRows = new ArrayList<>(dataSet);

        // 批量保存（内部可能需要优化为真正的批量操作）
        for (ProjectCostItemViewModel row : changedRows) {
            ProjectCostItem entity = loadRowData(row);
            if (entity == null) {
                entity = createNewEntity(row);
            }
            if (row.copyTo(entity)) {
                entity.setUpdater(controller.getCurrentUser());
                entity.setUpdateDate(LocalDateTime.now());
                ProjectCostItem saved = saveRowData(entity);
                row.update(saved);
            }
            if (row.isChanged()) {
                row.setChanged(false);
            }
        }

        setChanged(false);

        up(dataSet);

        ProjectCostTabSkinBase baseSkin = controller.getTabSkin(ProjectCostTabSkinBase.class);
        if (baseSkin.isChanged()) {
            baseSkin.save();
        }
    }

    /**
     * 计算统计数
     *
     * @param models
     */
    public void up(List<ProjectCostItemViewModel> models) {
        // 含税进项总金额
        double inTaxAmount = 0;
        // 不含税进项总金额
        double inExclusiveTaxAmount = 0;
        // 含税出项总金额
        double outTaxAmount = 0;
        // 不含税出项总金额
        double outExclusiveTaxAmount = 0;

        for (ProjectCostItemViewModel row : models) {
            inTaxAmount += row.getInTaxAmount().get();
            inExclusiveTaxAmount += row.getInExclusiveTaxAmount().get();
            outTaxAmount += row.getOutTaxAmount().get();
            outExclusiveTaxAmount += row.getOutExclusiveTaxAmount().get();
        }

        getViewModel().getInTaxAmount().set(inTaxAmount);
        getViewModel().getInExclusiveTaxAmount().set(inExclusiveTaxAmount);
        getViewModel().getOutTaxAmount().set(outTaxAmount);
        getViewModel().getOutExclusiveTaxAmount().set(outExclusiveTaxAmount);

        getViewModel().updateStampTaxFee();
        getViewModel().updateTaxAndSurchargesFee();
        getViewModel().updateGrossProfitMargin();
    }
}
