package mara.mybox.controller;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.Optional;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.control.TextInputDialog;
import javafx.scene.layout.Region;
import javafx.stage.Modality;
import javafx.stage.Stage;
import mara.mybox.data.PdfInformation;
import mara.mybox.data.VisitHistory;
import mara.mybox.fxml.FxmlControl;
import static mara.mybox.fxml.FxmlControl.badStyle;
import mara.mybox.tools.DateTools;
import mara.mybox.tools.PdfTools;
import mara.mybox.value.AppVariables;
import static mara.mybox.value.AppVariables.logger;
import static mara.mybox.value.AppVariables.message;
import mara.mybox.value.CommonFxValues;
import mara.mybox.value.CommonValues;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
import org.apache.pdfbox.pdmodel.encryption.InvalidPasswordException;

/**
 * @Author Mara
 * @CreateDate 2018-9-10
 * @Description
 * @License Apache License Version 2.0
 */
public class PdfAttributesController extends BaseController {

    private PdfInformation pdfInfo;
    private float version;
    private Date createTime, modifyTime;

    @FXML
    protected TextField titleInput, subjectInput, authorInput, creatorInput, producerInput,
            createTimeInput, modifyTimeInput, keywordInput, versionInput;
    @FXML
    protected PasswordField userPasswordInput, userPasswordInput2, ownerPasswordInput, ownerPasswordInput2;
    @FXML
    protected CheckBox assembleCheck, extractCheck, modifyCheck, fillCheck, printCheck,
            viewPasswordCheck;

    public PdfAttributesController() {
        baseTitle = AppVariables.message("PDFAttributes");

        SourceFileType = VisitHistory.FileType.PDF;
        SourcePathType = VisitHistory.FileType.PDF;
        TargetPathType = VisitHistory.FileType.PDF;
        TargetFileType = VisitHistory.FileType.PDF;
        AddFileType = VisitHistory.FileType.PDF;
        AddPathType = VisitHistory.FileType.PDF;

        targetPathKey = "PdfFilePath";
        sourcePathKey = "PdfFilePath";

        sourceExtensionFilter = CommonFxValues.PdfExtensionFilter;
        targetExtensionFilter = sourceExtensionFilter;

    }

