/* 
 * Diplomová práce ÚSI VUT 
 * Forenzní analýza webového prohlížeče
 *
 * Autor: Michal Bača
 * Datum: 20.5.2015
 */
package yafft;

import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.sql.Timestamp;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.SingleSelectionModel;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.input.InputMethodEvent;
import javafx.scene.text.TextFlow;
import yafft.FFModel.DataLoader;
import yafft.FFModel.Model;
import yafft.FFModel.ModelItems.Bookmark;
import yafft.FFModel.ModelItems.CookieValue;
import yafft.FFModel.ModelItems.DownloadsItem;
import yafft.FFModel.ModelItems.FormItem;
import yafft.FFModel.ModelItems.HistoryItem;
import yafft.FFModel.ModelItems.PasswordItem;
import yafft.Utils.HostInfo;
import yafft.Utils.ImportExportOptions;
import yafft.Utils.Log;
import yafft.Utils.XmlOutput;
import yafft.Utils.Zipper;

/**
 * FXML Controller class
 *
 * @author michal
 */
public class YafftMainWindowController implements Initializable {

    @FXML
    private MenuItem yafftMenuImport;
    @FXML
    private MenuItem yafftMenuExport;
    @FXML
    private MenuItem yafftMenuSettings;
    @FXML
    private MenuItem yafftMenuQuit;
    //-------------------------------
    @FXML
    private TextField yafftFilter;
    @FXML
    private Button yafftFilterButton;
    //---------------------------------
    @FXML
    private TextFlow yafftLog;
    private Label yafftStatusBarLabel;
    @FXML
    private ProgressBar yafftStatusProgressBar;
    @FXML
    private Label yafftStatusProgressLabel;
    //-------------------------------------
    @FXML
    private TableView<HistoryItem> yafftHistoryTable;
    @FXML
    private TableColumn<HistoryItem, String> historyTableDate;
    //private TableColumn<HistoryItem, LocalDateTime> historyTableDate;
    @FXML
    private TableColumn<HistoryItem, String> historyTableTitle;
    @FXML
    private TableColumn<HistoryItem, URL> historyTableUrl;
    @FXML
    private TableColumn<HistoryItem, Integer> historyTableVisitCount;
    //-------------------------------------------------------------
    @FXML
    private TableView<DownloadsItem> yafftDownloadsTable;
    @FXML
    private TableColumn<DownloadsItem, String> downloadsTableName;
    @FXML
    private TableColumn<DownloadsItem, URL> downloadsTableSource;
    @FXML
    private TableColumn<DownloadsItem, URI> downloadsTableDestination;
    @FXML
    private TableColumn<DownloadsItem, Timestamp> downloadsTableDateTime;
    @FXML
    private TableColumn<DownloadsItem, String> downloadsTableSize;
    //  @FXML
    //  private TableColumn<DownloadsItem, Byte> downloadsTableStatus;

    @FXML
    private TableColumn<?, ?> downloadsTableStatus;
    //------------------------------------

    //--------------------------------------------------
    @FXML
    private TreeTableView<CookieValue> yafftCookiesTable;
    @FXML
    private TreeTableColumn<CookieValue, String> cookiesTableDomain;
    @FXML
    private TreeTableColumn<CookieValue, String> cookiesTableKey;
    @FXML
    private TreeTableColumn<CookieValue, String> cookiesTableValue;
    @FXML
    private TreeTableColumn<CookieValue, Timestamp> cookiesTableCreationTime;
    @FXML
    private TreeTableColumn<CookieValue, Timestamp> cookiesTableExpiryTime;
    //-------------------------------------------------------
    @FXML
    private TableView<FormItem> yafftFormsTable;
    @FXML
    private TableColumn<FormItem, String> formsTableID;
    @FXML
    private TableColumn<FormItem, String> formsTableValue;
    @FXML
    private TableColumn<FormItem, Integer> formsTableTimesUsed;
    @FXML
    private TableColumn<FormItem, Timestamp> formsTableFirstUsed;
    @FXML
    private TableColumn<FormItem, Timestamp> formsTableLastUsed;

