/*
 * Copyright (C) 2018, 2021, 2023 Andrew Gegg
 *
 *	This file is part of the Garden Notebook application
 *
 * The Garden 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>.
 */

/*
	Change log
	3.0.0	Use FlagHandler
	3.1.0	Use jakarta implementation of JSON
 */

package uk.co.gardennotebook.mysql;

import java.beans.PropertyChangeListener;

import uk.co.gardennotebook.spi.*;

import java.time.LocalDateTime;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

//import java.time.*;
import java.util.Optional;

import jakarta.json.JsonBuilderFactory;
import jakarta.json.JsonArrayBuilder;
import jakarta.json.JsonArray;
import jakarta.json.JsonObjectBuilder;
import jakarta.json.JsonObject;

/**
*{@inheritDoc}
*
*	@author	Andy Gegg
*	@version	3.1.0
*	@since	1.0
*/

final class Retailer implements IRetailer
{
	private final FlagHandler<IRetailer> flagHandler;
	{
		flagHandler = new FlagHandler<>(this);
	}

	private final int id;
	private final String name;
	private final String description;
	private final String address;
	private final String webSite;
	private final String eMail;
	private final String phone;
	private final String mobile;

	/*
	*	If true, this retailer ONLY supplies Products of their own brand
	*/
	private final boolean ownBrandOnly;
	private final LocalDateTime lastUpdated;
	private final LocalDateTime created;
	private final List<Comment> commentsList;

	/**
	*	Build an immutable Retailer entry one field at a time
	*/
	Retailer(
		final int id,
		final String name,
		final String description,
		final String address,
		final String webSite,
		final String eMail,
		final String phone,
		final String mobile,
		final boolean ownBrandOnly,
		final LocalDateTime lastUpdated,
		final LocalDateTime created,
		final Comment... comments)
	{
		this.id = id;
		this.name = name;
		this.description = description;
		this.address = address;
		this.webSite = webSite;
		this.eMail = eMail;
		this.phone = phone;
		this.mobile = mobile;
		this.ownBrandOnly = ownBrandOnly;
		this.lastUpdated = lastUpdated;
		this.created = created;
		if (comments != null && comments.length>0)
		{
			this.commentsList = new ArrayList<>(Arrays.asList(comments));
		}
		else
		{
			this.commentsList = Collections.emptyList();
		}
	}

	/**
	*	Build an immutable Retailer entry cloning the given Retailer entry but adding the comments list
	*/
	Retailer(
		final Retailer toCopy,
		final Comment... comments)
	{
		this(toCopy, Arrays.asList(comments));
	}

	/**
	*	Build an immutable Retailer entry cloning the given Retailer entry but adding the comments list
	*/
	Retailer(
		final Retailer toCopy,
		final List<Comment> comments)
	{
		this.id = toCopy.id;
		this.name = toCopy.name;
		this.description = toCopy.description;
		this.address = toCopy.address;
		this.webSite = toCopy.webSite;
		this.eMail = toCopy.eMail;
		this.phone = toCopy.phone;
		this.mobile = toCopy.mobile;
		this.ownBrandOnly = toCopy.ownBrandOnly;
		this.lastUpdated = toCopy.lastUpdated;
		this.created = toCopy.created;
		if (comments != null && comments.size()>0)
		{
			if (toCopy.commentsList.size()>0)
			{
				// append new comments to previous list
				this.commentsList = new ArrayList<>(toCopy.commentsList);
				this.commentsList.addAll(comments);
			}
			else
			{	// no comments on original item
				this.commentsList = new ArrayList<>(comments);
			}
		}
		else
		{	// no new comments to add
			this.commentsList = toCopy.commentsList;
		}
	}

