package de.beyondsoft.ownchat.page.chat;

import android.net.Uri;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.github.lzyzsd.circleprogress.DonutProgress;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Optional;

import de.beyondsoft.ownchat.R;
import de.beyondsoft.ownchat.model.DateItem;
import de.beyondsoft.ownchat.model.Message;
import de.beyondsoft.ownchat.model.RecyclerViewItem;
import de.beyondsoft.ownchat.utils.DateUtils;
import de.beyondsoft.ownchat.utils.Utils;
import de.beyondsoft.ownchat.utils.ui.GlideApp;
import de.beyondsoft.ownchat.utils.ui.ViewUtils;

import hani.momanii.supernova_emoji_library.Helper.EmojiconTextView;

public abstract class BaseChatAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    final static int DATE_TYPE       = 0;
    final static int LEFT_CHAT_TYPE  = 1;
    final static int RIGHT_CHAT_TYPE = 2;
    final static int LOADING_TYPE    = 3;

    private long mUserId;

    final ArrayList<RecyclerViewItem> mItemList = new ArrayList<>();
    private final ArrayList<RecyclerViewItem> mTempItemList = new ArrayList<>();

    private boolean mIsLoading;
    private Calendar mCurrentDay;
    private Calendar mNextDay;
    private MessageClickListener mMessageClickListener;
    private boolean mIsRetrying;
    private HashMap<Integer, Integer> mDownloadProgressMap = new HashMap<>();

    BaseChatAdapter(long userId) {
        mUserId = userId;
        mCurrentDay = Calendar.getInstance();
        mNextDay = Calendar.getInstance();
    }

    void setMessages(ArrayList<Message> messages, boolean shouldAddFirstDate) {
        mItemList.clear();
        mIsLoading = false;
        mCurrentDay = Calendar.getInstance();
        mNextDay = Calendar.getInstance();
        mIsRetrying = false;

        for(int i = 0; i < messages.size(); i++) {
            mCurrentDay.setTimeInMillis(messages.get(i).createdAt);

            mNextDay.setTimeInMillis((i == messages.size() - 1) ? mCurrentDay.getTimeInMillis() : messages.get(i + 1).createdAt);

            mItemList.add(messages.get(i));

            if(Utils.isBefore(mNextDay, mCurrentDay)) {
                mItemList.add(new DateItem(mCurrentDay.getTime()));
            }

            if(shouldAddFirstDate && i == (messages.size() - 1)) {
                mItemList.add(new DateItem(new Date(messages.get(i).createdAt)));
            }
        }

        notifyDataSetChanged();
    }

    void addMessagesWhenScrollingDown(ArrayList<Message> messages) {
        mIsLoading = false;
        mCurrentDay = Calendar.getInstance();
        mIsRetrying = false;

        mTempItemList.clear();

        for(int i = 0; i < messages.size(); i++) {
            if(mItemList.contains(messages.get(i))) {
                continue;
            }

            mCurrentDay.setTimeInMillis(messages.get(i).createdAt);
            mNextDay.setTimeInMillis((i == messages.size() - 1) ? mCurrentDay.getTimeInMillis() : messages.get(i + 1).createdAt);

            mTempItemList.add(messages.get(i));

            if(Utils.isBefore(mNextDay, mCurrentDay)) {
                mTempItemList.add(new DateItem(mCurrentDay.getTime()));
            }
        }

        mItemList.addAll(0, mTempItemList);
        notifyItemRangeInserted(0, mTempItemList.size());
    }

    void addMessages(ArrayList<Message> messages) {
        if(messages == null || messages.isEmpty() || mItemList.equals(messages)) {
            return;
        }

        ArrayList<Message> filteredList = new ArrayList<>();

        for(Message message : messages) {
            if(!mItemList.contains(message)) {
                filteredList.add(message);
            }
        }

        int datesInserted = 0;

        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(filteredList.get(0).createdAt);

        for(int i = 0; i < filteredList.size(); i++) {
            mCurrentDay.setTimeInMillis(filteredList.get(i).createdAt);
            mNextDay.setTimeInMillis((i == filteredList.size() - 1) ? mCurrentDay.getTimeInMillis() : filteredList.get(i + 1).getCreatedAt());

            mItemList.add(filteredList.get(i));

            if(Utils.isBefore(mNextDay, mCurrentDay)) {
                mItemList.add(new DateItem(mCurrentDay.getTime()));
                datesInserted++;
            }
        }

        notifyItemRangeInserted(mItemList.size() - 1, filteredList.size() + datesInserted);
    }

    void addMessagesForSearchResult(ArrayList<Message> messages) {
        setMessages(messages, true);
    }

    void setItemProgress(int itemPosition, int progress) {
        mDownloadProgressMap.put(itemPosition, progress);
        notifyItemChanged(itemPosition);
    }

    void stopItemProgress(int itemPosition) {
        mDownloadProgressMap.remove(itemPosition);
        notifyItemChanged(itemPosition);
    }

    void resetItemsProgress() {
        mDownloadProgressMap.clear();
        notifyDataSetChanged();
    }

    void addMessage(Message message) {
        if(mItemList.contains(message)) {
            return;
        }

        Calendar oldNewestMessageDay = Calendar.getInstance();

        if(mItemList.size() > 0) {
            oldNewestMessageDay.setTimeInMillis(mItemList.get(0).getCreatedAt());

            Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(message.createdAt);

            if(Utils.isBefore(oldNewestMessageDay, calendar)) {
                mItemList.add(0, new DateItem(calendar.getTime()));
            }
        }

        mItemList.add(0, message);

        notifyItemInserted(0);
    }

    void setLoading(boolean loading) {
        mIsLoading = loading;

        if(loading) {
            notifyItemRangeInserted(getItemCount(), 1);
        } else {
            notifyItemRemoved(getItemCount() - 1);
        }
    }

    int getPosition(Message message) {
        int position = 0;

        for(int i = 0; i < mItemList.size(); i++) {
            RecyclerViewItem item = mItemList.get(i);

            if(item.equals(message)) {
                position = i;
                break;
            }
        }

        return position;
    }

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

    void updateMessage(Message newMessage, String oldMessageId) {
        int index = -1;
        for(int i = 0; i < mItemList.size(); i++) {
            if(oldMessageId.equals(((Message) mItemList.get(i)).id)) {
                index = i;
                break;
            }
        }

        if(index == -1) {
            Log.e("BaseChatAdapter", "message not found! " + newMessage.toString());
            return;
        }

        mItemList.set(index, newMessage);
        notifyItemChanged(index);
    }

    @Override
    public int getItemViewType(int position) {
        if(position == mItemList.size()) {
            return LOADING_TYPE;
        }

        RecyclerViewItem item = mItemList.get(position);

        if(item.getClass().equals(DateItem.class)) {
            return DATE_TYPE;
        }

        Message message = (Message) item;
        return message.senderId == mUserId ? RIGHT_CHAT_TYPE : LEFT_CHAT_TYPE;
    }

    void setMessageClickListener(MessageClickListener messageClickListener) {
        mMessageClickListener = messageClickListener;
    }

    void enableMessageRetry(String messageId) {
        mIsRetrying = false;
        Message myMessage = null;
        int index = -1;

        for(int i = 0; i < mItemList.size(); i++) {
            if(!mItemList.get(i).getClass().equals(Message.class)) {
                continue;
            }

            Message message = (Message) mItemList.get(i);
            if(messageId.equals(message.id)) {
                index = i;
                myMessage = message;
                myMessage.status = Message.Status.FAILED;
                break;
            }
        }

        if(myMessage == null) {
            Log.e("BaseChatAdapter", "Message not found! " + messageId);
            return;
        }

        mItemList.set(index, myMessage);
        notifyItemChanged(index);
    }

    void setSingleMessage(MessageViewHolder viewHolder, Message message, int position, boolean isLeft) {
        setMessage(viewHolder, message, position, false, isLeft);
    }

    void setGroupMessage(MessageViewHolder viewHolder, Message message, int position, boolean isLeft) {
        setMessage(viewHolder, message, position, true, isLeft);
    }

    private void setMessage(MessageViewHolder viewHolder, Message message, int position, boolean isGroup, boolean isLeft) {
        if(isGroup) {
            viewHolder.timeTextView.setText(String.format(Locale.getDefault(), "%s | %s", isLeft ? message.senderName : viewHolder.timeTextView.getContext().getString(R.string.chat_you_name), DateUtils.toHourFormatHour(message.createdAt)));

            if(TextUtils.isEmpty(message.senderAvatar)) {
                viewHolder.contactImageView.setImageResource(R.drawable.avatar);
            } else {
                GlideApp.with(viewHolder.contactImageView.getContext())
                        .load(message.senderAvatar)
                        .placeholder(R.drawable.avatar)
                        .diskCacheStrategy(DiskCacheStrategy.ALL)
                        .into(viewHolder.contactImageView);
            }
        } else {
            viewHolder.timeTextView.setText(DateUtils.toHourFormatHour(message.createdAt));
        }

        if(!TextUtils.isEmpty(message.filePath)) {
            viewHolder.messageTextView.setVisibility(View.GONE);

            int maxWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, viewHolder.filenameTextView.getResources().getDisplayMetrics());

            if(mDownloadProgressMap.containsKey(position)) {
                viewHolder.fileImageView.setVisibility(View.GONE);
                viewHolder.donutProgress.setVisibility(View.VISIBLE);
                viewHolder.donutProgress.setProgress(mDownloadProgressMap.get(position));
                viewHolder.donutProgress.setText(String.format(Locale.getDefault(), "%d %%", mDownloadProgressMap.get(position)));

                if(!message.filePath.toLowerCase().endsWith(".png") && !message.filePath.toLowerCase().endsWith(".jpg") && !message.filePath.toLowerCase().endsWith(".gif")) {
                    viewHolder.filenameTextView.setVisibility(View.VISIBLE);
                    viewHolder.filenameTextView.setText(message.originalFilename);
                    viewHolder.filenameTextView.setMaxWidth(maxWidth);
                } else {
                    viewHolder.filenameTextView.setVisibility(View.GONE);
                }
            } else {
                viewHolder.donutProgress.setVisibility(View.GONE);
                viewHolder.fileImageView.setVisibility(View.VISIBLE);

                if(!message.filePath.toLowerCase().endsWith(".png") && !message.filePath.toLowerCase().endsWith(".jpg") && !message.filePath.toLowerCase().endsWith(".gif")) {
                    viewHolder.filenameTextView.setVisibility(View.VISIBLE);
                    viewHolder.filenameTextView.setText(message.originalFilename);
                    viewHolder.filenameTextView.setMaxWidth(maxWidth);
                } else {
                    viewHolder.filenameTextView.setVisibility(View.GONE);
                }

                viewHolder.fileImageView.setImageDrawable(null);

                GlideApp.with(viewHolder.fileImageView.getContext())
                        .load(TextUtils.isEmpty(message.smallerImage) ? message.filePath : message.smallerImage)
                        .placeholder(R.drawable.document)
                        .override(500, 500)
                        .diskCacheStrategy(DiskCacheStrategy.ALL)
                        .into(viewHolder.fileImageView);
            }

            if(!TextUtils.isEmpty(message.message)) {
                viewHolder.messageTextView.setVisibility(View.VISIBLE);
                viewHolder.messageTextView.setMaxWidth(maxWidth);
                viewHolder.messageTextView.setText(message.message);
            }
        } else {
            viewHolder.donutProgress.setVisibility(View.GONE);
            viewHolder.fileImageView.setVisibility(View.GONE);
            viewHolder.filenameTextView.setVisibility(View.GONE);
            viewHolder.messageTextView.setVisibility(View.VISIBLE);
            viewHolder.messageTextView.setText(message.message);
        }
    }

    static class DateViewHolder extends RecyclerView.ViewHolder {
        @BindView(R.id.chat_item_date)
        TextView dateTextView;

        DateViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }
    }

    class MessageViewHolder extends RecyclerView.ViewHolder {

        @Nullable
        @BindView(R.id.chat_item_message_status_progress_bar)
        ProgressBar statusProgressBar;

        @Nullable
        @BindView(R.id.chat_item_message_status)
        ImageView statusImageView;

        @Nullable
        @BindView(R.id.chat_item_sending_failed)
        TextView sendingFailedTextView;

        @Nullable
        @BindView(R.id.chat_item_contact_image)
        ImageView contactImageView;

        @BindView(R.id.chat_item_time)
        TextView timeTextView;

        @BindView(R.id.chat_item_message)
        EmojiconTextView messageTextView;

        @BindView(R.id.chat_item_file)
        ImageView fileImageView;

        @BindView(R.id.chat_item_progress)
        DonutProgress donutProgress;

        @BindView(R.id.chat_item_file_name)
        TextView filenameTextView;

        @BindView(R.id.chat_item_container)
        RelativeLayout chatItemContainer;

        MessageViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }

        @Optional
        @OnClick(R.id.chat_item_container)
        void jumpToMessage() {
            int currentPosition = getAdapterPosition();

            int position = currentPosition;

            // TODO: Needs to be improved! (or not?)
            if(!mItemList.get(currentPosition).getClass().equals(Message.class)) {
                for(int i = currentPosition; i > 0; i--) {
                    if(mItemList.get(i).getClass().equals(Message.class)) {
                        position = i;
                    }
                }
            }

            if(!mItemList.get(position).getClass().equals(Message.class)) {
                return;
            }

            Message messageToJumpTo = (Message) mItemList.get(position);

            mMessageClickListener.jumpToMessage(messageToJumpTo);
        }

        @Optional
        @OnClick(R.id.chat_item_sending_failed)
        void retrySendingClicked() {
            if(mIsRetrying) {
                return;
            }

            mIsRetrying = true;
            int currentPosition = getAdapterPosition();
            int position = currentPosition;

            // TODO: Needs to be improved!
            if(!mItemList.get(currentPosition).getClass().equals(Message.class)) {
                for(int i = currentPosition; i > 0; i--) {
                    if(mItemList.get(i).getClass().equals(Message.class)) {
                        position = i;
                    }
                }
            }

            if(!mItemList.get(position).getClass().equals(Message.class)) {
                return;
            }

            Message messageToResend = (Message) mItemList.get(position);

            if(statusProgressBar != null) {
                statusProgressBar.setVisibility(View.VISIBLE);
            }

            ViewUtils.setProgressBarColor(statusProgressBar, R.color.colorWhite);

            if(statusImageView != null) {
                statusImageView.setVisibility(View.GONE);
            }

            if(sendingFailedTextView != null) {
                sendingFailedTextView.setVisibility(View.GONE);
            }

            mMessageClickListener.resendMessage(messageToResend);
        }

        @Optional
        @OnClick(R.id.chat_item_file)
        void fileClicked() {
            // TODO: Needs to be improved! (or not?)
            int currentPosition = getAdapterPosition();
            int position = currentPosition;

            if(!mItemList.get(currentPosition).getClass().equals(Message.class)) {
                for(int i = currentPosition; i > 0; i--) {
                    if(mItemList.get(i).getClass().equals(Message.class)) {
                        position = i;
                    }
                }
            }

            if(!mItemList.get(position).getClass().equals(Message.class)) {
                return;
            }

            Message message = (Message) mItemList.get(position);

            if(message.status != Message.Status.DELIVERED) {
                mMessageClickListener.localFileClicked(Uri.parse(message.uri), message);
                return;
            }

            if(mDownloadProgressMap.containsKey(currentPosition)) {
                return;
            }

            mDownloadProgressMap.put(currentPosition, 0);
            mMessageClickListener.fileClicked(message, getAdapterPosition());
        }
    }

    static class ProgressViewHolder extends RecyclerView.ViewHolder {
        ProgressViewHolder(View itemView) {
            super(itemView);
        }
    }

    interface MessageClickListener {
        void fileClicked(Message message, int itemPosition);

        void localFileClicked(Uri uri, Message message);

        void resendMessage(Message message);

        void jumpToMessage(Message message);
    }
}