    @FXML
    private TableView<Bookmark> yafftBookmarksTable;
    @FXML
    private TableColumn<Bookmark, String> bookmarksTableName;
    @FXML
    private TableColumn<Bookmark, URL> bookmarksTableUrl;
    @FXML
    private TableColumn<Bookmark, Timestamp> bookmarksTableAdded;
    @FXML
    private TableColumn<Bookmark, Timestamp> bookmarksTableModified;

    public Log log;

    private Model model;

    private DataLoader dl;

    private final boolean debug = true;

    private HostInfo info;

    private ImportExportOptions exportOptions;

    @FXML
    private TabPane yafftTabs;
    @FXML
    private Label yafftWorkDir;
    @FXML
    private TableColumn<PasswordItem, URL> passwordTableHostname;
    @FXML
    private TableColumn<PasswordItem, String> passwordTableFieldname;
    @FXML
    private TableColumn<PasswordItem, String> passwordTableUsername;
    @FXML
    private TableColumn<PasswordItem, String> passwordTablePassword;
    @FXML
    private TableColumn<PasswordItem, Timestamp> passwordTableCreated;
    @FXML
    private TableColumn<PasswordItem, Timestamp> passwordTableLastUsed;
    @FXML
    private TableColumn<PasswordItem, Timestamp> passwordTableTimeChanged;
    @FXML
    private TableColumn<PasswordItem, Integer> passwordTableTimesUsed;
    @FXML
    private TableView<PasswordItem> yafftPasswordTable;

    public void setWorkDirLabel(String m) {
        yafftWorkDir.setText(m);
    }

    public HostInfo getInfo() {
        return info;
    }

    private void enableTab(int i) {

        yafftTabs.getTabs().get(i).setDisable(false);

    }

