package com.prolificinteractive.chandelier.widget;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.prolificinteractive.chandelier.R;
import java.util.ArrayList;
import java.util.List;

import static android.view.Gravity.CENTER;
import static android.view.Gravity.CENTER_VERTICAL;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;

public class OrnamentLayout extends FrameLayout {

  private static final int ITEM_WEIGHT = 1;
  private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR =
      new AccelerateInterpolator();
  private static final AccelerateDecelerateInterpolator ACCELERATE_DECELERATE_INTERPOLATOR =
      new AccelerateDecelerateInterpolator();
  private static final int DEFAULT_SCALE = 1;

  private final int imageViewMargin;
  private final int selectedSize;

  private final LinearLayout container;
  private final ImageView selectedImageView;
  private final List<Ornament> ornaments = new ArrayList<>();
  private final List<ImageView> imageViews = new ArrayList<>();
  private final int actionItemLayoutHeight;
  private final int actionItemLayoutWidth;
  private final int shortAnimDuration =
      getResources().getInteger(android.R.integer.config_shortAnimTime);

  private boolean isScaleEnabled;
  private int measuredWidth;
  private int selectedIndex = -1;
  private boolean isAnimating = false;
  private Animation.AnimationListener actionListener;

  private final Animation.AnimationListener animationListener = new SimpleAnimationListener() {
    @Override public void onAnimationStart(Animation animation) {
      imageViews.get(selectedIndex).setSelected(true);
    }

    @Override public void onAnimationEnd(Animation animation) {
      isAnimating = false;
      if (actionListener != null) {
        actionListener.onAnimationEnd(null);
      }
      actionListener = null;
    }
  };

