/*
 *
 *  Copyright (C) 2022 Andrew Gegg
 *
 * 	This file is part of the Gardeners Notebook application
 *
 *  The Gardeners Notebook application is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/gpl.html>.
 *
 *
 */

package uk.co.gardennotebook;

import javafx.beans.binding.StringBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.EntryMessage;
import uk.co.gardennotebook.fxbean.CommentBean;
import uk.co.gardennotebook.fxbean.INotebookBean;
import uk.co.gardennotebook.fxbean.SaleBean;
import uk.co.gardennotebook.spi.GNDBException;
import uk.co.gardennotebook.spi.INotebookEntry;
import uk.co.gardennotebook.spi.NotebookEntryType;
import uk.co.gardennotebook.util.SimpleMoney;

import java.time.LocalDate;
import java.util.Objects;
import java.util.ResourceBundle;

/**
 *	A specific implementation of DiaryBean for Sale diary entries.
 *
 * @author Andy Gegg
 *	@version	3.0.5
 *	@since	3.0.5
 */
final class DiaryBeanSale extends DiaryBean
{
    private static final Logger LOGGER = LogManager.getLogger();

    private final SaleBean sale;

    private Integer itemKey = 0;

    final ObjectProperty<LocalDate> dateProperty;

    private final SimpleObjectProperty<String> mainTitleProperty;	//	the title - the Buyer
    private final StringBinding mainTitleText;
    private final ReadOnlyStringWrapper subTitleProperty;	//	fixed text 'Total'
    private final StringBinding subTitleText;
    private final ObjectProperty<SimpleMoney> varietyProperty;	// the Total Price
    private final StringBinding varietyText;
    private final Property<?> detailProperty = null;    //  null text
    private final StringBinding detailText;

    private final SimpleObjectProperty<ObservableList<CommentBean>> comments =
            new SimpleObjectProperty<>(this, "comments", FXCollections.emptyObservableList());

    private final ResourceBundle resources = ResourceBundle.getBundle("notebook");

    DiaryBeanSale(SaleBean saleBean)
    {
        EntryMessage log4jEntryMsg = LOGGER.traceEntry("DiaryBeanSale: constructor(): SaleBean:{}", saleBean);

        sale = saleBean;
        itemKey = sale.getKey();

        dateProperty = sale.dateProperty();

        mainTitleProperty = new SimpleObjectProperty( sale.purchasedByProperty() );
        mainTitleText = new StringBinding()
        {
            {
                super.bind(mainTitleProperty);
            }
            @Override
            protected String computeValue()
            {
                return sale.getPurchasedBy();
            }

            @Override
            public void dispose()
            {
                super.unbind(mainTitleProperty);
            }
        };
		//	following code invalidates mainTitleProperty so that the new value is shown on the DiaryTab
        //  If Logging is on (above INFO) this doesn't work and changes don't get reflected to the Diary.  Probably a timing problem.
		sale.purchasedByProperty().addListener((obs, old, newVal) -> {
					((SimpleObjectProperty)mainTitleProperty).setValue( new SimpleStringProperty("hello"));
					((SimpleObjectProperty)mainTitleProperty).setValue(sale.purchasedByProperty());
		});

        subTitleProperty = new ReadOnlyStringWrapper(resources.getString("lbl.total"));
        subTitleText = new StringBinding()
        {
//            {
//                super.bind(subTitleProperty);
//            }
            @Override
            protected String computeValue()
            {
                return resources.getString("lbl.total");
            }

//            @Override
//            public void dispose()
//            {
//                super.unbind(subTitleProperty);
//            }
        };

        varietyProperty = this.sale.totalPriceProperty();
        varietyText = new StringBinding()
        {
            {
                super.bind(varietyProperty);
            }
            @Override
            protected String computeValue()
            {
                return sale.getTotalPrice().toString();
            }

            @Override
            public void dispose()
            {
                super.unbind(varietyProperty);
            }
        };

//        detailProperty = null;
        detailText = new StringBinding()
        {
//            {
//                super.bind(detailProperty);
//            }
            @Override
            protected String computeValue()
            {
                return "";
            }

//            @Override
//            public void dispose()
//            {
//                super.unbind(detailProperty);
//            }
        };

        LOGGER.traceExit(log4jEntryMsg);
    }

    @Override
    INotebookBean getItem()
    {
        return sale;
    }

    @Override
    INotebookEntry getBaseItem()
    {
        return sale.get().orElse(null);
    }

    @Override
    Integer getKey()
    {
        return itemKey;
    }

    @Override
    NotebookEntryType getItemType()
    {
        return NotebookEntryType.SALE;
    }

    @Override
    boolean canDelete() throws GNDBException
    {
        return sale.canDelete();
    }

    /**
     * The date of this diary entry
     *
     * @return	the (value of) the dateProperty
     */
    @Override
    LocalDate getDate()
    {
        return dateProperty.getValue();
    }