    /**
     * Initializes the controller class.
     *
     * @param url
     * @param rb
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO

        this.yafftHistoryTable.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        this.yafftHistoryTable.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);;
        this.yafftDownloadsTable.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        this.yafftDownloadsTable.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);

        this.exportOptions = new ImportExportOptions();
        this.exportOptions.setAll(true);

        log = new Log(yafftLog, yafftStatusProgressLabel);
        yafftStatusProgressBar.setProgress(0.0);
        yafftStatusProgressLabel.setText("");

        yafftTabs.getTabs().stream().forEach((t) -> {
            t.setDisable(true);
        });

        //enable log
        this.enableTab(6);

        /////////////////////////////
        info = null;

        try {

            info = new HostInfo();
            ImportExportOptions.setHostInfo(info);
            this.setWorkDirLabel("Pracovní adresář: " + info.getUserDir());

            //pujde pryč -- nastaví se v ImportExort options
    //        ImportExportOptions.setProfile(info.getFFProfiles().get(0));
    //        ImportExportOptions.setProfieIndex(0);
            //info.printOsInfo();

            //yafftWorkDir.addEventHandler(EventType.ROOT, null);
        } catch (IOException ex) {
            Logger.getLogger(YafftMainWindowController.class.getName()).log(Level.SEVERE, null, ex);
        }

        if (info != null) {
            dl = new DataLoader(info);
        }
        this.model = new Model();

        /////////////////////////
        //----------------------------------------------------------------------
        //History
        historyTableDate.setCellValueFactory(date -> date.getValue().getDate());
        //historyTableDate.setCellValueFactory(cellData -> cellData.getValue().getDateTime());
        historyTableTitle.setCellValueFactory(title -> title.getValue().getTitle());
        historyTableUrl.setCellValueFactory(source -> source.getValue().getUrl());
        historyTableVisitCount.setCellValueFactory(visits -> visits.getValue().getVisitCount().asObject());

        //---------------------------------------------------------------------
        //Downloads
        this.downloadsTableDateTime.setCellValueFactory(ts -> ts.getValue().getEndTime());
        this.downloadsTableDestination.setCellValueFactory(dest -> dest.getValue().getDestination());
        this.downloadsTableName.setCellValueFactory(name -> name.getValue().getFileName());
        //this.downloadsTableSize.setCellValueFactory(size -> size.getValue().getFileSize().asObject());
        this.downloadsTableSize.setCellValueFactory(size -> size.getValue().getFileSizeMB());
        this.downloadsTableSource.setCellValueFactory(source -> source.getValue().getSource());
        //this.downloadsTableStatus.setCellValueFactory(status -> new SimpleObjectProperty<>());

        //----------------------------------------------------------------------
        //Bookmarks
        this.bookmarksTableAdded.setCellValueFactory(ts -> ts.getValue().getAdded());
        this.bookmarksTableModified.setCellValueFactory(ts -> ts.getValue().getModified());
        this.bookmarksTableName.setCellValueFactory(name -> name.getValue().getName());
        this.bookmarksTableUrl.setCellValueFactory(src -> src.getValue().getUrl());

        //--------------------------------------------------------------------------------------
        //Forms
        this.formsTableFirstUsed.setCellValueFactory(ts -> ts.getValue().getFirstUsed());
        this.formsTableID.setCellValueFactory(id -> id.getValue().getFieldID());
        this.formsTableLastUsed.setCellValueFactory(ts -> ts.getValue().getLastUsed());
        this.formsTableTimesUsed.setCellValueFactory(used -> used.getValue().getTimesUsed().asObject());
        this.formsTableValue.setCellValueFactory(value -> value.getValue().getFormValue());

        //-------------------------------------------------------------------------
        //Cookies
        this.cookiesTableCreationTime.setCellValueFactory(ts -> ts.getValue().getValue().getCreationTime());
        this.cookiesTableDomain.setCellValueFactory(domain -> domain.getValue().getValue().getDomain());
        this.cookiesTableExpiryTime.setCellValueFactory(ts -> ts.getValue().getValue().getExpiry());
        this.cookiesTableKey.setCellValueFactory(key -> key.getValue().getValue().getKey());
        this.cookiesTableValue.setCellValueFactory(value -> value.getValue().getValue().getValue());

        //-------------------------------------------------------------------------------
        //Passwords
        this.passwordTableCreated.setCellValueFactory(ts -> ts.getValue().getTimeCreated());
        this.passwordTableUsername.setCellValueFactory(name -> name.getValue().getEncryptedUsername());
        this.passwordTableHostname.setCellValueFactory(host -> host.getValue().getHostName());
        this.passwordTableFieldname.setCellValueFactory(field -> field.getValue().getFieldName());
        this.passwordTableLastUsed.setCellValueFactory(ts -> ts.getValue().getTimeLastUsed());
        this.passwordTablePassword.setCellValueFactory(pwd -> pwd.getValue().getEncryptedPassword());
        this.passwordTableTimeChanged.setCellValueFactory(ts -> ts.getValue().getTimePasswordChanged());
        this.passwordTableTimesUsed.setCellValueFactory(used -> used.getValue().getTimesUsed().asObject());

    }

    public void importData(ActionEvent event) {

        yafftStatusProgressBar.setProgress(-1.0);

        //log.append("MenuItem Clicked: Import");
        //yafftStatusProgressBar.setDisable(false);
        log.append("Probíhá import dat");
        log.append("Pracovní adresář: " + ImportExportOptions.getExportDirectory());

        if (ImportExportOptions.getHistory()) {
            model.setHistory(dl.getHistory(ImportExportOptions.getProfieIndex()));
            log.append("   Načítám historii");
            this.enableTab(0);
        }

        if (ImportExportOptions.getDownloads()) {
            model.setDownloads(dl.getDownloads(ImportExportOptions.getProfieIndex()));
            model.printDownloads();
            log.append("   Načítám stažené soubory");
            this.enableTab(1);

        }

        if (ImportExportOptions.getCookies()) {
            model.setCookies2(dl.getCookies2(ImportExportOptions.getProfieIndex()));
            model.printCookies2();
            log.append("   Načítám cookies");
            this.enableTab(2);
        }

        if (ImportExportOptions.getForms()) {
            model.setForms(dl.getForms(ImportExportOptions.getProfieIndex()));
            model.printForms();
            log.append("   Načítám formuláře");
            this.enableTab(3);
        }

        if (ImportExportOptions.getBookmarks()) {
            model.setBookmarks(dl.getBookmarks(ImportExportOptions.getProfieIndex()));
            model.printBookmarks();
            log.append("   Načítám záložky");
            this.enableTab(4);
        }

        if (ImportExportOptions.getPasswords()) {
            model.setPasswords(dl.getPasswords(ImportExportOptions.getProfieIndex()));
            model.printPasswords();
            this.enableTab(5);
            log.append("   Načítám hesla");
        }

        ////////////////////////////////////////////////////////////////////////////////////////
        //Hoistory        
        FilteredList<HistoryItem> filteredHistory = new FilteredList<>(model.getHistory(), p -> true);

        yafftFilter.textProperty().addListener((observable, oldValue, newValue) -> {
            filteredHistory.setPredicate((HistoryItem hi) -> {

                // If filter text is empty, display all persons.
                if (newValue.isEmpty() || newValue == null) {
                    return true;
                }

                if (hi.getTitle().getValue().toLowerCase().contains(newValue.toLowerCase())) {
                    return true;
                } else if (hi.getUrl().toString().toLowerCase().contains(newValue.toLowerCase())) {
                    return true;
                }

                return false; // no match
            });
        });

        SortedList<HistoryItem> sortedHistory = new SortedList<>(filteredHistory);

        sortedHistory.comparatorProperty().bind(yafftHistoryTable.comparatorProperty());

        yafftHistoryTable.setItems(sortedHistory);
        log.append("History: Loaded " + yafftHistoryTable.getItems().size() + " items.");

        ////////////////////////////////////////////////////////////////////////////////////////
        //Downloads
        FilteredList<DownloadsItem> filteredDownloads = new FilteredList<>(model.getDownloads(), p -> true);

        yafftFilter.textProperty().addListener((observable, oldValue, newValue) -> {
            filteredDownloads.setPredicate((DownloadsItem di) -> {

                if (newValue.isEmpty() || newValue == null) {
                    return true;
                }

                if (di.getFileName().getValue().contains(newValue.toLowerCase())) {
                    return true;
                } else if (di.getDestination().getName().toLowerCase().contains(newValue.toLowerCase())) {
                    return true;
                } else if (di.getSource().getName().toLowerCase().contains(newValue.toLowerCase())) {
                    return true;
                }
                if (di.getEndTime().getName().toLowerCase().contains(newValue.toLowerCase())) {
                    return true;
                }

                return false; // no match
            });
        });

        SortedList<DownloadsItem> sortedDownloads = new SortedList<>(filteredDownloads);
        sortedDownloads.comparatorProperty().bind(yafftDownloadsTable.comparatorProperty());

        yafftDownloadsTable.setItems(sortedDownloads);
        log.append("Downloads: Loaded " + yafftDownloadsTable.getItems().size() + " items.");

        ////////////////////////////////////////////////////////////////////////////////////////
        //Bookmarks
        FilteredList<Bookmark> filteredBookmarks = new FilteredList<>(model.getBookmarks(), p -> true);

        yafftFilter.textProperty().addListener((observable, oldValue, newValue) -> {
            filteredBookmarks.setPredicate((Bookmark b) -> {

                if (newValue.isEmpty() || newValue == null) {
                    return true;
                }

                if (b.getName().getValueSafe().contains(newValue.toLowerCase())) {
                    return true;
                } else if (b.getUrl().getName().toLowerCase().contains(newValue.toLowerCase())) {
                    return true;
                } else if (b.getAdded().getName().toLowerCase().contains(newValue.toLowerCase())) {
                    return true;
                }
                if (b.getModified().getName().toLowerCase().contains(newValue.toLowerCase())) {
                    return true;
                }

                return false; // no match
            });
        });

        SortedList<Bookmark> sortedBookmarks = new SortedList<>(filteredBookmarks);
        sortedBookmarks.comparatorProperty().bind(yafftBookmarksTable.comparatorProperty());

        yafftBookmarksTable.setItems(sortedBookmarks);

        log.append("Bookmars: Loaded " + yafftBookmarksTable.getItems().size() + " items");

        ////////////////////////////////////////////////////////////////////////////////////////
        //Forms
        FilteredList<FormItem> filteredForms = new FilteredList<>(model.getForms(), p -> true);

        yafftFilter.textProperty().addListener((observable, oldValue, newValue) -> {
            filteredForms.setPredicate((FormItem fi) -> {

                if (newValue.isEmpty() || newValue == null) {
                    return true;
                }

                if (fi.getFieldID().getValueSafe().contains(newValue.toLowerCase())) {
                    return true;
                } else if (fi.getFirstUsed().getName().toLowerCase().contains(newValue.toLowerCase())) {
                    return true;
                } else if (fi.getFormValue().getValueSafe().toLowerCase().contains(newValue.toLowerCase())) {
                    return true;
                }
                if (fi.getLastUsed().getName().toLowerCase().contains(newValue.toLowerCase())) {
                    return true;
                }

                return false; // no match
            });
        });

        SortedList<FormItem> sortedForms = new SortedList<>(filteredForms);
        sortedForms.comparatorProperty().bind(yafftFormsTable.comparatorProperty());

        yafftFormsTable.setItems(sortedForms);
        log.append("Fomrs: Loaded " + yafftFormsTable.getItems().size() + " items");

        //////////////////////////////////////////////////////////////////////////////////
        //Passwords
        FilteredList<PasswordItem> filteredPasswords = new FilteredList<>(model.getPasswords(), p -> true);

        yafftFilter.textProperty().addListener((observable, oldValue, newValue) -> {
            filteredPasswords.setPredicate((PasswordItem p) -> {

                // If filter text is empty, display all persons.
                if (newValue.isEmpty() || newValue == null) {
                    return true;
                }

                if (p.getHostName().getValue().toString().toLowerCase().contains(newValue.toLowerCase())) {
                    return true;
                } else if (p.getTimeCreated().getValue().toString().toLowerCase().contains(newValue.toLowerCase())) {
                    return true;
                } else if (p.getTimeLastUsed().getValue().toString().toLowerCase().contains(newValue.toLowerCase())) {
                    return true;
                } else if (p.getTimePasswordChanged().getValue().toString().toLowerCase().contains(newValue.toLowerCase())) {
                    return true;
                }

                return false; // no match
            });
        });

        SortedList<PasswordItem> sortedPasswords = new SortedList<>(filteredPasswords);

        sortedPasswords.comparatorProperty().bind(yafftPasswordTable.comparatorProperty());

        yafftPasswordTable.setItems(sortedPasswords);
        log.append("Password: Loaded " + yafftPasswordTable.getItems().size() + " items.");

        //////////////////////////////////////////////////////////////////////////////////
        //Cookies
        CookieValue c = new CookieValue("root", true);
        TreeItem<CookieValue> root = new TreeItem(c);

        TreeItem<CookieValue> leaf = root;

        for (CookieValue item : model.getCookies2()) {
            CookieValue rootCookie;

            if (item.isRoot()) {

                leaf = new TreeItem(item);
                root.getChildren().add(leaf);

            } else {
                TreeItem<CookieValue> cv = new TreeItem<>(item);
                leaf.getChildren().add(cv);
            }

        }

        yafftCookiesTable.setRoot(root);
        yafftCookiesTable.setTableMenuButtonVisible(true);
        yafftCookiesTable.setShowRoot(false);

        yafftStatusProgressBar.setProgress(0.0);

        SingleSelectionModel<Tab> tabsSelection = yafftTabs.getSelectionModel();

        for (Tab t : yafftTabs.getTabs()) {
            if (!t.isDisable()) {
                tabsSelection.select(t);
                break;
            }
        }

        if (ImportExportOptions.getCreateCopyAndExit()) {

            log.append("Pořizuji kopii dat: ");
            this.createCopy();

            log.append("Exportuji data");

            log.noAction();

            this.exportData(event);

            //System.exit(0);
        }

    }

    public void exportData(ActionEvent event) {

        // log.append("MenuItem Clicked: Export");
        //this.info.listProfilesFiles();
        log.append("Pracovní adresář: " + ImportExportOptions.getExportDirectory());
        log.append("     Writing report: " + ImportExportOptions.getExportDirectory() + java.io.File.separator + "report.xml");

        yafftStatusProgressBar.setProgress(-1.0);

        if (ImportExportOptions.getCreateCopyAndExit()) {
            this.createCopy();

        }

        Task task;
        task = new Task<Void>() {
            @Override
            public Void call() {

                updateProgress(-1, 0);
                updateMessage("Zapisuji do souboru");
                //////////////////////////////////////////////////////////////////////

                XmlOutput writer = new XmlOutput(model);
                writer.setOptions(exportOptions);
                //System.out.println("/////////////////\n\n\n");
                writer.write();

                updateProgress(0, 0);
                updateMessage("Hotovo");

                ////////////////////////////////////////////////////////////////////
                return null;
            }

            @Override
            protected void succeeded() {
                super.succeeded();
                yafftStatusProgressBar.progressProperty().unbind();

                yafftStatusProgressBar.setProgress(0.0);

                log.getLabel().textProperty().unbind();

            }

        };

        yafftStatusProgressBar.progressProperty().bind(task.progressProperty());
        log.getLabel().textProperty().bind(task.messageProperty());

        new Thread(task).start();

    }

    private void exportDataFinished() {
        yafftStatusProgressBar.setProgress(0.0);

    }

    @FXML
    private void yafftOpenSettings(ActionEvent event) {
        log.append("MenuItem Clicked: Settings");

    }

    @FXML
    private void yafftQuit(ActionEvent event) {
        System.exit(0);
    }

    @FXML
    private void yafftFilterTextChanged(InputMethodEvent event) {
        log.append("Text in filtr changed");
    }

    @FXML
    private void yafftFilterButtonClicked(ActionEvent event) {
        log.append("Filter Button CLicked");

    }

    @FXML
    private void toolBarImportButtonClicked(ActionEvent event) {
        YafftImExWizard wiz = new YafftImExWizard(true, this);
    }

    @FXML
    private void toolBarExportButtonClicked(ActionEvent event) {
        YafftImExWizard wiz = new YafftImExWizard(false, this);
    }

    public void print(String message) {
        log.append(message);
    }

    @FXML
    private void yafftMenuImportButtonClicked(ActionEvent event) {
        YafftImExWizard wiz = new YafftImExWizard(true, this);
    }

    @FXML
    private void yafftMenuExportClicked(ActionEvent event) {
        YafftImExWizard wiz = new YafftImExWizard(false, this);
    }

    @FXML
    private void yafftMenuCopyClicked(ActionEvent event) {
        this.createCopy();
    }

    private void createCopy() {
        if (ImportExportOptions.getProfile() != null || ImportExportOptions.getExportDirectory() != null || info.getProfileFiles(ImportExportOptions.getProfile()) != null) {

            try {
                Zipper zip = new Zipper(info.getProfileFiles(ImportExportOptions.getProfile()), ImportExportOptions.getExportDirectory(), info.getFFProfilePaths().get(ImportExportOptions.getProfieIndex()), ImportExportOptions.getProfile().toString());

                log.append("Vytvářím ZIP archiv: " + zip.getOutputFileName());

                zip.createZip();
            } catch (IOException ex) {
                Logger.getLogger(YafftMainWindowController.class.getName()).log(Level.SEVERE, null, ex);
            }

            log.append("     ZIP vytvořen");
        } else {
            System.out.println("Nedělám zip, něco chybí---------------------------");
        }
    }

    @FXML
    private void toolBarECreateCopyButtonClicked(ActionEvent event) {
        //YafftImExWizard wiz = new YafftImExWizard(false, this);

        this.createCopy();
    }

}