  public OrnamentLayout(final Context context, final AttributeSet attrs) {
    super(context);
    final Resources res = getResources();
    final TypedArray a = context.getTheme().obtainStyledAttributes(
        attrs,
        R.styleable.ChandelierLayout,
        0, 0);

    // Defaults
    final int defaultElevation = res.getDimensionPixelSize(R.dimen.default_elevation);
    final int defaultSelectorMargin = res.getDimensionPixelSize(R.dimen.default_selector_margin);
    final int defaultSelectorSize = res.getDimensionPixelSize(R.dimen.default_selector_size);
    final boolean defaultScaleEnabled = res.getBoolean(R.bool.default_scale_enabled);

    isScaleEnabled = a.getBoolean(R.styleable.ChandelierLayout_chandelier_scale_enabled,
        defaultScaleEnabled);
    actionItemLayoutHeight =
        a.getDimensionPixelSize(R.styleable.ChandelierLayout_ornament_layout_height,
            WRAP_CONTENT);
    actionItemLayoutWidth = a.getDimensionPixelSize(R.styleable.ChandelierLayout_ornament_layout_width,
        WRAP_CONTENT);

    // Action Layout
    container = new LinearLayout(context);
    container.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT, CENTER_VERTICAL));
    setBackground(a.getDrawable(R.styleable.ChandelierLayout_chandelier_background));

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      setElevation(
          a.getDimensionPixelSize(R.styleable.ChandelierLayout_chandelier_elevation, defaultElevation));
    }

    container.setOrientation(LinearLayout.HORIZONTAL);
    container.setGravity(CENTER_VERTICAL);

    // Action Item
    imageViewMargin =
        a.getDimensionPixelSize(R.styleable.ChandelierLayout_ornament_margin, defaultSelectorMargin);
    selectedSize = a.getDimensionPixelSize(R.styleable.ChandelierLayout_chandelier_selected_size,
        defaultSelectorSize);
    selectedImageView = new ImageView(context);
    final LayoutParams selectedLp = new LayoutParams(selectedSize, selectedSize, CENTER_VERTICAL);
    selectedLp.setMargins(0, imageViewMargin, 0, imageViewMargin);
    selectedImageView.setLayoutParams(selectedLp);

    Drawable selectorBackground = a.getDrawable(R.styleable.ChandelierLayout_chandelier_selector);
    if (selectorBackground != null) {
      selectedImageView.setBackground(selectorBackground);
    } else {
      selectedImageView.setBackground(res.getDrawable(R.drawable.default_selector));
    }

    Drawable layoutBackground = a.getDrawable(R.styleable.ChandelierLayout_chandelier_background);
    if (layoutBackground != null) {
      setBackground(layoutBackground);
    } else {
      TypedValue typedValue = new TypedValue();
      context.getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true);
      setBackgroundColor(typedValue.data);
    }

    a.recycle();

    addView(selectedImageView);
    addView(container);
  }

  public void populateActionItems(int... drawablesResIds) {
    final ArrayList<Ornament> items = new ArrayList<>();
    for (int resId : drawablesResIds) {
      items.add(new Ornament(resId));
    }
    populateActionItems(items);
  }

  public void populateActionItems(@Nullable final List<? extends Ornament> items) {
    container.removeAllViews();
    ornaments.clear();
    imageViews.clear();

    if (items != null) {
      ornaments.addAll(items);
      final Context context = getContext();
      for (final Ornament item : items) {
        final FrameLayout frame = new FrameLayout(context);
        frame.setLayoutParams(new LinearLayout.LayoutParams(
            MATCH_PARENT,
            WRAP_CONTENT,
            ITEM_WEIGHT)
        );

        final ImageView imageView = new ImageView(context);
        final FrameLayout.LayoutParams imageLp = new FrameLayout.LayoutParams(
            actionItemLayoutWidth,
            actionItemLayoutHeight,
            CENTER
        );
        imageLp.setMargins(imageViewMargin, imageViewMargin, imageViewMargin, imageViewMargin);
        imageView.setLayoutParams(imageLp);
        imageView.setBackgroundResource(item.drawableResId);

        imageViews.add(imageView);

        frame.addView(imageView);
        container.addView(frame);
      }
      selectedIndex = ornaments.size() / 2;
      imageViews.get(selectedIndex).setSelected(true);
    }
  }

  @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    this.measuredWidth = getMeasuredWidth();
  }

  public void onParentTouchEvent(final MotionEvent ev) {
    if (measuredWidth <= 0 || isAnimating) {
      return;
    }

    final float x = ev.getX();
    final int count = ornaments.size();

    // One quarter of the screen width
    final int q = measuredWidth / 4;
    // projected x in center of screen
    final int pX = getInRange(Math.round((x - q) * 2), 0, measuredWidth);
    // width of one item
    final int iW = Math.round(measuredWidth / count);
    final int newSelectedIndex = getInRange(Math.round(pX / iW), 0, count - 1);

    // sensitivity range
    final int xS = iW / count;

    switch (ev.getAction()) {
      case MotionEvent.ACTION_MOVE:
        if (pX < selectedIndex * iW + xS) {
          // left edge

          if (selectedIndex == 0) {
            break;
          }

          // actual progress
          final float t = getInRange((selectedIndex * iW + xS - pX) / (2f * xS), 0f, 1f);
          // scale
          final float sX = isScaleEnabled ? t / 2 + 1 : 1;
          // position in the middle
          final int mX = iW * selectedIndex + (iW - selectedSize) / 2;
          // interpolated progress
          final float it = getInRange(ACCELERATE_INTERPOLATOR.getInterpolation(t), 0f, 1f);
          // amount to move
          final int dX = Math.round(it * 2 * xS);
          // target
          final int tX = mX - dX;

          selectedImageView.setPivotX(0);
          selectedImageView.setScaleX(sX);
          selectedImageView.setTranslationX(tX);

          if (pX < iW * selectedIndex - xS) {
            setSelectedIndex(newSelectedIndex);
          }
        } else if (pX > (selectedIndex + 1) * iW - xS) {
          // right edge

          if (selectedIndex == count - 1) {
            break;
          }

          // actual progress
          final float t = getInRange((pX - (selectedIndex + 1) * iW + xS) / (2f * xS), 0f, 1f);
          // scale
          final float sX = isScaleEnabled ? t / 2 + 1 : 1;
          // position in the middle
          final int mX = iW * selectedIndex + (iW - selectedSize) / 2;
          // interpolated progress
          final float it = getInRange(ACCELERATE_INTERPOLATOR.getInterpolation(t), 0f, 1f);
          // amount to move
          final int dX = Math.round(it * 2 * xS);
          // target
          final int tX = mX + dX;

          selectedImageView.setPivotX(selectedSize);
          selectedImageView.setScaleX(sX);
          selectedImageView.setTranslationX(tX);

          if (pX > iW * (selectedIndex + 1) + xS) {
            setSelectedIndex(newSelectedIndex);
          }
        } else {
          // middle
          selectedImageView.setTranslationX(selectedIndex * iW + (iW - selectedSize) / 2);
          selectedImageView.setScaleX(DEFAULT_SCALE);
        }
        break;
    }
  }

  private void setSelectedIndex(final int newSelectedIndex) {
    // Un-select previous index
    imageViews.get(selectedIndex).setSelected(false);

    selectedIndex = newSelectedIndex;
    isAnimating = true;

    final int iW = measuredWidth / ornaments.size();
    final int target = iW * selectedIndex + (iW - selectedSize) / 2;
    final float currentScale = selectedImageView.getScaleX();
    final float currentTranslation = selectedImageView.getTranslationX();

    final Animation animation = new Animation() {
      @Override protected void applyTransformation(float t, Transformation transformation) {
        ViewCompat.setScaleX(selectedImageView, (DEFAULT_SCALE - currentScale) * t + currentScale);
        ViewCompat.setTranslationX(selectedImageView,
            (target - currentTranslation) * t + currentTranslation);
      }
    };

    animation.setAnimationListener(animationListener);
    animation.setDuration(shortAnimDuration);
    animation.setInterpolator(ACCELERATE_DECELERATE_INTERPOLATOR);
    selectedImageView.clearAnimation();
    selectedImageView.startAnimation(animation);
  }

  private int getInRange(final int value, final int min, final int max) {
    return Math.max(min, Math.min(max, value));
  }

  private float getInRange(final float value, final float min, final float max) {
    return Math.max(min, Math.min(max, value));
  }

  public int getSelectedIndex() {
    return selectedIndex;
  }

  public void finishAction(Animation.AnimationListener mActionListener) {
    actionListener = mActionListener;
    setSelectedIndex(selectedIndex);
  }

  public void onLayoutTranslated(final float progress) {

  }

  public Ornament getActionItem(int index) {
    return ornaments.get(index);
  }
}