package com.p3.bewoplaner.page.chat;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.github.lzyzsd.circleprogress.DonutProgress;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.utils.DateUtils;
import com.p3.bewoplaner.utils.ui.ViewUtils;

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.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;

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

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Optional;
import hani.momanii.supernova_emoji_library.Helper.EmojiconTextView;

/**
 * Created by Rares Teodorescu on 21/02/2017.
 */
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<Message> mMessages = new ArrayList<>();
    private final List<Integer> mDatePositions = new ArrayList<>();
    final List<Date> mDateList = new ArrayList<>();
    final List<Integer> mPositionOffset = new ArrayList<>();
    private boolean mIsLoading;
    private int mLastOffset = 0;
    private Calendar mCurrentDay;
    private Calendar mLastDay;
    private MessageClickListener mMessageClickListener;
    private boolean mIsRetrying;
    protected HashMap<Integer, Integer> mDownloadProgressMap = new HashMap<>();

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

    void setMessages(ArrayList<Message> messages) {
        mMessages.clear();
        mDatePositions.clear();
        mDateList.clear();
        mPositionOffset.clear();
        mIsLoading = false;
        mLastOffset = 0;
        mCurrentDay = Calendar.getInstance();
        mLastDay = Calendar.getInstance();
        mIsRetrying = false;
        if (!messages.isEmpty()) {
            mPositionOffset.add(0);
            mDateList.add(new Date(messages.get(0).createdAt));
        }
        int messageSize = messages.size();
        for (int i = 1; i < messageSize; i++) {
            mCurrentDay.setTimeInMillis(messages.get(i).createdAt);
            mLastDay.setTimeInMillis(messages.get(i - 1).createdAt);
            if (mCurrentDay.get(Calendar.DAY_OF_YEAR) != mLastDay.get(Calendar.DAY_OF_YEAR)) {
                mDatePositions.add(i + mLastOffset);
                mPositionOffset.add(0);
                mDateList.add(mLastDay.getTime());
                mLastOffset++;
            }
            mPositionOffset.add(mLastOffset);
            mDateList.add(new Date(messages.get(i).createdAt));
        }
        mMessages.addAll(messages);
        notifyDataSetChanged();
    }

    void addMessages(ArrayList<Message> messages) {
        if (messages == null || messages.isEmpty() || mMessages.equals(messages)) {
            return;
        }
        int currentMessagesCount = mMessages.size();
        int lastOffsetBeforeParsing = mLastOffset;
        int datesInserted = 0;
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(messages.get(0).createdAt);
        if (mCurrentDay.get(Calendar.DAY_OF_YEAR) != calendar.get(Calendar.DAY_OF_YEAR)) {
            mDatePositions.add(currentMessagesCount + mLastOffset);
            datesInserted++;
            mDateList.add(new Date(messages.get(0).createdAt));
            mPositionOffset.add(0);
            mLastOffset++;
        }
        mPositionOffset.add(mLastOffset);
        mDateList.add(new Date(messages.get(0).createdAt));
        int messageSize = messages.size();
        for (int i = 1; i < messageSize; i++) {
            mCurrentDay.setTimeInMillis(messages.get(i).createdAt);
            mLastDay.setTimeInMillis(messages.get(i - 1).createdAt);
            if (mCurrentDay.get(Calendar.DAY_OF_YEAR) != mLastDay.get(Calendar.DAY_OF_YEAR)) {
                mDatePositions.add(currentMessagesCount + mLastOffset + i);
                datesInserted++;
                mPositionOffset.add(0);
                mDateList.add(mLastDay.getTime());
                mLastOffset++;
            }
            mPositionOffset.add(mLastOffset);
            mDateList.add(new Date(messages.get(i).createdAt));
        }
        mMessages.addAll(messages);
        notifyItemRangeInserted(currentMessagesCount + lastOffsetBeforeParsing, messages.size() + datesInserted);
    }

    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 (mMessages.contains(message)) {
            return;
        }

        ArrayList<Message> currentMessages = new ArrayList<>();
        currentMessages.add(message);
        currentMessages.addAll(mMessages);
        mMessages.clear();
        mMessages.addAll(currentMessages);

        Calendar lastDay = Calendar.getInstance();
        if (mMessages.size() > 1) {
            lastDay.setTimeInMillis(mMessages.get(1).createdAt);
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(message.createdAt);
        if (calendar.get(Calendar.DAY_OF_YEAR) != lastDay.get(Calendar.DAY_OF_YEAR)) {
            // New date inserted
            setMessages(new ArrayList<>(mMessages));
            return;
        }

        List<Integer> positionOffset = new ArrayList<>();
        positionOffset.add(0);
        positionOffset.addAll(mPositionOffset);
        mPositionOffset.clear();
        mPositionOffset.addAll(positionOffset);

        List<Date> currentDateList = new ArrayList<>();
        currentDateList.add(new Date(message.createdAt));
        currentDateList.addAll(mDateList);
        mDateList.clear();
        mDateList.addAll(currentDateList);

        List<Integer> currentDatePositions = new ArrayList<>();
        boolean newDateAdded = false;
        if (calendar.get(Calendar.DAY_OF_YEAR) != lastDay.get(Calendar.DAY_OF_YEAR)) {
            currentDatePositions.add(1);
            newDateAdded = true;
        }
        for (int i = 0, size = mDatePositions.size(); i < size; i++) {
            currentDatePositions.add(mDatePositions.get(i) + (newDateAdded ? 2 : 1));
        }
        mDatePositions.clear();
        mDatePositions.addAll(currentDatePositions);

        notifyItemInserted(0);
    }

    void setLoading(boolean loading) {
        mIsLoading = loading;
        if (loading) {
            notifyItemRangeInserted(getItemCount(), 1);
        } else {
            notifyItemRemoved(getItemCount() - 1);
        }
    }

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

    void updateMessage(Message newMessage, String oldMessageId) {
        int index = -1;
        for (int i = 0; i < mMessages.size(); i++) {
            if (oldMessageId.equals(mMessages.get(i).id)) {
                index = i;
                break;
            }
        }
        if (index == -1) {
            Log.e("BaseChatAdapter", "message not found!! " + newMessage.toString());
            return;
        }
        mMessages.set(index, newMessage);
        notifyItemChanged(index);
    }

    @Override
    public int getItemViewType(int position) {
        if (mDatePositions.contains(position)) {
            return DATE_TYPE;
        }
        if (position == mMessages.size() + mLastOffset) {
            return LOADING_TYPE;
        }
        return mMessages.get(position - mPositionOffset.get(position)).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 < mMessages.size(); i++) {
            Message message = mMessages.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;
        }
        mMessages.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)) {
                //noinspection ConstantConditions
                viewHolder.contactImageView.setImageResource(R.drawable.avatar);
            } else {
                //noinspection ConstantConditions
                Glide.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.message)) {
            viewHolder.messageTextView.setVisibility(View.GONE);
            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.endsWith(".png")
                        && !message.filePath.endsWith(".jpg")
                        && !message.filePath.endsWith(".JPG")) {
                    viewHolder.filenameTextView.setVisibility(View.VISIBLE);
                    viewHolder.filenameTextView.setText(message.originalFilename);
                } else {
                    viewHolder.filenameTextView.setVisibility(View.GONE);
                }
            } else if (message.filePath.endsWith(".png")
                    || message.filePath.endsWith(".jpg")
                    || message.filePath.endsWith(".JPG")) {
                viewHolder.donutProgress.setVisibility(View.GONE);
                viewHolder.fileImageView.setVisibility(View.VISIBLE);
                viewHolder.filenameTextView.setVisibility(View.GONE);
                viewHolder.fileImageView.setImageDrawable(null);
                Glide.with(viewHolder.fileImageView.getContext())
                        .load(message.smallerImage)
                        .placeholder(R.drawable.document)
                        .dontAnimate()
                        .override(500, 500)
                        .diskCacheStrategy(DiskCacheStrategy.ALL)
                        .into(viewHolder.fileImageView);
            } else {
                viewHolder.donutProgress.setVisibility(View.GONE);
                viewHolder.fileImageView.setVisibility(View.VISIBLE);
                viewHolder.filenameTextView.setVisibility(View.VISIBLE);
                viewHolder.fileImageView.setImageDrawable(null);
                viewHolder.fileImageView.setImageResource(R.drawable.document);
                viewHolder.filenameTextView.setText(message.originalFilename);
            }
        } 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;

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

        @Optional
        @OnClick(R.id.chat_item_sending_failed)
        void retrySendingClicked() {
            if (mIsRetrying) {
                return;
            }
            mIsRetrying = true;
            int currentPosition = getAdapterPosition();
            Message messageToResend = mMessages.get(currentPosition - mPositionOffset.get(currentPosition));
            //noinspection ConstantConditions
            statusProgressBar.setVisibility(View.VISIBLE);
            ViewUtils.setProgressBarColorToOrange(statusProgressBar);
            //noinspection ConstantConditions
            statusImageView.setVisibility(View.GONE);
            //noinspection ConstantConditions
            sendingFailedTextView.setVisibility(View.GONE);
            mMessageClickListener.resendMessage(messageToResend);
        }

        @Optional
        @OnClick(R.id.chat_item_file)
        void fileClicked() {
            int currentPosition = getAdapterPosition();
            Message message = mMessages.get(currentPosition - mPositionOffset.get(currentPosition));
            if (message.status != Message.Status.DELIVERED) {
                mMessageClickListener.localFileClicked(Uri.parse(message.uri));
                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);

        void resendMessage(Message message);
    }
}
