/*
 * Copyright (C) 2015 Marie Schweiz & Lars Werkman
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.larswerkman.lobsterpicker.sliders;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Path;
import android.graphics.Region;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.MotionEvent;

import com.larswerkman.lobsterpicker.LobsterPicker;
import com.larswerkman.lobsterpicker.LobsterSlider;

/**
 * Slider that is able to manipulate the Opacity value of a color.
 */
public class LobsterOpacitySlider extends LobsterSlider {

    private LobsterPicker.Chain chain = LobsterPicker.EMPTY_CHAIN;
    private Path pointerPath;

    private int chainedColor;
    private boolean pointerPressed;
    private float[] hsv = new float[3];

    private int opacity = 255;

    public LobsterOpacitySlider(Context context) {
        super(context);
        init(context, null, 0);
    }

    public LobsterOpacitySlider(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public LobsterOpacitySlider(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttributeSet attrs, int defStyle) {
        pointerPath = new Path();

        chainedColor = 0xFF000000;
        updateShader();

        invalidate();
    }

    @Override
    public void onColorChanged(LobsterPicker.Chain chain, int color) {
        this.chain = chain;

        chainedColor = color;
        pointerPaint.setColor(color);
        opacity = (int) (((float) pointerPosition.x / length) * 255);

        updateShader();
        updateColor();
        chain.setColor(this, chainedColor);

        if(Color.alpha(color) != 0xFF){
            setOpacity(Color.alpha(color));
        }
        invalidate();
    }

    /** {@inheritDoc} */
    @Override
    public int getColor() {
        return chainedColor;
    }

    /**
     * Returns the selected opacity value
     *
     * @return opacity >= 0 and <= 255
     */
    public int getOpacity() {
        return opacity;
    }

    /**
     * Set the opacity value
     *
     * @param opacity value >= 0 and <= 255
     */
    public void setOpacity(int opacity) {
        this.opacity = opacity;
        updateColor();
        chain.setColor(this, chainedColor);
        getMoveAnimation().start();
    }

    private void updateShader() {
        paint.setShader(new LinearGradient(0, 0,
                length, 0, new int[]{0x00FFFFFF & chainedColor, chainedColor}, null,
                Shader.TileMode.CLAMP));
    }

    private void updateColor() {
        Color.colorToHSV(chainedColor, hsv);
        chainedColor = Color.HSVToColor(opacity, hsv);
        pointerPaint.setColor(chainedColor);
        pointerShadowPaint.setColor(Color.HSVToColor(0x59, hsv));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.translate(pointerShadowRadius, getHeight() / 2);

        canvas.save();

        pointerPath.reset();
        pointerPath.addCircle(pointerPosition.x, pointerPosition.y, pointerRadius, Path.Direction.CW);
        pointerPath.close();

        canvas.clipPath(pointerPath, Region.Op.DIFFERENCE);
        canvas.drawLine(0, 0, length, 0, paint);
        canvas.drawCircle(pointerPosition.x, pointerPosition.y, pointerShadowRadius, pointerShadowPaint);

        canvas.restore();

        canvas.drawCircle(pointerPosition.x, pointerPosition.y, pointerRadius, pointerPaint);
    }

    private ValueAnimator getMoveAnimation() {
        ValueAnimator animator = ValueAnimator.ofInt(pointerPosition.x,
                (int) (((float) opacity / 255.f) * (float) length));
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                pointerPosition.x = (Integer) animation.getAnimatedValue();
                invalidate();
            }
        });
        return animator;
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (x >= pointerShadowRadius && x <= pointerShadowRadius + length) {
                    pointerPosition.x = (int) x - pointerShadowRadius;
                } else if (x < pointerShadowRadius) {
                    pointerPosition.x = 0;
                } else if (x > pointerShadowRadius + length) {
                    pointerPosition.x = length;
                }
                pointerPressed = true;

                opacity = (int) (((float) pointerPosition.x / length) * 255);
                updateColor();
                chain.setColor(this, chainedColor);

                getGrowAnimation().start();
                break;
            case MotionEvent.ACTION_MOVE:
                if (pointerPressed) {
                    if (x >= pointerShadowRadius && x <= pointerShadowRadius + length) {
                        pointerPosition.x = (int) x - pointerShadowRadius;
                    } else if (x < pointerShadowRadius) {
                        pointerPosition.x = 0;
                    } else if (x > pointerShadowRadius + length) {
                        pointerPosition.x = length;
                    }

                    opacity = (int) (((float) pointerPosition.x / length) * 255);
                    updateColor();
                    chain.setColor(this, chainedColor);

                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                if (pointerPressed) {
                    pointerPosition.x = (int) x - pointerShadowRadius;
                    if(pointerPosition.x > length){
                        pointerPosition.x = length;
                    } else if(pointerPosition.x < 0){
                        pointerPosition.x = 0;
                    }

                    opacity = (int) (((float) pointerPosition.x / length) * 255);
                    updateColor();
                    chain.setColor(this, chainedColor);

                    getShrinkAnimation().start();
                }
                pointerPressed = false;
                break;
        }
        return true;
    }
}