/*
 * Copyright (C) 2011 4th Line GmbH, Switzerland
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.fourthline.konto.client.ledger.component;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.TreeItem;
import com.google.gwt.user.client.ui.Widget;
import org.fourthline.konto.client.bundle.Bundle;
import org.seamless.gwt.component.client.widget.GhostedTextBox;
import org.seamless.gwt.component.client.widget.ImageTextButton;
import org.fourthline.konto.shared.AccountType;
import org.fourthline.konto.shared.entity.Account;
import org.fourthline.konto.shared.query.AccountsQueryCriteria;
import org.seamless.gwt.theme.shared.client.ThemeBundle;
import org.seamless.gwt.theme.shared.client.ThemeStyle;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Christian Bauer
 */
public class AccountTreeSelectViewImpl extends Composite implements AccountTreeSelectView {

    interface UI extends UiBinder<Widget, AccountTreeSelectViewImpl> {
    }

    interface Style extends CssResource {
    }

    private static UI ui = GWT.create(UI.class);

    @UiField(provided = true)
    final Bundle bundle;
    @UiField(provided = true)
    final ThemeBundle themeBundle;
    @UiField
    Style style;

    @UiField
    ImageTextButton newButton;
    @UiField
    SimplePanel newPanel;

    @UiField(provided = true)
    GhostedTextBox filterTextBox;

    @UiField
    Button filterClearButton;

    @UiField(provided = true)
    AccountTree assetTree;
    @UiField(provided = true)
    AccountTree liabilityTree;
    @UiField(provided = true)
    AccountTree incomeTree;
    @UiField(provided = true)
    AccountTree expenseTree;
    @UiField
    Label noAssetsLabel;
    @UiField
    Label noLiabilitiesLabel;
    @UiField
    Label noIncomeLabel;
    @UiField
    Label noExpenseLabel;
    @UiField
    SimplePanel filterPanel;

    protected Presenter presenter;
    protected Option[] options;
    protected List<Account> accounts = new ArrayList();
    protected Timer filterTimer;

    @Inject
    public AccountTreeSelectViewImpl(Bundle bundle) {
        this.bundle = bundle;
        this.themeBundle = bundle.themeBundle().create();

        this.filterTextBox =
                new GhostedTextBox(getFilterLabel(), ThemeStyle.GhostedTextBox()) {
                    @Override
                    public void onKeyUp(KeyUpEvent event) {
                        super.onKeyUp(event);

                        final String enteredValue = getValue();
                        if (enteredValue.length() > 0) {
                            filterClearButton.setEnabled(true);
                            scheduleFilterReqest(new Timer() {
                                public void run() {
                                    if (presenter != null) {
                                        presenter.onFilter(enteredValue);
                                    }
                                }
                            });
                        } else {
                            filterClearButton.setEnabled(false);
                            if (filterTimer != null) filterTimer.cancel();
                            if (presenter != null) {
                                presenter.onFilter(null);
                            }
                        }
                    }
                };

        assetTree = new AccountTree(AccountType.Asset);
        liabilityTree = new AccountTree(AccountType.Liability);
        incomeTree = new AccountTree(AccountType.Income);
        expenseTree = new AccountTree(AccountType.Expense);

        addSelectionHandler(new AccountTree.AccountSelectionHandler() {

            @Override
            public void onSelection(SelectionEvent<TreeItem> event) {
                super.onSelection(event);
                if (Option.MULTISELECT.in(options))
                    presenter.onMultiSelectionChange(getSelectedAccounts());
            }

            @Override
            public boolean onAccountGroupSelection(Long id, String name) {
                return !Option.MULTISELECT.in(options);
            }

            @Override
            public boolean onAccountSelection(Account account) {
                if (!Option.MULTISELECT.in(options))
                    presenter.onSingleSelectionChange(account);
                return !Option.MULTISELECT.in(options);
            }

            @Override
            public boolean onSelection(AccountType type) {
                return !Option.MULTISELECT.in(options);
            }
        });

        initWidget(ui.createAndBindUi(this));
    }


    @Override
    public void setPresenter(Presenter presenter) {
        this.presenter = presenter;
    }

    @Override
    public void setAccounts(List<Account> value, Option... options) {
        this.accounts = value;
        this.options = options;

        getWidget().setVisible(true);
        clearTrees();
        updateTrees(true);
        applyCurrentOptions();
    }

    public void addSelectionHandler(SelectionHandler handler) {
        assetTree.addSelectionHandler(handler);
        liabilityTree.addSelectionHandler(handler);
        incomeTree.addSelectionHandler(handler);
        expenseTree.addSelectionHandler(handler);
    }

    @Override
    public void setSelectedAccounts(AccountsQueryCriteria[] selectedAccounts) {
        AccountsQueryCriteria criteria;

        if ((criteria = AccountsQueryCriteria.get(selectedAccounts, AccountType.Asset)) != null)
            assetTree.setMultiSelected(criteria.getListOfIdentifiers());
        else
            assetTree.setMultiSelected(false);

        if ((criteria = AccountsQueryCriteria.get(selectedAccounts, AccountType.Liability)) != null)
            liabilityTree.setMultiSelected(criteria.getListOfIdentifiers());
        else
            liabilityTree.setMultiSelected(false);

        if ((criteria = AccountsQueryCriteria.get(selectedAccounts, AccountType.Income)) != null)
            incomeTree.setMultiSelected(criteria.getListOfIdentifiers());
        else
            incomeTree.setMultiSelected(false);

        if ((criteria = AccountsQueryCriteria.get(selectedAccounts, AccountType.Expense)) != null)
            expenseTree.setMultiSelected(criteria.getListOfIdentifiers());
        else
            expenseTree.setMultiSelected(false);
    }