	/**
	*	Build an immutable Retailer entry from a JSON dump.
	*	The dumped object must be complete (all non-nullable fields must have values) except
	*	the id field can be null or absent to indicate that this is a new item to be inserted.
	*
	*	@param	json	a JsonObject holding all the fields for a full initialisation.
	*/
	Retailer(JsonObject json)
	{
		this.id = json.getInt("id", -1);
		this.name = json.getString("name");
		if (json.containsKey("description") && !json.isNull("description"))
		{
			this.description = json.getString("description");
		}
		else
		{
			this.description = null;
		}

		if (json.containsKey("address") && !json.isNull("address"))
		{
			this.address = json.getString("address");
		}
		else
		{
			this.address = null;
		}

		if (json.containsKey("webSite") && !json.isNull("webSite"))
		{
			this.webSite = json.getString("webSite");
		}
		else
		{
			this.webSite = null;
		}

		if (json.containsKey("eMail") && !json.isNull("eMail"))
		{
			this.eMail = json.getString("eMail");
		}
		else
		{
			this.eMail = null;
		}

		if (json.containsKey("phone") && !json.isNull("phone"))
		{
			this.phone = json.getString("phone");
		}
		else
		{
			this.phone = null;
		}

		if (json.containsKey("mobile") && !json.isNull("mobile"))
		{
			this.mobile = json.getString("mobile");
		}
		else
		{
			this.mobile = null;
		}

		this.ownBrandOnly = json.getBoolean("ownBrandOnly", false);
		this.lastUpdated = LocalDateTime.parse(json.getString("lastUpdated"));
		this.created = LocalDateTime.parse(json.getString("created"));
		JsonArray jsonComments = json.getJsonArray("comments");
		if (jsonComments != null && !jsonComments.isEmpty())
		{// there is probably only one comment
			this.commentsList = new ArrayList<>(jsonComments.size());
			for (JsonObject ct : jsonComments.getValuesAs(JsonObject.class))
			{
				this.commentsList.add(new Comment(ct));
			}
		}
		else
		{
			this.commentsList = Collections.emptyList();
		}
	}	//constructor from JSON

	int getId()
	{
		return id;
	}
	@Override
	public Integer getKey()
	{
		return id;
	}

	@Override
	public NotebookEntryType getType()
	{
		return NotebookEntryType.RETAILER;
	}

	@Override
	public String getName()
	{
		return name;
	}

	@Override
	public Optional<String> getDescription()
	{
		return Optional.ofNullable(description);
	}

	@Override
	public Optional<String> getAddress()
	{
		return Optional.ofNullable(address);
	}

	@Override
	public Optional<String> getWebSite()
	{
		return Optional.ofNullable(webSite);
	}

	@Override
	public Optional<String> getEMail()
	{
		return Optional.ofNullable(eMail);
	}

	@Override
	public Optional<String> getPhone()
	{
		return Optional.ofNullable(phone);
	}

	@Override
	public Optional<String> getMobile()
	{
		return Optional.ofNullable(mobile);
	}

	@Override
	public boolean isOwnBrandOnly()
	{
		return ownBrandOnly;
	}

	@Override
	public LocalDateTime getLastUpdated()
	{
		return lastUpdated;
	}

	@Override
	public LocalDateTime getCreated()
	{
		return created;
	}

	@Override
	public boolean sameAs(INotebookEntry other)
	{
		if (other == null || other.getType() != this.getType())
		{
			return false;
		}
		return other.getKey().equals(this.getKey());
	}

	@Override
	public IProductBrandLister getProductBrand()
	{
		return new ProductBrandLister().retailer(this);
	}

	@Override
	public IPurchaseLister getPurchase()
	{
		return new PurchaseLister().retailer(this);
	}

	@Override
	public IRetailerHasProductLister getRetailerHasProduct()
	{
		return new RetailerHasProductLister().retailer(this);
	}

	@Override
	public List<IComment> getComments() {
		return new ArrayList<>(this.commentsList);
	}