    @Override
    public void initializeNext() {
        versionInput.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue ov, String oldValue, String newValue) {
                try {
                    Float f = Float.parseFloat(newValue);
                    if (f >= 0) {
                        versionInput.setStyle(null);
                        version = f;
                    } else {
                        versionInput.setStyle(badStyle);
                    }
                } catch (Exception e) {
                    versionInput.setStyle(badStyle);
                }
            }
        });
        FxmlControl.setTooltip(versionInput, message("PdfVersionComments"));

        createTimeInput.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue ov, String oldValue, String newValue) {
                if (newValue == null || newValue.trim().isEmpty()) {
                    createTime = null;
                    createTimeInput.setStyle(null);
                    return;
                }
                try {
                    Date d = DateTools.stringToDatetime(newValue);
                    if (d != null) {
                        createTimeInput.setStyle(null);
                        createTime = d;
                    } else {
                        createTimeInput.setStyle(badStyle);
                    }
                } catch (Exception e) {
                    createTimeInput.setStyle(badStyle);
                }
            }
        });

        modifyTimeInput.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue ov, String oldValue, String newValue) {
                if (newValue == null || newValue.trim().isEmpty()) {
                    modifyTime = null;
                    modifyTimeInput.setStyle(null);
                    return;
                }
                try {
                    Date d = DateTools.stringToDatetime(newValue);
                    if (d != null) {
                        modifyTimeInput.setStyle(null);
                        modifyTime = d;
                    } else {
                        modifyTimeInput.setStyle(badStyle);
                    }
                } catch (Exception e) {
                    modifyTimeInput.setStyle(badStyle);
                }
            }
        });

        userPasswordInput.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue ov, String oldValue, String newValue) {
                checkUserPassword();
            }
        });

        userPasswordInput2.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue ov, String oldValue, String newValue) {
                checkUserPassword();
            }
        });

        ownerPasswordInput.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue ov, String oldValue, String newValue) {
                checkOwnerPassword();
            }
        });

        ownerPasswordInput2.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue ov, String oldValue, String newValue) {
                checkOwnerPassword();
            }
        });

        authorInput.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
                AppVariables.setUserConfigValue("AuthorKey", newValue);
            }
        });
        authorInput.setText(AppVariables.getUserConfigValue("AuthorKey", System.getProperty("user.name")));

        saveButton.disableProperty().bind(
                sourceFileInput.styleProperty().isEqualTo(badStyle)
                        .or(versionInput.styleProperty().isEqualTo(badStyle))
                        .or(createTimeInput.styleProperty().isEqualTo(badStyle))
                        .or(modifyTimeInput.styleProperty().isEqualTo(badStyle))
                        .or(userPasswordInput2.styleProperty().isEqualTo(badStyle))
                        .or(ownerPasswordInput2.styleProperty().isEqualTo(badStyle))
        );

    }

    @Override
    public void sourceFileChanged(final File file) {
        sourceFileInput.setStyle(badStyle);
        if (!PdfTools.isPDF(file.getAbsolutePath())) {
            return;
        }
        sourceFile = file;
        loadPdfInformation(null);
    }

    public void checkUserPassword() {
        String p1 = userPasswordInput.getText();
        String p2 = userPasswordInput2.getText();
        boolean valid;
        if (p1 == null || p1.isEmpty()) {
            valid = p2 == null || p2.isEmpty();
        } else {
            valid = p1.equals(p2);
        }
        if (valid) {
            userPasswordInput.setStyle(null);
            userPasswordInput2.setStyle(null);
        } else {
            userPasswordInput2.setStyle(badStyle);
        }
    }

    public void checkOwnerPassword() {
        String p1 = ownerPasswordInput.getText();
        String p2 = ownerPasswordInput2.getText();
        boolean valid;
        if (p1 == null || p1.isEmpty()) {
            valid = p2 == null || p2.isEmpty();
        } else {
            valid = p1.equals(p2);
        }
        if (valid) {
            ownerPasswordInput.setStyle(null);
            ownerPasswordInput2.setStyle(null);
        } else {
            ownerPasswordInput2.setStyle(badStyle);
        }
    }

    public void loadPdfInformation(final String password) {
        synchronized (this) {
            if (task != null) {
                return;
            }
            infoButton.setDisable(true);
            pdfInfo = null;
            version = -1;
            createTime = null;
            modifyTime = null;
            if (sourceFile == null) {
                return;
            }
            pdfInfo = new PdfInformation(sourceFile);
            task = new SingletonTask<Void>() {

                private boolean pop;

                @Override
                protected boolean handle() {
                    ok = false;
                    pop = false;
                    try {
                        try ( PDDocument doc = PDDocument.load(sourceFile, password, AppVariables.pdfMemUsage)) {
                            pdfInfo.setOwnerPassword(password);
                            pdfInfo.readInfo(doc);
                            doc.close();
                            ok = true;
                        }
                    } catch (InvalidPasswordException e) {
                        pop = true;
                    } catch (IOException e) {
                    }
                    if (pop) {
                        Platform.runLater(new Runnable() {
                            @Override
                            public void run() {
                                TextInputDialog dialog = new TextInputDialog();
                                dialog.setHeaderText(AppVariables.message("OwnerPasswordComments"));
                                dialog.setContentText(AppVariables.message("OwnerPassword"));
                                Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow();
                                stage.setAlwaysOnTop(true);
                                stage.toFront();

                                Optional<String> result = dialog.showAndWait();
                                if (result.isPresent()) {
                                    loadPdfInformation(result.get());
                                }
                            }
                        });
                    }
                    return true;
                }

                @Override
                protected void whenSucceeded() {
                    sourceFileInput.setStyle(null);
                    infoButton.setDisable(false);
                    resetAction();
                }
            };
            openHandlingStage(task, Modality.WINDOW_MODAL);
            Thread thread = new Thread(task);
            thread.setDaemon(true);
            thread.start();
        }
    }

    @FXML
    public void resetAction() {
        if (pdfInfo == null) {
            return;
        }
        titleInput.setText(pdfInfo.getTitle());
        subjectInput.setText(pdfInfo.getSubject());
        authorInput.setText(pdfInfo.getAuthor());
        creatorInput.setText(pdfInfo.getCreator());
        producerInput.setText(pdfInfo.getProducer());
        createTime = new Date(pdfInfo.getCreateTime());
        modifyTime = new Date(pdfInfo.getModifyTime());
        createTimeInput.setText(DateTools.datetimeToString(pdfInfo.getCreateTime()));
        modifyTimeInput.setText(DateTools.datetimeToString(pdfInfo.getModifyTime()));
        keywordInput.setText(pdfInfo.getKeywords());
        version = pdfInfo.getVersion();
        versionInput.setText(pdfInfo.getVersion() + "");
        AccessPermission acc = pdfInfo.getAccess();
        if (acc != null) {
            assembleCheck.setSelected(acc.canAssembleDocument());
            extractCheck.setSelected(acc.canExtractContent());
            modifyCheck.setSelected(acc.canModify());
            fillCheck.setSelected(acc.canFillInForm());
            printCheck.setSelected(acc.canPrint());
        }
        ownerPasswordInput.setText(pdfInfo.getOwnerPassword());
        ownerPasswordInput2.setText(pdfInfo.getOwnerPassword());
        userPasswordInput.setText("");
        userPasswordInput2.setText("");

    }

    @FXML
    @Override
    public void infoAction() {
        if (pdfInfo == null) {
            return;
        }
        try {
            final PdfInformationController controller = (PdfInformationController) openStage(CommonValues.PdfInformationFxml);
            controller.setInformation(pdfInfo);

        } catch (Exception e) {
            logger.error(e.toString());
        }
    }

    @FXML
    public void nowCreateTime() {
        createTimeInput.setText(DateTools.nowString());
    }

    @FXML
    public void nowModifyTime() {
        modifyTimeInput.setText(DateTools.nowString());
    }

    @FXML
    @Override
    public void saveAction() {
        if (sourceFile == null) {
            return;
        }
        if ((userPasswordInput.getText() != null && !userPasswordInput.getText().isEmpty())
                || (ownerPasswordInput.getText() != null && !ownerPasswordInput.getText().isEmpty())) {
            Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
            alert.setTitle(myStage.getTitle());
            alert.setContentText(AppVariables.message("SureSetPassword"));
            alert.getDialogPane().setMinHeight(Region.USE_PREF_SIZE);
            ButtonType buttonSure = new ButtonType(AppVariables.message("Sure"));
            ButtonType buttonCancel = new ButtonType(AppVariables.message("Cancel"));
            alert.getButtonTypes().setAll(buttonSure, buttonCancel);
            Stage stage = (Stage) alert.getDialogPane().getScene().getWindow();
            stage.setAlwaysOnTop(true);
            stage.toFront();
            Optional<ButtonType> result = alert.showAndWait();
            if (result.get() != buttonSure) {
                return;
            }
        }

        final PdfInformation modify = new PdfInformation(sourceFile);
        modify.setAuthor(authorInput.getText());
        modify.setTitle(titleInput.getText());
        modify.setSubject(subjectInput.getText());
        modify.setCreator(creatorInput.getText());
        modify.setProducer(producerInput.getText());
        modify.setKeywords(keywordInput.getText());
        if (modifyTime != null) {
            modify.setModifyTime(modifyTime.getTime());
        }
        if (createTime != null) {
            modify.setCreateTime(createTime.getTime());
        }
        if (version > 0) {
            modify.setVersion(version);
        }
        modify.setUserPassword(userPasswordInput.getText());
        modify.setOwnerPassword(ownerPasswordInput.getText());
        AccessPermission acc = AccessPermission.getOwnerAccessPermission();
        acc.setCanAssembleDocument(assembleCheck.isSelected());
        acc.setCanExtractContent(extractCheck.isSelected());
        acc.setCanExtractForAccessibility(extractCheck.isSelected());
        acc.setCanFillInForm(fillCheck.isSelected());
        acc.setCanModify(modifyCheck.isSelected());
        acc.setCanModifyAnnotations(modifyCheck.isSelected());
        acc.setCanPrint(printCheck.isSelected());
        acc.setCanPrintDegraded(printCheck.isSelected());
        modify.setAccess(acc);

        synchronized (this) {
            if (task != null) {
                return;
            }
            task = new SingletonTask<Void>() {

                private boolean pop;

                @Override
                protected boolean handle() {
                    return PdfTools.setAttributes(sourceFile, pdfInfo.getOwnerPassword(), modify);
                }

                @Override
                protected void whenSucceeded() {
                    loadPdfInformation(ownerPasswordInput.getText());
                    popSuccessful();
                }

            };
            openHandlingStage(task, Modality.WINDOW_MODAL);
            Thread thread = new Thread(task);
            thread.setDaemon(true);
            thread.start();
        }
    }

}