    @Override
    public AccountsQueryCriteria[] getSelectedAccounts() {
        List<AccountsQueryCriteria> list = new ArrayList();
        List<Long> ids;

        if (!Option.HIDE_ASSET.in(options)) {
            ids = assetTree.getSelectedAccounts();
            if (ids.size() > 0)
                list.add(new AccountsQueryCriteria(ids, AccountType.Asset));
        }

        if (!Option.HIDE_LIABILITY.in(options)) {
            ids = liabilityTree.getSelectedAccounts();
            if (ids.size() > 0)
                list.add(new AccountsQueryCriteria(ids, AccountType.Liability));
        }

        if (!Option.HIDE_INCOME.in(options)) {
            ids = incomeTree.getSelectedAccounts();
            if (ids.size() > 0)
                list.add(new AccountsQueryCriteria(ids, AccountType.Income));
        }

        if (!Option.HIDE_EXPENSE.in(options)) {
            ids = expenseTree.getSelectedAccounts();
            if (ids.size() > 0)
                list.add(new AccountsQueryCriteria(ids, AccountType.Expense));
        }

        return list.toArray(new AccountsQueryCriteria[list.size()]);
    }

    protected void applyCurrentOptions() {
        newPanel.setVisible(false);
        filterPanel.setVisible(false);
        setTreesMultiSelectEnabled(false);
        setTreesMultiSelected(false);

        assetTree.setVisible(true);
        liabilityTree.setVisible(true);
        incomeTree.setVisible(true);
        expenseTree.setVisible(true);

        if (options == null) return;

        for (Option option : options) {
            switch (option) {
                case NEW_BUTTON:
                    newPanel.setVisible(true);
                    break;
                case LABEL_FILTER:
                    filterPanel.setVisible(true);
                    break;
                case MULTISELECT:
                    setTreesMultiSelectEnabled(true);
                    break;
                case SELECT_ALL:
                    setTreesMultiSelected(true);
                    break;
                case SELECT_NONE:
                    setTreesMultiSelected(false);
                    break;
                case HIDE_ASSET:
                    assetTree.setVisible(false);
                    break;
                case HIDE_LIABILITY:
                    liabilityTree.setVisible(false);
                    break;
                case HIDE_INCOME:
                    incomeTree.setVisible(false);
                    break;
                case HIDE_EXPENSE:
                    expenseTree.setVisible(false);
                    break;
            }
        }
    }

    protected void clearTrees() {
        assetTree.clear();
        liabilityTree.clear();
        incomeTree.clear();
        expenseTree.clear();
    }

    protected void setTreesMultiSelectEnabled(boolean enabled) {
        assetTree.setMultiSelectEnabled(enabled);
        liabilityTree.setMultiSelectEnabled(enabled);
        incomeTree.setMultiSelectEnabled(enabled);
        expenseTree.setMultiSelectEnabled(enabled);
    }

    protected void setTreesMultiSelected(boolean selected) {
        assetTree.setMultiSelected(selected);
        liabilityTree.setMultiSelected(selected);
        incomeTree.setMultiSelected(selected);
        expenseTree.setMultiSelected(selected);
    }

    protected void updateTrees(boolean expandAll) {
        for (Account account : accounts) {

            switch (account.getType()) {
                case Asset:
                case BankAccount:
                    assetTree.addAccount(account, expandAll);
                    break;
                case Liability:
                    liabilityTree.addAccount(account, expandAll);
                    break;
                case Income:
                    incomeTree.addAccount(account, expandAll);
                    break;
                case Expense:
                    expenseTree.addAccount(account, expandAll);
                    break;
            }
        }
        if (!Option.HIDE_ASSET.in(options))
            noAssetsLabel.setVisible(!assetTree.hasRootChildren());
        if (!Option.HIDE_LIABILITY.in(options))
            noLiabilitiesLabel.setVisible(!liabilityTree.hasRootChildren());
        if (!Option.HIDE_INCOME.in(options))
            noIncomeLabel.setVisible(!incomeTree.hasRootChildren());
        if (!Option.HIDE_EXPENSE.in(options))
            noExpenseLabel.setVisible(!expenseTree.hasRootChildren());

    }


    @UiHandler("newButton")
    void onClickNewAccountButton(ClickEvent e) {
        presenter.onNewAccount();
    }

    @UiHandler("filterClearButton")
    void onClickDescriptionFilterClear(ClickEvent e) {
        filterTextBox.clear();
        filterClearButton.setEnabled(false);
        if (filterTimer != null) filterTimer.cancel();
        if (presenter != null) {
            presenter.onFilter(null);
        }
    }

    protected void scheduleFilterReqest(Timer timer) {
        if (filterTimer != null) {
            filterTimer.cancel();
        }
        filterTimer = timer;
        filterTimer.schedule(1000);
    }

    protected String getFilterLabel() {
        return "Filter Accounts...";
    }

}
