/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * 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.android.gallery3d.filtershow.imageshow;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.Shader;

import com.android.gallery3d.R;

public class GradControl {
    private float mPoint1X = Float.NaN; // used to flag parameters have not been set
    private float mPoint1Y = 0;
    private float mPoint2X = 200;
    private float mPoint2Y = 300;
    private int mMinTouchDist = 80;// should be a resource & in dips

    private float[] handlex = new float[3];
    private float[] handley = new float[3];
    private int mSliderColor;
    private int mCenterDotSize;
    private float mDownX;
    private float mDownY;
    private float mDownPoint1X;
    private float mDownPoint1Y;
    private float mDownPoint2X;
    private float mDownPoint2Y;
    Rect mImageBounds;
    int mImageHeight;
    private Matrix mScrToImg;
    Paint mPaint = new Paint();
    DashPathEffect mDash = new DashPathEffect(new float[]{30, 30}, 0);
    private boolean mShowReshapeHandles = true;
    public final static int HAN_CENTER = 0;
    public final static int HAN_NORTH = 2;
    public final static int HAN_SOUTH = 1;
    private int[] mPointColorPatern;
    private int[] mGrayPointColorPatern;
    private float[] mPointRadialPos = new float[]{0, .3f, .31f, 1};
    private int mLineColor;
    private int mlineShadowColor;

    public GradControl(Context context) {

        Resources res = context.getResources();
        mCenterDotSize = (int) res.getDimension(R.dimen.gradcontrol_dot_size);
        mMinTouchDist = (int) res.getDimension(R.dimen.gradcontrol_min_touch_dist);
        int grayPointCenterColor = res.getColor(R.color.gradcontrol_graypoint_center);
        int grayPointEdgeColor = res.getColor(R.color.gradcontrol_graypoint_edge);
        int pointCenterColor = res.getColor(R.color.gradcontrol_point_center);
        int pointEdgeColor = res.getColor(R.color.gradcontrol_point_edge);
        int pointShadowStartColor = res.getColor(R.color.gradcontrol_point_shadow_start);
        int pointShadowEndColor = res.getColor(R.color.gradcontrol_point_shadow_end);
        mPointColorPatern = new int[]{
                pointCenterColor, pointEdgeColor, pointShadowStartColor, pointShadowEndColor};
        mGrayPointColorPatern = new int[]{
                grayPointCenterColor, grayPointEdgeColor, pointShadowStartColor, pointShadowEndColor};
        mSliderColor = Color.WHITE;
        mLineColor = res.getColor(R.color.gradcontrol_line_color);
        mlineShadowColor = res.getColor(R.color.gradcontrol_line_shadow);
    }

    public void setPoint2(float x, float y) {
        mPoint2X = x;
        mPoint2Y = y;
    }

    public void setPoint1(float x, float y) {
        mPoint1X = x;
        mPoint1Y = y;
    }

    public int getCloseHandle(float x, float y) {
        float min = Float.MAX_VALUE;
        int handle = -1;
        for (int i = 0; i < handlex.length; i++) {
            float dx = handlex[i] - x;
            float dy = handley[i] - y;
            float dist = dx * dx + dy * dy;
            if (dist < min) {
                min = dist;
                handle = i;
            }
        }

        if (min < mMinTouchDist * mMinTouchDist) {
            return handle;
        }
        for (int i = 0; i < handlex.length; i++) {
            float dx = handlex[i] - x;
            float dy = handley[i] - y;
            float dist = (float) Math.hypot(dx, dy);
        }

        return -1;
    }

    public void setScrImageInfo(Matrix scrToImg, Rect imageBounds) {
        mScrToImg = scrToImg;
        mImageBounds = new Rect(imageBounds);
    }

    private boolean centerIsOutside(float x1, float y1, float x2, float y2) {
        return (!mImageBounds.contains((int) ((x1 + x2) / 2), (int) ((y1 + y2) / 2)));
    }

    public void actionDown(float x, float y, Line line) {
        float[] point = new float[]{
                x, y};
        mScrToImg.mapPoints(point);
        mDownX = point[0];
        mDownY = point[1];
        mDownPoint1X = line.getPoint1X();
        mDownPoint1Y = line.getPoint1Y();
        mDownPoint2X = line.getPoint2X();
        mDownPoint2Y = line.getPoint2Y();
    }

