package com.leavjenn.hews.ui.adapter;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.net.Uri;
import android.support.customtabs.CustomTabsIntent;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.text.style.ClickableSpan;
import android.text.style.URLSpan;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.leavjenn.hews.Constants;
import com.leavjenn.hews.R;
import com.leavjenn.hews.misc.Utils;
import com.leavjenn.hews.misc.ChromeCustomTabsHelper;
import com.leavjenn.hews.misc.HTMLCodeTagHandler;
import com.leavjenn.hews.misc.SharedPrefsManager;
import com.leavjenn.hews.model.Comment;
import com.leavjenn.hews.model.HNItem;
import com.leavjenn.hews.model.Post;
import com.leavjenn.hews.ui.comment.CommentsActivity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CommentAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private static final int VIEW_TYPE_HEADER = 0;
    private static final int VIEW_TYPE_COMMENT = 1;
    private static final int VIEW_TYPE_FOOTER = 2;

    private int mLoadingState;
    private int mCommentIndentColorOrange, mCommentIndentColorBg, mCommentIndentWidth;
    private Typeface mFont;
    private float mTextSize, mLineHeight;
    private HTMLCodeTagHandler mHTMLCodeTagHandler;
    private Map<Long, List<Comment>> mCollapsedChildrenCommentsIndex;
    private Map<Long, List<Comment>> mCollapsedOlderCommentsIndex;
    private Context mContext;
    private RecyclerView mRecyclerView;
    private ArrayList<HNItem> mItemList;
    private ArrayList<Comment> mCommentList;
    private SharedPreferences prefs;

    public CommentAdapter(Context context, RecyclerView recyclerView) {
        mContext = context;
        mRecyclerView = recyclerView;
        mItemList = new ArrayList<>();
        mCommentList = new ArrayList<>();
        mHTMLCodeTagHandler = new HTMLCodeTagHandler();
        mCollapsedChildrenCommentsIndex = new HashMap<>();
        mCollapsedOlderCommentsIndex = new HashMap<>();
        prefs = ((CommentsActivity) mContext).getSharedPreferences();
        updateCommentPrefs();
        setCommentIndentStyle();
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        View v;
        RecyclerView.ViewHolder viewHolder = null;
        switch (viewType) {
            case VIEW_TYPE_COMMENT:
                v = LayoutInflater.from(viewGroup.getContext()).inflate(
                    R.layout.list_item_comment, viewGroup, false);
                CommentViewHolder vh = new CommentViewHolder(v, setupViewHolderClickListener());
                vh.tvComment.setTypeface(mFont);
                vh.tvComment.setPaintFlags(vh.tvComment.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
                vh.tvComment.setTextSize(mTextSize);
                vh.tvComment.setLineSpacing(0, mLineHeight);
                vh.setCommentIndentStripeStyle(mCommentIndentColorOrange,
                    mCommentIndentColorBg, mCommentIndentWidth);
                viewHolder = vh;
                break;
            case VIEW_TYPE_HEADER:
                v = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.list_item_comment_header, viewGroup, false);
                viewHolder = new CommentHeaderViewHolder(v);
                break;
            case VIEW_TYPE_FOOTER:
                v = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.list_item_footer, viewGroup, false);
                viewHolder = new FooterViewHolder(v);
        }
        return viewHolder;
    }

    private CommentViewHolder.ViewHolderClickListener setupViewHolderClickListener() {
        return new CommentViewHolder.ViewHolderClickListener() {
            @Override
            public void onClick(int position) {
                if (isInBetweenCommentsCollapsed(position)) {
                    expandInBetweenComments(position);
                } else {
                    collapseInBetweenComments(position);
                }
            }

            @Override
            public void onClickComment(int position) {
                if (isChildrenCommentsCollapsed(position)) {
                    expandChildrenComments(position);
                } else {
                    collapseChildrenComment(position);
                }
            }

            @Override
            public void onLongClick(int position) {
                showCommentOptionDialog(position);
            }
        };
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
        if (viewHolder instanceof CommentViewHolder) {
            // which one is better?
//        if (getItemViewType(position) == VIEW_TYPE_COMMENT) {
            bindCommentViewHolder(viewHolder, position);
        } else if (viewHolder instanceof CommentHeaderViewHolder) {
            bindHeaderViewHolder(viewHolder);
        } else if (viewHolder instanceof FooterViewHolder) {
            bindFooterViewHolder(viewHolder);
        }
    }

    private void bindCommentViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
        CommentViewHolder commentViewHolder = (CommentViewHolder) viewHolder;
        Comment comment = (Comment) mItemList.get(position);
        commentViewHolder.setCommentIndent(comment.getLevel());
        commentViewHolder.tvTime.setText(Utils.formatTime(comment.getTime()));
        if (!comment.getDeleted()) {
            commentViewHolder.tvAuthor.setText(comment.getBy());
        }

        if (mCollapsedChildrenCommentsIndex.containsKey(comment.getCommentId())) {
            commentViewHolder.tvComment.setText(mContext.getString(R.string.comments_collapsed_prompt,
                mCollapsedChildrenCommentsIndex.get(comment.getCommentId()).size() + 1));
            commentViewHolder.tvComment.setMinLines(2);
            commentViewHolder.tvComment.setGravity(Gravity.CENTER);
        } else {
            setTextViewHTML(commentViewHolder.tvComment, comment.getText());
            commentViewHolder.tvComment.setMinLines(Integer.MIN_VALUE);
            commentViewHolder.tvComment.setGravity(Gravity.LEFT);
        }

        if (mCollapsedOlderCommentsIndex.containsKey(comment.getCommentId())) {
            commentViewHolder.tvCollapseOlderComments.setVisibility(View.VISIBLE);
            commentViewHolder.tvCollapseOlderComments.setText(
                mContext.getString(R.string.comments_collapsed_prompt,
                    mCollapsedOlderCommentsIndex.get(comment.getCommentId()).size()));
        } else {
            commentViewHolder.tvCollapseOlderComments.setVisibility(View.GONE);
        }
    }

    private void bindHeaderViewHolder(RecyclerView.ViewHolder viewHolder) {
        CommentHeaderViewHolder commentHeaderViewHolder = (CommentHeaderViewHolder) viewHolder;
        Post post = (Post) mItemList.get(0);
        commentHeaderViewHolder.tvTitle.setText(post.getTitle());
        Typeface postFont = Typeface.createFromAsset(mContext.getAssets(),
            SharedPrefsManager.getPostFont(prefs) + ".ttf");
        commentHeaderViewHolder.tvTitle.setTypeface(postFont);
        commentHeaderViewHolder.tvTitle.setTextSize(SharedPrefsManager.getCommentFontSize(prefs) * 1.5f);
        commentHeaderViewHolder.tvTitle.setPaintFlags(
            commentHeaderViewHolder.tvTitle.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
        commentHeaderViewHolder.tvUrl.setText(post.getUrl());
        commentHeaderViewHolder.tvPoints.setText("+" + String.valueOf(post.getScore()));
        commentHeaderViewHolder.tvComments.setText(String.valueOf(post.getDescendants())
            + (post.getDescendants() > 1 ? " comments" : " comment"));
        commentHeaderViewHolder.tvTime.setText(Utils.formatTime(post.getTime()));
        commentHeaderViewHolder.tvPoster.setText("by: " + post.getBy());
        if (post.getText() != null && !post.getText().isEmpty()) {
            commentHeaderViewHolder.tvContent.setVisibility(View.VISIBLE);
            commentHeaderViewHolder.tvContent.setText(Html.fromHtml(post.getText()));
            commentHeaderViewHolder.tvContent.setTextSize(SharedPrefsManager.getCommentFontSize(prefs));
            Typeface commentFont = Typeface.createFromAsset(mContext.getAssets(),
                SharedPrefsManager.getCommentFont(prefs) + ".ttf");
            commentHeaderViewHolder.tvContent.setTypeface(commentFont);
            commentHeaderViewHolder.tvContent.
                setLineSpacing(0, SharedPrefsManager.getCommentLineHeight(prefs));
        }
    }

    private void bindFooterViewHolder(RecyclerView.ViewHolder viewHolder) {
        FooterViewHolder footerViewHolder = (FooterViewHolder) viewHolder;
        if (mLoadingState == Constants.LOADING_FINISH) {
            footerViewHolder.progressBar.setVisibility(View.GONE);
            footerViewHolder.tvPrompt.setVisibility(View.GONE);
        } else if (mLoadingState == Constants.LOADING_PROMPT_NO_CONTENT) {
            footerViewHolder.progressBar.setVisibility(View.GONE);
            footerViewHolder.tvPrompt.setText(
                mContext.getResources().getString(R.string.prompt_no_comments));
        } else if (mLoadingState == Constants.LOADING_IN_PROGRESS) {
            footerViewHolder.progressBar.setVisibility(View.VISIBLE);
            footerViewHolder.tvPrompt.setVisibility(View.GONE);
        } else if (mLoadingState == Constants.LOADING_ERROR) {
            footerViewHolder.progressBar.setVisibility(View.GONE);
            footerViewHolder.tvPrompt.setText(
                mContext.getResources().getString(R.string.prompt_comments_loading_error));
        }
    }

    private void setTextViewHTML(TextView textView, String string) {
        // trim two trailing blank lines
        CharSequence sequence =
            Html.fromHtml(string.replace("<p>", "<br /><br />").replace("\n", "<br />"), null, mHTMLCodeTagHandler);
        // use Chrome custom tab if it is available
        if (mContext instanceof CommentsActivity
            && ((CommentsActivity) mContext).getChromeCustomTabsHelper() != null) {
            SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence);
            URLSpan[] urls = strBuilder.getSpans(0, sequence.length(), URLSpan.class);
            for (URLSpan span : urls) {
                makeLinkClickable(strBuilder, span);
            }
            textView.setText(strBuilder);
        } else {
            textView.setText(sequence);
        }
    }

    private void makeLinkClickable(SpannableStringBuilder strBuilder, final URLSpan span) {
        int start = strBuilder.getSpanStart(span);
        int end = strBuilder.getSpanEnd(span);
        int flags = strBuilder.getSpanFlags(span);
        ClickableSpan clickable = new ClickableSpan() {
            public void onClick(View view) {
                Uri uri = Uri.parse(span.getURL());
                CustomTabsIntent.Builder intentBuilder = new CustomTabsIntent.Builder();
                Utils.setupIntentBuilder(intentBuilder, mContext,
                    ((CommentsActivity) mContext).getSharedPreferences());
                ChromeCustomTabsHelper.openCustomTab((Activity) mContext, intentBuilder.build(),
                    uri, null);
            }
        };
        strBuilder.setSpan(clickable, start, end, flags);
        strBuilder.removeSpan(span);
    }


    @Override
    public int getItemCount() {
        return mItemList.size();
    }

    @Override
    public int getItemViewType(int position) {
        if (mItemList.get(position) instanceof Post) {
            return VIEW_TYPE_HEADER;
        } else if (mItemList.get(position) instanceof HNItem.Footer) {
            return VIEW_TYPE_FOOTER;
        } else {
            return VIEW_TYPE_COMMENT;
        }
    }

    public void add(HNItem item) {
        mItemList.add(item);
    }

    public void addHeader(Post post) {
        mItemList.add(0, post);
        notifyItemInserted(0);
    }

    public void addComment(Comment comment) {
        mItemList.add(mItemList.size() - 1, comment);
        notifyItemInserted(mItemList.indexOf(comment));
        mCommentList.add(comment);
    }

    public void addFooter(HNItem.Footer footer) {
        mItemList.add(footer);
        notifyItemInserted(mItemList.size() - 1);
    }

    public void addAllComments(List<Comment> commentList) {
        mItemList.addAll(mItemList.size() - 1, commentList);
        notifyDataSetChanged();
        mCommentList.addAll(commentList);
    }

    public void updateFooter(int loadingState) {
        mLoadingState = loadingState;
    }

    public ArrayList<Comment> getCommentList() {
        return mCommentList;
    }

    public void clear() {
        mItemList.clear();
        mCommentList.clear();
    }

    public void collapseChildrenComment(int position) {
        ArrayList<Comment> childrenComments = new ArrayList<>();
        Comment parentComment = (Comment) mItemList.get(position);
        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
        // if a comment header is not visible when collapsing, scroll to it's header
        if (position != linearLayoutManager.findFirstCompletelyVisibleItemPosition()) {
            linearLayoutManager.scrollToPosition(position);
        }

        for (int curPosition = position + 1;
             (mItemList.get(curPosition) instanceof Comment
                 && ((Comment) mItemList.get(curPosition)).getLevel() > parentComment.getLevel());
             curPosition++) {
            childrenComments.add((Comment) mItemList.get(curPosition));
        }

        if (!childrenComments.isEmpty()) {
            mCollapsedChildrenCommentsIndex.put(parentComment.getCommentId(), childrenComments);
            for (Comment comment : childrenComments) {
                mItemList.remove(comment);
            }
            notifyItemChanged(position);
            notifyItemRangeRemoved(position + 1, childrenComments.size());
        }
    }

    public void expandChildrenComments(int position) {
        Comment parentComment = (Comment) mItemList.get(position);
        List<Comment> collapsedComments =
            mCollapsedChildrenCommentsIndex.get(parentComment.getCommentId());
        int insertPosition = mItemList.indexOf(parentComment) + 1;
        for (Comment comment : collapsedComments) {
            mItemList.add(insertPosition, comment);
            insertPosition++;
        }
        notifyItemRangeInserted(mItemList.indexOf(parentComment) + 1,
            collapsedComments.size());
        notifyItemChanged(position);
        mCollapsedChildrenCommentsIndex.remove(parentComment.getCommentId());
    }

    public boolean isChildrenCommentsCollapsed(int position) {
        Comment Comment = (Comment) mItemList.get(position);
        return mCollapsedChildrenCommentsIndex.containsKey(Comment.getCommentId());
    }

    public void collapseInBetweenComments(int position) {
        ArrayList<Comment> olderComments = new ArrayList<>();
        Comment curComment = (Comment) mItemList.get(position);
        for (int curPosition = position - 1;
             curComment.getLevel() > 0
                 && ((Comment) mItemList.get(curPosition)).getLevel() >= curComment.getLevel()
                 && curPosition > 0;
             curPosition--) {
            olderComments.add(0, (Comment) mItemList.get(curPosition));
        }

        if (!olderComments.isEmpty()) {
            mCollapsedOlderCommentsIndex.put(curComment.getCommentId(), olderComments);
            for (Comment comment : olderComments) {
                mItemList.remove(comment);
            }
            notifyItemChanged(position);
            notifyItemRangeRemoved(position - olderComments.size(), olderComments.size());
        }
    }

    public void expandInBetweenComments(int position) {
        Comment clingedComment = (Comment) mItemList.get(position);
        List<Comment> olderComments = mCollapsedOlderCommentsIndex.get(clingedComment.getCommentId());
        int insertPosition = position;
        for (Comment comment : olderComments) {
            mItemList.add(insertPosition, comment);
            insertPosition++;
        }

        notifyItemChanged(position);
        notifyItemRangeInserted(position, olderComments.size());
        mCollapsedOlderCommentsIndex.remove(clingedComment.getCommentId());

    }

    public boolean isInBetweenCommentsCollapsed(int position) {
        Comment Comment = (Comment) mItemList.get(position);
        return mCollapsedOlderCommentsIndex.containsKey(Comment.getCommentId());
    }

    private void showCommentOptionDialog(int position) {
        final Comment comment = (Comment) mItemList.get(position);
        ((CommentsActivity) mContext).showCommentOptionDialog(comment);
        // scrollToPosition() not working
        ((LinearLayoutManager) mRecyclerView.getLayoutManager()).scrollToPositionWithOffset(position, 0);
    }

    public void updateCommentPrefs() {
        mFont = Typeface.createFromAsset(mContext.getAssets(),
            SharedPrefsManager.getCommentFont(prefs) + ".ttf");
        mTextSize = SharedPrefsManager.getCommentFontSize(prefs);
        mLineHeight = SharedPrefsManager.getCommentLineHeight(prefs);
    }

    private void setCommentIndentStyle() {
        mCommentIndentWidth = Utils.convertDpToPixels(CommentViewHolder.UNIT_COMMENT_INDENT_DP, mContext);
        if (SharedPrefsManager.getTheme(prefs).equals(SharedPrefsManager.THEME_LIGHT)) {
            mCommentIndentColorOrange = 0xFFffa726; //orange_400
            mCommentIndentColorBg = 0xFFEEEEEE; //grey_200
        } else if (SharedPrefsManager.getTheme(prefs).equals(SharedPrefsManager.THEME_DARK)) {
            mCommentIndentColorOrange = 0xFFe65100; //orange_900
            mCommentIndentColorBg = 0xFF212121; //grey_900
        } else if (SharedPrefsManager.getTheme(prefs).equals(SharedPrefsManager.THEME_AMOLED_BLACK)) {
            mCommentIndentColorOrange = 0xFFe65100; //orange_900
            mCommentIndentColorBg = 0xFF000000; //black
        } else {
            mCommentIndentColorOrange = 0xFFffa726; //orange_400
            mCommentIndentColorBg = 0xFFF4ECD8; //sepia
        }
    }
}