package me.anon.controller.adapter;

import android.annotation.SuppressLint;
import android.content.DialogInterface;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.Html;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.esotericsoftware.kryo.Kryo;
import com.prolificinteractive.materialcalendarview.CalendarDay;
import com.prolificinteractive.materialcalendarview.DayViewDecorator;
import com.prolificinteractive.materialcalendarview.DayViewFacade;
import com.prolificinteractive.materialcalendarview.MaterialCalendarView;
import com.prolificinteractive.materialcalendarview.OnDateSelectedListener;
import com.prolificinteractive.materialcalendarview.spans.DotSpan;

import org.jetbrains.annotations.NotNull;
import org.threeten.bp.Instant;
import org.threeten.bp.LocalDate;
import org.threeten.bp.ZoneId;

import java.io.File;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.PopupMenu;
import androidx.recyclerview.widget.RecyclerView;
import kotlinx.android.parcel.Parcelize;
import me.anon.grow.R;
import me.anon.lib.DateRenderer;
import me.anon.lib.TempUnit;
import me.anon.lib.Unit;
import me.anon.lib.ext.IntUtilsKt;
import me.anon.lib.helper.TimeHelper;
import me.anon.model.Action;
import me.anon.model.EmptyAction;
import me.anon.model.NoteAction;
import me.anon.model.Plant;
import me.anon.model.PlantStage;
import me.anon.model.StageChange;
import me.anon.model.Water;
import me.anon.view.ActionHolder;
import me.anon.view.ImageActionHolder;

/**
 * // TODO: Add class description
 *
 * @author 7LPdWcaW
 * @documentation // TODO Reference flow doc
 * @project GrowTracker
 */
public class ActionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements ItemTouchHelperAdapter
{
	public interface OnActionSelectListener
	{
		public void onActionDeleted(Action action);
		public void onActionEdit(Action action);
		public void onActionCopy(Action action);
		public void onActionDuplicate(Action action);
	}

	public interface OnItemSelectCallback
	{
		public void onItemSelected(Action action);
	}

	private OnDateSelectedListener onDateSelectedListener;
	private OnItemSelectCallback onItemSelectCallback;
	private OnActionSelectListener onActionSelectListener;
	@Nullable private Plant plant;
	private List<Action> actions = new ArrayList<>();
	private Unit measureUnit, deliveryUnit;
	private TempUnit tempUnit;
	private boolean showDate = true;
	private boolean showActions = true;
	public boolean showCalendar = false;
	private CalendarDay selectedFilterDate = null;

	public void setFilterDate(CalendarDay selectedFilterDate)
	{
		this.selectedFilterDate = selectedFilterDate;
	}

	public void setOnDateChangedListener(OnDateSelectedListener onDateSelectedListener)
	{
		this.onDateSelectedListener = onDateSelectedListener;
	}

	/**
	 * Dummy image action placeholder class
	 */
	@SuppressLint("ParcelCreator") @Parcelize
	private static class ImageAction extends Action implements Parcelable
	{
		@NotNull @Override public String getTypeStr()
		{
			return "image";
		}

		public ArrayList<String> images = new ArrayList<>();

		@Override public long getDate()
		{
			if (images.size() <= 0) return 0;
			return getImageDate(images.get(0));
		}

		@Override public int describeContents()
		{
			return 0;
		}

		@Override public void writeToParcel(Parcel parcel, int i)
		{

		}
	}

	public void setOnActionSelectListener(OnActionSelectListener onActionSelectListener)
	{
		this.onActionSelectListener = onActionSelectListener;
	}

	public void setOnItemSelectCallback(OnItemSelectCallback onItemSelectCallback)
	{
		this.onItemSelectCallback = onItemSelectCallback;
	}

	public void setShowDate(boolean showDate)
	{
		this.showDate = showDate;
	}

	public void setShowActions(boolean showActions)
	{
		this.showActions = showActions;
	}

	public Plant getPlant()
	{
		return plant;
	}

	public List<Action> getActions()
	{
		ArrayList<Action> actions = new ArrayList<>();
		for (Object item : this.actions)
		{
			if (item.getClass() != ImageAction.class) actions.add((Action)item);
		}

		return actions;
	}

	public Unit getMeasureUnit()
	{
		return measureUnit;
	}

	public Unit getDeliveryUnit()
	{
		return deliveryUnit;
	}

	public TempUnit getTempUnit()
	{
		return tempUnit;
	}

	public void setActions(@Nullable Plant plant, ArrayList<Action> actions)
	{
		setActions(plant, actions, new ArrayList<Class>());
	}