    public void actionMove(int handle, float x, float y, Line line) {
        float[] point = new float[]{
                x, y};
        mScrToImg.mapPoints(point);
        x = point[0];
        y = point[1];

        // Test if the matrix is swapping x and y
        point[0] = 0;
        point[1] = 1;
        mScrToImg.mapVectors(point);
        boolean swapxy = (point[0] > 0.0f);

        int sign = 1;

        float dx = x - mDownX;
        float dy = y - mDownY;
        switch (handle) {
            case HAN_CENTER:
                if (centerIsOutside(mDownPoint1X + dx, mDownPoint1Y + dy,
                        mDownPoint2X + dx, mDownPoint2Y + dy)) {
                    break;
                }
                line.setPoint1(mDownPoint1X + dx, mDownPoint1Y + dy);
                line.setPoint2(mDownPoint2X + dx, mDownPoint2Y + dy);
                break;
            case HAN_SOUTH:
                if (centerIsOutside(mDownPoint1X + dx, mDownPoint1Y + dy,
                        mDownPoint2X, mDownPoint2Y)) {
                    break;
                }
                line.setPoint1(mDownPoint1X + dx, mDownPoint1Y + dy);
                break;
            case HAN_NORTH:
                if (centerIsOutside(mDownPoint1X, mDownPoint1Y,
                        mDownPoint2X + dx, mDownPoint2Y + dy)) {
                    break;
                }
                line.setPoint2(mDownPoint2X + dx, mDownPoint2Y + dy);
                break;
        }
    }

    public void paintGrayPoint(Canvas canvas, float x, float y) {
        if (isUndefined()) {
            return;
        }

        Paint paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        RadialGradient g = new RadialGradient(x, y, mCenterDotSize, mGrayPointColorPatern,
                mPointRadialPos, Shader.TileMode.CLAMP);
        paint.setShader(g);
        canvas.drawCircle(x, y, mCenterDotSize, paint);
    }

    public void paintPoint(Canvas canvas, float x, float y) {
        if (isUndefined()) {
            return;
        }

        Paint paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        RadialGradient g = new RadialGradient(x, y, mCenterDotSize, mPointColorPatern,
                mPointRadialPos, Shader.TileMode.CLAMP);
        paint.setShader(g);
        canvas.drawCircle(x, y, mCenterDotSize, paint);
    }

    void paintLines(Canvas canvas, float p1x, float p1y, float p2x, float p2y) {
        if (isUndefined()) {
            return;
        }

        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);

        mPaint.setStrokeWidth(6);
        mPaint.setColor(mlineShadowColor);
        mPaint.setPathEffect(mDash);
        paintOvallines(canvas, mPaint, p1x, p1y, p2x, p2y);

        mPaint.setStrokeWidth(3);
        mPaint.setColor(mLineColor);
        mPaint.setPathEffect(mDash);
        paintOvallines(canvas, mPaint, p1x, p1y, p2x, p2y);
    }

    public void paintOvallines(
            Canvas canvas, Paint paint, float p1x, float p1y, float p2x, float p2y) {



        canvas.drawLine(p1x, p1y, p2x, p2y, paint);

        float cx = (p1x + p2x) / 2;
        float cy = (p1y + p2y) / 2;
        float dx = p1x - p2x;
        float dy = p1y - p2y;
        float len = (float) Math.hypot(dx, dy);
        dx *= 2048 / len;
        dy *= 2048 / len;

        canvas.drawLine(p1x + dy, p1y - dx, p1x - dy, p1y + dx, paint);
        canvas.drawLine(p2x + dy, p2y - dx, p2x - dy, p2y + dx, paint);
    }

    public void fillHandles(Canvas canvas, float p1x, float p1y, float p2x, float p2y) {
        float cx = (p1x + p2x) / 2;
        float cy = (p1y + p2y) / 2;
        handlex[0] = cx;
        handley[0] = cy;
        handlex[1] = p1x;
        handley[1] = p1y;
        handlex[2] = p2x;
        handley[2] = p2y;

    }

    public void draw(Canvas canvas) {
        paintLines(canvas, mPoint1X, mPoint1Y, mPoint2X, mPoint2Y);
        fillHandles(canvas, mPoint1X, mPoint1Y, mPoint2X, mPoint2Y);
        paintPoint(canvas, mPoint2X, mPoint2Y);
        paintPoint(canvas, mPoint1X, mPoint1Y);
        paintPoint(canvas, (mPoint1X + mPoint2X) / 2, (mPoint1Y + mPoint2Y) / 2);
    }

    public boolean isUndefined() {
        return Float.isNaN(mPoint1X);
    }

    public void setShowReshapeHandles(boolean showReshapeHandles) {
        this.mShowReshapeHandles = showReshapeHandles;
    }
}