    @Override
    void setDate(LocalDate date)
    {
        dateProperty().setValue(date);
    }

    @Override
    ObjectProperty<LocalDate> dateProperty()
    {
        return dateProperty;
    }

    /**
     * The title for this Diary entry - here, the Buyer's name
     *
     * @return	the (value of) the mainTitleProperty
     */
    @Override
    StringBinding getMainTitleText()
    {
        return mainTitleText;
    }

    @Override
    SimpleObjectProperty<String> mainTitleProperty()
    {
        return mainTitleProperty;
    }

    /**
     * The first detail for this Diary entry - here, the fixed String 'Total'
     *
     * @return	the (value of) the subTitleProperty
     */
    @Override
    StringBinding getSubTitleText()
    {
        return subTitleText;
    }

    @Override
    ReadOnlyStringWrapper subTitleProperty()
    {
        return subTitleProperty;
    }

    /**
     * The second detail for this Diary entry - here, the Total Price of the Sale
     *
     * @return	the (value of) the varietyProperty
     */
    @Override
    StringBinding getVarietyText()
    {
        return varietyText;
    }

    @Override
    Property<SimpleMoney> varietyProperty()
    {
        return varietyProperty;
    }

    /**
     * The third detail for this Diary entry - here, a null value
     *
     * @return	the (value of) the detailProperty
     */
    @Override
    StringBinding getDetailText()
    {
        return detailText;
    }

    @Override
    Property<?> detailProperty()
    {
        return detailProperty;
    }

    private ObservableList<CommentBean> getCommentList()
    {
        return sale.getComments();
    }

    @Override
    ObservableList<CommentBean> getComments()
    {
        LOGGER.debug("getComments(): getValue(): {}", commentProperty().getValue());
        return commentProperty().getValue();
    }

    @Override
    SimpleObjectProperty<ObservableList<CommentBean>> commentProperty()
    {
        LOGGER.debug("commentProperty(): getCommentList(): {}", getCommentList());
        comments.set(getCommentList());	//	2.9.6
        return comments;
    }

    @Override
    /**
     * Implement the comparison for sorting.
     * Items are first sorted in date order.
     * if same date, then by item type
     *      -   weather then husbandry, then affliction,
     * 		-	then groundwork, then wildlife,
     * 		-	then purchases and items within purchases,
     * 		-	then sales and sale items within sales
     * 		-	and journal entries
     * 	Items of same type on same day are sorted by key to give a stable ordering
     * 	    (this will usually correspond to sorting by order of creation).
     *
     * @implNote Be very careful with the sort order for Sales - the sorting for SaleItems relies on it.
     */
    public int compareTo(DiaryBean o)
    {
		EntryMessage log4jEntryMsg = LOGGER.traceEntry("compareTo(): this: {}. other: {}", this.toString(), o.toString());

//        LOGGER.debug("this.dateProperty: {}", this.dateProperty.get());
//        LOGGER.debug("other.dateProperty: {}", o.dateProperty().get());

        if (!this.dateProperty.get().equals(o.dateProperty().get()))
        {//sort on date as primary key
            return this.dateProperty.get().compareTo(o.dateProperty().get());
        }

        int retVal = switch (o.getItemType())
                {
                    case SALE -> this.itemKey.compareTo(o.getKey()); //  stable ordering for entries of same type
                    case WEATHER, HUSBANDRY, AFFLICTIONEVENT, GROUNDWORK, WILDLIFE, PURCHASE, PURCHASEITEM -> 1;
                    case SALEITEM -> (sale.getKey().equals(((DiaryBeanSaleItem)o).getSaleKey())) ? -1 :
                                sale.getKey().compareTo(((DiaryBeanSaleItem)o).getSaleKey());
                    default -> -1;
                };
        LOGGER.debug("retVal: {}", retVal);
        return retVal;
    }

    @Override
    public boolean equals(Object o)
    {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        if (!super.equals(o)) return false;
        DiaryBeanSale that = (DiaryBeanSale) o;
        return sale.equals(that.sale) && itemKey.equals(that.itemKey) && mainTitleProperty.equals(that.mainTitleProperty) && mainTitleText.equals(that.mainTitleText) && subTitleProperty.equals(that.subTitleProperty) && subTitleText.equals(that.subTitleText) && varietyProperty.equals(that.varietyProperty) && varietyText.equals(that.varietyText) && Objects.equals(detailProperty, that.detailProperty) && Objects.equals(detailText, that.detailText) && Objects.equals(comments, that.comments) && Objects.equals(resources, that.resources);
    }

    @Override
    public int hashCode()
    {
        return Objects.hash(super.hashCode(), sale, itemKey, mainTitleProperty, mainTitleText, subTitleProperty, subTitleText, varietyProperty, varietyText, detailProperty, detailText, comments, resources);
    }

    @Override
    public  String toString()
    {
        return ("DiaryBeanSale of: hash: " + this.hashCode() + ", " + sale.toString());
    }

}