	public void setActions(@Nullable Plant plant, ArrayList<Action> actions, ArrayList<Class> exclude)
	{
		this.plant = plant;
		this.actions = new ArrayList<>();

		ArrayList<String> addedImages = new ArrayList<>();
		Collections.reverse(actions);
		for (Action item : actions)
		{
			// force planted stage to use plant date
			if (item instanceof StageChange && ((StageChange)item).getNewStage() == PlantStage.PLANTED)
			{
				item.setDate(plant.getPlantDate());
			}

			if (plant != null)
			{
				ArrayList<String> groupedImages = new ArrayList<>();
				for (String image : plant.getImages())
				{
					long imageDate = getImageDate(image);

					if (imageDate <= item.getDate() && !addedImages.contains(image))
					{
						groupedImages.add(image);
						addedImages.add(image);
					}
				}

				if (!groupedImages.isEmpty())
				{
					Collections.sort(groupedImages, new Comparator<String>()
					{
						@Override public int compare(String o1, String o2)
						{
							long o1Date = getImageDate(o1);
							long o2Date = getImageDate(o2);

							if (o2Date < o1Date) return -1;
							if (o2Date > o1Date) return 1;
							return 0;
						}
					});
					ImageAction imageAction = new ImageAction();
					imageAction.images = groupedImages;
					this.actions.add(imageAction);
				}
			}

			this.actions.add(item);
		}

		if (plant != null && addedImages.size() != plant.getImages().size())
		{
			ArrayList<String> remainingImages = new ArrayList<>(plant.getImages());
			remainingImages.removeAll(addedImages);

			Collections.sort(remainingImages, new Comparator<String>()
			{
				@Override public int compare(String o1, String o2)
				{
					long o1Date = getImageDate(o1);
					long o2Date = getImageDate(o2);

					if (o2Date < o1Date) return -1;
					if (o2Date > o1Date) return 1;
					return 0;
				}
			});

			ImageAction imageAction = new ImageAction();
			imageAction.images = remainingImages;
			this.actions.add(imageAction);
		}

		Collections.reverse(this.actions);
	}

	private static long getImageDate(String image)
	{
		File currentImage = new File(image);
		long fileDate = Long.parseLong(currentImage.getName().replaceAll("[^0-9]", ""));

		if (fileDate == 0)
		{
			fileDate = currentImage.lastModified();
		}

		return fileDate;
	}

	@Override public int getItemViewType(int position)
	{
		if (showCalendar && position == 0)
		{
			return 3;
		}

		position = position - (showCalendar ? 1 : 0);

		if (selectedFilterDate != null)
		{
			LocalDate actionDate = CalendarDay.from(LocalDate.from(Instant.ofEpochMilli(actions.get(position).getDate()).atZone(ZoneId.systemDefault()))).getDate();
			if (!selectedFilterDate.getDate().equals(actionDate)) return 0;
		}

		if (actions.get(position).getClass() == ImageAction.class)
		{
			return 2;
		}

		return 1;
	}

