content: /*
   * MIT License
   *
   * Copyright (c) 2018 Alibaba Group
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in all
   * copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   */
  
  package com.tmall.wireless.tangram3.view;
  
  import android.content.Context;
  import android.content.res.Configuration;
  import android.content.res.Resources;
  import android.graphics.Rect;
  import android.graphics.drawable.Drawable;
  import android.graphics.drawable.GradientDrawable;
  import android.graphics.drawable.ShapeDrawable;
  import android.graphics.drawable.shapes.Shape;
  import android.support.annotation.NonNull;
  import android.support.v7.widget.GridLayoutManager;
  import android.support.v7.widget.LinearLayoutManager;
  import android.support.v7.widget.RecyclerView;
  import android.util.AttributeSet;
  import android.util.DisplayMetrics;
  import android.view.Gravity;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.FrameLayout;
  import android.widget.ImageView;
  import android.widget.LinearLayout;
  import android.widget.RelativeLayout;
  
  import com.tmall.wireless.tangram3.core.R;
  import com.tmall.wireless.tangram3.TangramEngine;
  import com.tmall.wireless.tangram3.core.adapter.BinderViewHolder;
  import com.tmall.wireless.tangram3.core.adapter.GroupBasicAdapter;
  import com.tmall.wireless.tangram3.dataparser.concrete.Style;
  import com.tmall.wireless.tangram3.structure.BaseCell;
  import com.tmall.wireless.tangram3.structure.cell.LinearScrollCell;
  import com.tmall.wireless.tangram3.structure.view.ITangramViewLifeCycle;
  import com.tmall.wireless.tangram3.util.ImageUtils;
  
  import java.util.ArrayList;
  import java.util.List;
  
  
  /**
   * Created by Kunlun on 9/17/16.
   */
  public class LinearScrollView extends RelativeLayout implements ITangramViewLifeCycle {
      private RecyclerView recyclerView;
      private GridLayoutManager layoutManager;
      private View indicator, indicatorContainer;
  
      private LinearScrollCell lSCell;
  
      // total distance that indicator can move.
      private float totalDistanceOfIndicator = 0;
      // total distance that recycler view can scroll.
      private float totalDistance = 0;
  
      private List<BinderViewHolder> mViewHolders = new ArrayList<BinderViewHolder>();
  
      private RecyclerView.ItemDecoration itemDecoration;
  
      private View backgroundView;
  
      private RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
          @Override
          public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
              super.onScrolled(recyclerView, dx, dy);
              if (lSCell == null) {
                  return;
              }
  
              lSCell.currentDistance += dx;
  
              if (lSCell.hasIndicator && totalDistance > 0) {
                  float distance = Math.max(0, Math.min((int) (lSCell.currentDistance * totalDistanceOfIndicator
                          / totalDistance + 0.5), totalDistanceOfIndicator));
                  indicator.setTranslationX(distance);
              }
          }
      };
  
      public LinearScrollView(Context context) {
          this(context, null);
      }
  
      public LinearScrollView(Context context, AttributeSet attrs) {
          this(context, attrs, 0);
      }
  
      public LinearScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
          super(context, attrs, defStyleAttr);
          init();
      }
  
      private void init() {
          inflate(getContext(), R.layout.tangram_linearscrollview, this);
          setClickable(true);
          recyclerView = (RecyclerView) findViewById(R.id.tangram_linearscrollview_container);
  
          layoutManager = new GridLayoutManager(getContext(), 1, LinearLayoutManager.HORIZONTAL, false);
          recyclerView.setLayoutManager(layoutManager);
  
          indicator = findViewById(R.id.tangram_linearscrollview_indicator);
          indicatorContainer = findViewById(R.id.tangram_linearscrollview_indicator_container);
  
          totalDistanceOfIndicator = Style.dp2px(34);
      }
  
      @Override
      public void cellInited(BaseCell cell) {
          if (cell instanceof LinearScrollCell) {
              this.lSCell = (LinearScrollCell) cell;
              if (lSCell.maxRows > 1) {
                  layoutManager.setSpanCount(lSCell.maxRows);
              } else {
                  layoutManager.setSpanCount(1);
              }
              totalDistanceOfIndicator = (float) (this.lSCell.defaultIndicatorWidth - this.lSCell.indicatorWidth);
              if (backgroundView == null) {
                  backgroundView = ((TangramEngine) cell.serviceManager).getLayoutManager().generateLayoutView();
                  addView(backgroundView, 0, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
              }
          }
      }
  
      @Override
      public void postBindView(BaseCell cell) {
          if (lSCell == null) {
              return;
          }
          recyclerView.setRecycledViewPool(lSCell.getRecycledViewPool());
  
          recyclerView.removeItemDecoration(itemDecoration);
          if (lSCell.hGap > 0) {
              itemDecoration = new RecyclerView.ItemDecoration() {
                  @Override
                  public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
                      outRect.set(0, 0, 0, 0);
  
                      int cellCount = lSCell.cells.size();
                      int viewIndex = (int) view.getTag(R.id.TANGRAM_LINEAR_SCROLL_POS);
                      int rowCount = (int) (lSCell.cells.size() * 1.0f / lSCell.maxRows + 0.5f);
  
                      boolean isLastCellInRow = false;
                      if ((viewIndex + 1) % rowCount == 0) {
                          // the last cell in every row
                          isLastCellInRow = true;
                      }
  
                      if (viewIndex == (cellCount - 1)) {
                          // the last cell in cells
                          isLastCellInRow = true;
                      }
  
                      if (viewIndex % rowCount == 0) {
                          // first view only set right
                          outRect.right = (int) (lSCell.hGap / 2);
                      } else if (isLastCellInRow) {
                          // last view only set left
                          outRect.left = (int) (lSCell.hGap / 2);
  
                          if (lSCell.maxRows > 1 && (cellCount % lSCell.maxRows == 1) && (viewIndex == cellCount - 1)) {
                              // the last cell in penultimate row
                              outRect.right = (int) (lSCell.hGap / 2);
                          }
                      } else {
                          outRect.left = (int) (lSCell.hGap / 2);
                          outRect.right = (int) (lSCell.hGap / 2);
                      }
                  }
              };
              recyclerView.addItemDecoration(itemDecoration);
          }
  
          float[] starts = null;
          if (lSCell.cells != null && lSCell.cells.size() > 0) {
              int maxRowCount = lSCell.cells.size();
              if (lSCell.maxRows > 1) {
                  maxRowCount = (int) (maxRowCount * 1.0f / lSCell.maxRows + 0.5f);
              }
  
              starts = new float[maxRowCount];
              for (int i = 0; i < maxRowCount; i++) {
                  int rowIndex = lSCell.maxRows * i;
                  int mapperIndex = lSCell.getMapperPosition(rowIndex);
  
                  starts[i] = totalDistance;
  
                  BaseCell bc = lSCell.cells.get(mapperIndex);
                  if (bc.style != null && bc.style.margin.length > 0) {
                      totalDistance = totalDistance + bc.style.margin[1] + bc.style.margin[3];
                  }
                  totalDistance += lSCell.pageWidth;
                  if (i > 0 && lSCell.hGap > 0) {
                      totalDistance += lSCell.hGap;
                  }
              }
          }
          totalDistance -= getScreenWidth();
  
          // calculate height of recycler view.
          ViewGroup.LayoutParams lp = recyclerView.getLayoutParams();
          if (!Double.isNaN(lSCell.pageHeight)) {
              if (lSCell.maxRows == 0) {
                  lp.height = (int) (lSCell.pageHeight + 0.5f);
              } else {
                  lp.height = (int) (lSCell.pageHeight * lSCell.maxRows + 0.5f);
              }
              if (lSCell.maxRows > 1 && lSCell.vGap > 0) {
                  lp.height += (int) ((lSCell.maxRows - 1) * lSCell.vGap + 0.5f);
              }
          }
          recyclerView.setLayoutParams(lp);
  
          recyclerView.setAdapter(lSCell.adapter);
  
          if (lSCell.hasIndicator && totalDistance > 0) {
              setViewColor(indicator, lSCell.indicatorColor);
              setViewColor(indicatorContainer, lSCell.defaultIndicatorColor);
              setIndicatorMeasure(indicator, (int) Math.round(lSCell.indicatorWidth),
                      (int) Math.round(lSCell.indicatorHeight), 0, lSCell.indicatorRadius);
              setIndicatorMeasure(indicatorContainer, (int) Math.round(lSCell.defaultIndicatorWidth),
                      (int) Math.round(lSCell.indicatorHeight), (int) Math.round(lSCell.indicatorMargin), lSCell.indicatorRadius);
              indicatorContainer.setVisibility(VISIBLE);
          } else {
              indicatorContainer.setVisibility(GONE);
          }
  
          recyclerView.addOnScrollListener(onScrollListener);
  
          setBackgroundColor(lSCell.bgColor);
  
          if (backgroundView != null) {
              if (backgroundView instanceof ImageView) {
                  ImageUtils.doLoadImageUrl((ImageView) backgroundView, lSCell.nativeBackgroundImage);
              }
          }
  
          if (lSCell.retainScrollState && starts != null) {
              GridLayoutManager lm = (GridLayoutManager) recyclerView.getLayoutManager();
              int position = computeFirstCompletelyVisibleItemPositionForScrolledX(starts);
              lm.scrollToPositionWithOffset(position * lSCell.maxRows, (int) (starts[position] - lSCell.currentDistance));
          }
          if (lSCell.scrollMarginLeft > 0 || lSCell.scrollMarginRight > 0) {
              setPadding(lSCell.scrollMarginLeft, 0, lSCell.scrollMarginRight, 0);
              setClipToPadding(false);
              setClipChildren(false);
          } else {
              setPadding(0, 0, 0, 0);
              setClipToPadding(true);
              setClipChildren(true);
          }
          recycleView(lSCell);
          bindHeaderView(lSCell.mHeader);
          bindFooterView(lSCell.mFooter);
      }
  
      /**
       * Set indicator measure
       *
       * @param indicator indicator view
       * @param width     indicator width
       * @param height    indicator height
       * @param margin    indicator top margin
       */
      private void setIndicatorMeasure(View indicator, int width, int height, int margin, double radius) {
          if (indicator != null) {
              ViewGroup.LayoutParams layoutParams = indicator.getLayoutParams();
              layoutParams.width = width;
              layoutParams.height = height;
  
              if (margin > 0) {
                  if (layoutParams instanceof FrameLayout.LayoutParams) {
                      FrameLayout.LayoutParams frameLayoutParams = (FrameLayout.LayoutParams) layoutParams;
                      frameLayoutParams.topMargin = margin;
                  } else if (layoutParams instanceof LayoutParams) {
                      LayoutParams linearLayoutParams = (LayoutParams) layoutParams;
                      linearLayoutParams.topMargin = margin;
                  }
              }
              indicator.setLayoutParams(layoutParams);
  
              Drawable drawable = indicator.getBackground();
              if (drawable instanceof GradientDrawable) {
                  GradientDrawable gradientDrawable = (GradientDrawable)drawable;
                  gradientDrawable.setCornerRadius((float)radius);
              }
          }
      }
  
      /**
       * Find the first completely visible position.
       *
       * @param starts A recorder array to save each item's left position, including its margin.
       * @return Position of first completely visible item.
       */
      private int computeFirstCompletelyVisibleItemPositionForScrolledX(float[] starts) {
          if (lSCell == null || starts == null || starts.length <= 0) {
              return 0;
          }
          for (int i = 0; i < starts.length; i++) {
              if (starts[i] >= lSCell.currentDistance) {
                  return i;
              }
          }
          return starts.length - 1;
      }
  
      @Override
      public void postUnBindView(BaseCell cell) {
          if (lSCell == null) {
              return;
          }
  
          totalDistance = 0;
  
          if (lSCell.hasIndicator) {
              indicator.setTranslationX(0);
          }
  
          recyclerView.removeOnScrollListener(onScrollListener);
          recyclerView.setAdapter(null);
  
          lSCell = null;
          recycleView(cell);
      }
  
      private void setViewColor(View view, int color) {
          if (view.getBackground() instanceof GradientDrawable) {
              GradientDrawable drawable = (GradientDrawable) view.getBackground().mutate();
              drawable.setColor(color);
          }
      }
  
      private int getScreenWidth() {
          Resources resources = getContext().getResources();
          DisplayMetrics dm = resources.getDisplayMetrics();
          Configuration configuration = resources.getConfiguration();
          return configuration.orientation == Configuration.ORIENTATION_PORTRAIT ?
                  dm.widthPixels : dm.heightPixels;
      }
  
      private void bindHeaderView(BaseCell cell) {
          if (cell != null && cell.isValid()) {
              View header = getViewFromRecycler(cell);
              if (header != null) {
                  header.setId(R.id.TANGRAM_BANNER_HEADER_ID);
                  //为了解决在 item 复用过程中，itemView 的 layoutParams 复用造成 layout 错误,这里要提供一个新的 layoutParams。
                  LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                          ViewGroup.LayoutParams.WRAP_CONTENT);
                  lp.topMargin = cell.style.margin[Style.MARGIN_TOP_INDEX];
                  lp.leftMargin = cell.style.margin[Style.MARGIN_LEFT_INDEX];
                  lp.bottomMargin = cell.style.margin[Style.MARGIN_BOTTOM_INDEX];
                  lp.rightMargin = cell.style.margin[Style.MARGIN_RIGHT_INDEX];
                  addView(header, 0, lp);
              }
          }
      }
  
      private void bindFooterView(BaseCell cell) {
          if (cell != null && cell.isValid()) {
              View footer = getViewFromRecycler(cell);
              if (footer != null) {
                  footer.setId(R.id.TANGRAM_BANNER_FOOTER_ID);
                  //为了解决在 item 复用过程中，itemView 的 layoutParams 复用造成 layout 错误,这里要提供一个新的 layoutParams。
                  LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                          ViewGroup.LayoutParams.WRAP_CONTENT);
                  lp.topMargin = cell.style.margin[Style.MARGIN_TOP_INDEX];
                  lp.leftMargin = cell.style.margin[Style.MARGIN_LEFT_INDEX];
                  lp.bottomMargin = cell.style.margin[Style.MARGIN_BOTTOM_INDEX];
                  lp.rightMargin = cell.style.margin[Style.MARGIN_RIGHT_INDEX];
                  addView(footer, lp);
              }
          }
      }
  
      private View getViewFromRecycler(@NonNull BaseCell cell) {
          GroupBasicAdapter adapter = cell.serviceManager.getService(GroupBasicAdapter.class);
          RecyclerView.RecycledViewPool pool = cell.serviceManager.getService(RecyclerView.RecycledViewPool.class);
          int itemViewType = adapter.getItemType(cell);
          BinderViewHolder holder = (BinderViewHolder) pool.getRecycledView(itemViewType);
          if (holder == null) {
              holder = (BinderViewHolder) adapter.createViewHolder(this, itemViewType);
          }
          holder.bind(cell);
          mViewHolders.add(holder);
          return holder.itemView;
      }
  
      private void recycleView(@NonNull BaseCell cell) {
          if (!mViewHolders.isEmpty()) {
              RecyclerView.RecycledViewPool pool = cell.serviceManager.getService(RecyclerView.RecycledViewPool.class);
              for (int i = 0, size = mViewHolders.size(); i < size; i++) {
                  BinderViewHolder viewHolder = mViewHolders.get(i);
                  viewHolder.unbind();
                  removeView(viewHolder.itemView);
                  pool.putRecycledView(viewHolder);
              }
              mViewHolders.clear();
          }
      }
  }
  