	JsonObjectBuilder toJson(JsonBuilderFactory jsonFactory)
	{
		JsonObjectBuilder jsonBuilder = jsonFactory.createObjectBuilder();
		jsonBuilder.add("id", id);
		jsonBuilder.add("name", name);
		if (description != null)
		{
			jsonBuilder.add("description", description);
		}
		else
		{
			jsonBuilder.addNull("description");
		}
		if (address != null)
		{
			jsonBuilder.add("address", address);
		}
		else
		{
			jsonBuilder.addNull("address");
		}
		if (webSite != null)
		{
			jsonBuilder.add("webSite", webSite);
		}
		else
		{
			jsonBuilder.addNull("webSite");
		}
		if (eMail != null)
		{
			jsonBuilder.add("eMail", eMail);
		}
		else
		{
			jsonBuilder.addNull("eMail");
		}
		if (phone != null)
		{
			jsonBuilder.add("phone", phone);
		}
		else
		{
			jsonBuilder.addNull("phone");
		}
		if (mobile != null)
		{
			jsonBuilder.add("mobile", mobile);
		}
		else
		{
			jsonBuilder.addNull("mobile");
		}
		jsonBuilder.add("ownBrandOnly", ownBrandOnly);
		jsonBuilder.add("lastUpdated", lastUpdated.toString());
		jsonBuilder.add("created", created.toString());
		if (commentsList != null && !commentsList.isEmpty())
		{// no point writing an empty comments array (the loaders handle this)
			JsonArrayBuilder jsonComments = jsonFactory.createArrayBuilder();
			for (Comment ct : commentsList)
			{
				jsonComments.add(ct.toJson(jsonFactory));
			}
			jsonBuilder.add("comments", jsonComments);
		}
		jsonBuilder.add("JsonMode", "DUMP");
        jsonBuilder.add("JsonNBClass", "Retailer");
		return jsonBuilder;
	}	//	toJson

	@Override
	public void addPropertyChangeListener(final String propertyName, final PropertyChangeListener listener)
	{
		flagHandler.addPropertyChangeListener(propertyName, listener);
	}

	@Override
	public void removePropertyChangeListener(final String propertyName, final PropertyChangeListener listener)
	{
		flagHandler.removePropertyChangeListener(propertyName, listener);
	}

	@Override
	public void flagDeleted()
	{
		flagHandler.flagDeleted();
	}

	@Override
	public void flagReplaced(final IRetailer newValue)
	{
		flagHandler.flagReplaced(newValue, newValue::addPropertyChangeListener);
	}

	@Override
	public void flagChildDeleted(final IProductBrand child)
	{
		flagHandler.flagChildDeleted("ProductBrand", child);
	}

	@Override
	public void flagChildAdded(final IProductBrand child)
	{
		flagHandler.flagChildAdded("ProductBrand", child);
	}

	@Override
	public void flagChildDeleted(final IPurchase child)
	{
		flagHandler.flagChildDeleted("Purchase", child);
	}

	@Override
	public void flagChildAdded(final IPurchase child)
	{
		flagHandler.flagChildAdded("Purchase", child);
	}

	@Override
	public void flagChildDeleted(final IRetailerHasProduct child)
	{
		flagHandler.flagChildDeleted("RetailerHasProduct", child);
	}

	@Override
	public void flagChildAdded(final IRetailerHasProduct child)
	{
		flagHandler.flagChildAdded("RetailerHasProduct", child);
	}


	@Override
	public String toString() {
		return "Retailer: " + "id: " + id + ", " +
				"name: " + name + ", " +
				"description: " + description + ", " +
				"address: " + address + ", " +
				"webSite: " + webSite + ", " +
				"eMail: " + eMail + ", " +
				"phone: " + phone + ", " +
				"mobile: " + mobile + ", " +
				"ownBrandOnly: " + ownBrandOnly + ", " +
				"lastUpdated: " + lastUpdated + ", " +
				"created: " + created;
	}

}