	@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType)
	{
		if (viewType == 3)
		{
			return new RecyclerView.ViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.calendar_item, viewGroup, false)){};
		}
		else if (viewType == 0)
		{
			return new RecyclerView.ViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.empty, viewGroup, false)){};
		}
		else if (viewType == 2)
		{
			return new ImageActionHolder(this, LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.action_image, viewGroup, false));
		}

		return new ActionHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.action_item, viewGroup, false));
	}

	@Override public void onBindViewHolder(final RecyclerView.ViewHolder vh, final int index)
	{
		if (getItemViewType(index) == 0) return;

		if (getItemViewType(index) == 3)
		{
			final MaterialCalendarView calendar = (MaterialCalendarView)vh.itemView;
			calendar.removeDecorators();
			calendar.addDecorator(new DayViewDecorator()
			{
				@Override public boolean shouldDecorate(CalendarDay calendarDay)
				{
					// find an action that is on this day
					for (Action action : plant.getActions())
					{
						LocalDate actionDate = CalendarDay.from(LocalDate.from(Instant.ofEpochMilli(action.getDate()).atZone(ZoneId.systemDefault()))).getDate();
						if (calendarDay.getDate().equals(actionDate))
						{
							return true;
						}
					}

					return false;
				}

				@Override public void decorate(DayViewFacade dayViewFacade)
				{
					dayViewFacade.addSpan(new DotSpan(6.0f, IntUtilsKt.resolveColor(R.attr.colorAccent, calendar.getContext())));
				}
			});
			calendar.setOnDateChangedListener(onDateSelectedListener);
			calendar.setSelectedDate(selectedFilterDate);
			calendar.setCurrentDate(selectedFilterDate);

			return;
		}

		final int actionIndex = index - (showCalendar ? 1 : 0);
		final Action action = actions.get(actionIndex);
		DateFormat dateFormat = android.text.format.DateFormat.getDateFormat(vh.itemView.getContext());
		DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(vh.itemView.getContext());
		TextView dateDay = null;
		TextView stageDay = null;

		if (vh instanceof ImageActionHolder)
		{
			final ImageActionHolder viewHolder = (ImageActionHolder)vh;
			viewHolder.bind(((ImageAction)action).images);
			dateDay = viewHolder.getDateDay();
			stageDay = viewHolder.getStageDay();
		}
		else if (vh instanceof ActionHolder)
		{
			final ActionHolder viewHolder = (ActionHolder)vh;

			if (measureUnit == null)
			{
				measureUnit = Unit.getSelectedMeasurementUnit(viewHolder.itemView.getContext());
			}

			if (deliveryUnit == null)
			{
				deliveryUnit = Unit.getSelectedDeliveryUnit(viewHolder.itemView.getContext());
			}

			if (tempUnit == null)
			{
				tempUnit = TempUnit.getSelectedTemperatureUnit(viewHolder.itemView.getContext());
			}

			if (action == null) return;

			dateDay = viewHolder.getDateDay();
			stageDay = viewHolder.getStageDay();

			Date actionDate = new Date(action.getDate());
			Calendar actionCalendar = GregorianCalendar.getInstance();
			actionCalendar.setTime(actionDate);
			String fullDateStr = dateFormat.format(actionDate) + " " + timeFormat.format(actionDate);
			String dateStr = vh.itemView.getContext().getString(R.string.ago, "<b>" + new DateRenderer(viewHolder.itemView.getContext()).timeAgo(action.getDate()).formattedDate + "</b>");

			if (actionIndex > 0)
			{
				long difference = actions.get(actionIndex - 1).getDate() - action.getDate();
				int days = (int)Math.round(((double)difference / 60d / 60d / 24d / 1000d));

				dateStr += " (-" + days + vh.itemView.getContext().getString(R.string.day_abbr) + ")";
			}

			viewHolder.getFullDate().setText(Html.fromHtml(fullDateStr));
			viewHolder.getDate().setText(Html.fromHtml(dateStr));
			viewHolder.getSummary().setVisibility(View.GONE);
			viewHolder.getCard().setCardBackgroundColor(IntUtilsKt.resolveColor(R.attr.colorSurface, viewHolder.itemView.getContext()));

			String summary = "";
			if (action.getClass() == Water.class)
			{
				summary += ((Water)action).getSummary(viewHolder.itemView.getContext());
				viewHolder.getCard().setCardBackgroundColor(viewHolder.itemView.getContext().getResources().getColor(R.color.light_blue));
				viewHolder.getName().setText(R.string.watered);
			}
			else if (action instanceof EmptyAction && ((EmptyAction)action).getAction() != null)
			{
				viewHolder.getName().setText(((EmptyAction)action).getAction().getPrintString());
				viewHolder.getCard().setCardBackgroundColor(((EmptyAction)action).getAction().getColour());
			}
			else if (action instanceof NoteAction)
			{
				viewHolder.getName().setText(R.string.note);
				viewHolder.getCard().setCardBackgroundColor(IntUtilsKt.getColor(R.color.light_grey, viewHolder.itemView.getContext()));
			}
			else if (action instanceof StageChange)
			{
				viewHolder.getName().setText(((StageChange)action).getNewStage().getPrintString());
				viewHolder.getCard().setCardBackgroundColor(0x9AB39DDB);
			}

			if (!TextUtils.isEmpty(action.getNotes()))
			{
				summary += summary.length() > 0 ? "<br/><br/>" : "";
				summary += action.getNotes();
			}

			if (summary.endsWith("<br/>"))
			{
				summary = summary.substring(0, summary.length() - "<br/>".length());
			}

			if (!TextUtils.isEmpty(summary))
			{
				viewHolder.getSummary().setText(Html.fromHtml(summary));
				viewHolder.getSummary().setVisibility(View.VISIBLE);
			}

			if (showActions)
			{
				viewHolder.getOverflow().setVisibility(View.VISIBLE);
				viewHolder.getOverflow().setOnClickListener(new View.OnClickListener()
				{
					@Override public void onClick(final View v)
					{
						PopupMenu menu = new PopupMenu(v.getContext(), v, Gravity.BOTTOM);
						menu.inflate(R.menu.event_overflow);
						menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener()
						{
							@Override public boolean onMenuItemClick(MenuItem item)
							{
								if (item.getItemId() == R.id.duplicate)
								{
									if (onActionSelectListener != null)
									{
										Kryo kryo = new Kryo();
										onActionSelectListener.onActionDuplicate(kryo.copy(action));
									}

									return true;
								}
								else if (item.getItemId() == R.id.copy)
								{
									if (onActionSelectListener != null)
									{
										Kryo kryo = new Kryo();
										onActionSelectListener.onActionCopy(kryo.copy(action));
									}

									return true;
								}
								else if (item.getItemId() == R.id.edit)
								{
									if (onActionSelectListener != null)
									{
										onActionSelectListener.onActionEdit(action);
									}

									return true;
								}
								else if (item.getItemId() == R.id.delete)
								{
									new AlertDialog.Builder(v.getContext())
										.setTitle(R.string.delete_event_dialog_title)
										.setMessage(Html.fromHtml(v.getContext().getString(R.string.confirm_delete_item_message) + " <b>" + viewHolder.getName().getText() + "</b>?"))
										.setPositiveButton(R.string.confirm_positive, new DialogInterface.OnClickListener()
										{
											@Override public void onClick(DialogInterface dialog, int which)
											{
												if (onActionSelectListener != null)
												{
													onActionSelectListener.onActionDeleted(action);
												}
											}
										})
										.setNegativeButton(R.string.confirm_negative, null)
										.show();

									return true;
								}

								return false;
							}
						});

						menu.show();
					}
				});
			}
			else
			{
				viewHolder.getOverflow().setVisibility(View.GONE);
			}
		}

		if (showDate)
		{
			// plant date & stage
			Date actionDate = new Date(action.getDate());
			Calendar actionCalendar = GregorianCalendar.getInstance();
			actionCalendar.setTime(actionDate);

			String dateDayStr = actionCalendar.get(Calendar.DAY_OF_MONTH) + " " + actionCalendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault());

			if (dateDay != null)
			{
				String lastDateStr = "";

				if (actionIndex - 1 >= 0)
				{
					Date lastActionDate = new Date(actions.get(actionIndex - 1).getDate());
					Calendar lastActionCalendar = GregorianCalendar.getInstance();
					lastActionCalendar.setTime(lastActionDate);
					lastDateStr = lastActionCalendar.get(Calendar.DAY_OF_MONTH) + " " + lastActionCalendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault());
				}

				if (!lastDateStr.equalsIgnoreCase(dateDayStr))
				{
					dateDay.setText(Html.fromHtml(dateDayStr));

					String stageDayStr = "";

					StageChange lastChange = null;
					long currentChangeDate = action.getDate();

					for (int aIndex = actionIndex; aIndex < actions.size(); aIndex++)
					{
						if (actions.get(aIndex) instanceof StageChange)
						{
							if (lastChange == null)
							{
								lastChange = (StageChange)actions.get(aIndex);
								break;
							}
						}
					}

					int totalDays = (int)TimeHelper.toDays(Math.abs(action.getDate() - plant.getPlantDate()));
					stageDayStr += (totalDays == 0 ? 1 : totalDays);

					if (lastChange != null)
					{
						int currentDays = (int)TimeHelper.toDays(Math.abs(currentChangeDate - lastChange.getDate()));
						currentDays = (currentDays == 0 ? 1 : currentDays);
						stageDayStr += "/" + currentDays + dateDay.getContext().getString(lastChange.getNewStage().getPrintString()).substring(0, 1).toLowerCase();
					}

					stageDay.setText(stageDayStr);
				}
				else
				{
					dateDay.setText("");
					stageDay.setText("");
				}

				((View)dateDay.getParent()).setVisibility(View.VISIBLE);
			}
		}
		else
		{
			if (dateDay != null && stageDay != null)
			{
				((View)dateDay.getParent()).setVisibility(View.GONE);
			}
		}

		if (onItemSelectCallback != null)
		{
			if (vh instanceof ActionHolder)
			{
				((ActionHolder)vh).getCard().setClickable(true);
				((ActionHolder)vh).getCard().setFocusable(true);

				((ActionHolder)vh).getCard().setOnClickListener(new View.OnClickListener()
				{
					@Override public void onClick(View v)
					{
						onItemSelectCallback.onItemSelected(action);
					}
				});
			}
		}
	}

	@Override public int getItemCount()
	{
		return actions.size() + (showCalendar ? 1 : 0);
	}

	@Override public void onItemMove(int fromPosition, int toPosition)
	{
		if (fromPosition < toPosition)
		{
			for (int index = fromPosition; index < toPosition; index++)
			{
				Collections.swap(actions, index, index + 1);
			}
		}
		else
		{
			for (int index = fromPosition; index > toPosition; index--)
			{
				Collections.swap(actions, index, index - 1);
			}
		}

		notifyItemMoved(fromPosition, toPosition);
	}

	@Override public void onItemDismiss(int position)
	{
		actions.remove(position);
		notifyItemRemoved(position);
	}
}
