/*
 * Copyright 2015 Hippo Seven
 *
 * 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.hippo.nimingban.ui;

import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.StateListDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import com.hippo.app.ProgressDialogBuilder;
import com.hippo.io.UriInputStreamPipe;
import com.hippo.nimingban.NMBAppConfig;
import com.hippo.nimingban.R;
import com.hippo.nimingban.util.BitmapUtils;
import com.hippo.nimingban.util.ReadableTime;
import com.hippo.nimingban.util.Settings;
import com.hippo.nimingban.widget.ColorPickerView;
import com.hippo.nimingban.widget.DoodleView;
import com.hippo.nimingban.widget.ThicknessPreviewView;
import com.hippo.ripple.Ripple;
import com.hippo.util.AnimationUtils2;
import com.hippo.util.DrawableManager;
import com.hippo.widget.Slider;
import com.hippo.yorozuya.LayoutUtils;
import com.hippo.yorozuya.ResourcesUtils;
import com.hippo.yorozuya.SimpleHandler;

import java.io.File;

public final class DoodleActivity extends TranslucentActivity implements View.OnClickListener, DoodleView.Helper {

    public static final int REQUEST_CODE_SELECT_IMAGE = 0;

    private DoodleView mDoodleView;

    private View mSide;
    private View mPalette;
    private View mThickness;
    private ImageView mDrawAction;
    private ImageView mImage;
    private ImageView mUndo;
    private ImageView mRedo;
    private View mClear;
    private View mOk;
    private View mMenu;

    private File mOutputFile;

    private Dialog mExitWaitingDialog;

    private ValueAnimator mSideAnimator;
    private boolean mShowSide = true;
    private Runnable mHideSideRunnable;

    @Override
    protected int getLightThemeResId() {
        return Settings.getColorStatusBar() ? R.style.NormalActivity : R.style.NormalActivity_NoStatus;
    }

    @Override
    protected int getDarkThemeResId() {
        return Settings.getColorStatusBar() ? R.style.NormalActivity_Dark : R.style.NormalActivity_Dark_NoStatus;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        File dir = NMBAppConfig.getDoodleDir();
        if (dir != null) {
            String filename = "doodle-" + ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".png";
            mOutputFile = new File(dir, filename);
        }

        setStatusBarColor(ResourcesUtils.getAttrColor(this, R.attr.colorPrimaryDark));
        setContentView(R.layout.activity_doodle);

        mDoodleView = (DoodleView) findViewById(R.id.doodle_view);
        mSide = findViewById(R.id.side);
        mPalette = findViewById(R.id.palette);
        mThickness = findViewById(R.id.thickness);
        mDrawAction = (ImageView) findViewById(R.id.draw_action);
        mImage = (ImageView) findViewById(R.id.image);
        mUndo = (ImageView) findViewById(R.id.undo);
        mRedo = (ImageView) findViewById(R.id.redo);
        mClear = findViewById(R.id.clear);
        mOk = findViewById(R.id.ok);
        mMenu = findViewById(R.id.menu);

        mDoodleView.setHelper(this);

        StateListDrawable undoDrawable = new StateListDrawable();
        undoDrawable.addState(new int[]{-android.R.attr.state_enabled},
                DrawableManager.getDrawable(this, R.drawable.v_undo_disabled_dark_x24));
        undoDrawable.addState(new int[]{},
                DrawableManager.getDrawable(this, R.drawable.v_undo_default_dark_x24));
        mUndo.setImageDrawable(undoDrawable);

        StateListDrawable redoDrawable = new StateListDrawable();
        redoDrawable.addState(new int[]{-android.R.attr.state_enabled},
                DrawableManager.getDrawable(this, R.drawable.v_redo_disabled_dark_x24));
        redoDrawable.addState(new int[]{},
                DrawableManager.getDrawable(this, R.drawable.v_redo_default_dark_x24));
        mRedo.setImageDrawable(redoDrawable);

        StateListDrawable actionDrawable = new StateListDrawable();
        actionDrawable.addState(new int[]{android.R.attr.state_activated},
                DrawableManager.getDrawable(this, R.drawable.v_eraser_dark_x24));
        actionDrawable.addState(new int[]{},
                DrawableManager.getDrawable(this, R.drawable.v_brush_dark_x24));
        mDrawAction.setImageDrawable(actionDrawable);

        StateListDrawable imageDrawable = new StateListDrawable();
        imageDrawable.addState(new int[]{android.R.attr.state_activated},
                DrawableManager.getDrawable(this, R.drawable.v_image_off_dark_x24));
        imageDrawable.addState(new int[]{},
                DrawableManager.getDrawable(this, R.drawable.v_image_dark_x24));
        mImage.setImageDrawable(imageDrawable);

        Ripple.addRipple(mPalette, true);
        Ripple.addRipple(mThickness, true);
        Ripple.addRipple(mDrawAction, true);
        Ripple.addRipple(mImage, true);
        Ripple.addRipple(mUndo, true);
        Ripple.addRipple(mRedo, true);
        Ripple.addRipple(mClear, true);
        Ripple.addRipple(mOk, true);
        Ripple.addRipple(mMenu, ResourcesUtils.getAttrBoolean(this, R.attr.dark));

        mSide.setOnClickListener(this);
        mPalette.setOnClickListener(this);
        mThickness.setOnClickListener(this);
        mDrawAction.setOnClickListener(this);
        mImage.setOnClickListener(this);
        mUndo.setOnClickListener(this);
        mRedo.setOnClickListener(this);
        mClear.setOnClickListener(this);
        mOk.setOnClickListener(this);
        mMenu.setOnClickListener(this);

        updateUndoRedo();

        if (mOutputFile == null) {
            Toast.makeText(this, R.string.cant_create_image_file, Toast.LENGTH_SHORT).show();
        }

        mSideAnimator = new ValueAnimator();
        mSideAnimator.setDuration(300);
        mSideAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mSide.setTranslationX((Float) animation.getAnimatedValue());
            }
        });

        mHideSideRunnable = new Runnable() {
            @Override
            public void run() {
                hideSide();
            }
        };
        SimpleHandler.getInstance().postDelayed(mHideSideRunnable, 3000);
    }

    @Override
    public void onBackPressed() {
        DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                if (which == DialogInterface.BUTTON_POSITIVE) {
                    if (mOutputFile != null) {
                        saveDoodle();
                    } else {
                        Toast.makeText(DoodleActivity.this, R.string.cant_create_image_file, Toast.LENGTH_SHORT).show();
                        finish();
                    }
                } else if (which == DialogInterface.BUTTON_NEUTRAL) {
                    finish();
                }
            }
        };

        AlertDialog dialog = new AlertDialog.Builder(this).setMessage(R.string.save_doodle)
                .setPositiveButton(R.string.save, listener)
                .setNegativeButton(android.R.string.cancel, null)
                .setNeutralButton(R.string.dont_save, listener)
                .show();
        Button button = dialog.getButton(DialogInterface.BUTTON_NEUTRAL);
        button.setTextColor(getResources().getColor(R.color.red_500));
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_CODE_SELECT_IMAGE && resultCode == RESULT_OK) {
            Uri uri = data.getData();
            if (uri == null) {
                return;
            }

            try {
                Bitmap bitmap = BitmapUtils.decodeStream(new UriInputStreamPipe(this, uri),
                        mDoodleView.getWidth(), mDoodleView.getHeight());
                if (bitmap != null) {
                    mDoodleView.insertBitmap(bitmap);
                    mImage.setActivated(true);
                }
            } catch (OutOfMemoryError e) {
                // Ignore
            }
        }
    }

    private void showSide() {
        if (mHideSideRunnable != null) {
            SimpleHandler.getInstance().removeCallbacks(mHideSideRunnable);
            mHideSideRunnable = null;
        }

        if (!mShowSide) {
            mShowSide = true;
            mSideAnimator.cancel();

            mSideAnimator.setInterpolator(AnimationUtils2.FAST_SLOW_INTERPOLATOR);
            mSideAnimator.setFloatValues(mSide.getTranslationX(), 0);
            mSideAnimator.start();
        }
    }

    private void hideSide() {
        if (mHideSideRunnable != null) {
            SimpleHandler.getInstance().removeCallbacks(mHideSideRunnable);
            mHideSideRunnable = null;
        }

        if (mShowSide) {
            mShowSide = false;
            mSideAnimator.cancel();

            mSideAnimator.setInterpolator(AnimationUtils2.SLOW_FAST_INTERPOLATOR);
            mSideAnimator.setFloatValues(mSide.getTranslationX(), -mSide.getWidth());
            mSideAnimator.start();
        }
    }

    private void updateUndoRedo() {
        mUndo.setEnabled(mDoodleView.canUndo());
        mRedo.setEnabled(mDoodleView.canRedo());
    }

    private class PickColorDialogHelper implements DialogInterface.OnClickListener {

        private final ColorPickerView mView;

        public PickColorDialogHelper() {
            mView = new ColorPickerView(DoodleActivity.this);
            mView.setColor(mDoodleView.getPaintColor());
        }

        public View getView() {
            return mView;
        }

        @Override
        public void onClick(DialogInterface dialog, int which) {
            if (DialogInterface.BUTTON_POSITIVE == which) {
                mDoodleView.setPaintColor(mView.getColor());
            }
        }
    }

    private void showPickColorDialog() {
        PickColorDialogHelper helper = new PickColorDialogHelper();
        new AlertDialog.Builder(DoodleActivity.this)
                .setView(helper.getView())
                .setPositiveButton(android.R.string.ok, helper)
                .setNegativeButton(android.R.string.cancel, null)
                .show();
    }

    private class ThicknessDialogHelper implements DialogInterface.OnClickListener, Slider.OnSetProgressListener {

        private final View mView;
        private final ThicknessPreviewView mTpv;
        private final Slider mSlider;

        @SuppressLint("InflateParams")
        public ThicknessDialogHelper() {
            mView = getLayoutInflater().inflate(R.layout.dialog_thickness, null);
            mTpv = (ThicknessPreviewView) mView.findViewById(R.id.thickness_preview_view);
            mSlider = (Slider) mView.findViewById(R.id.slider);

            mTpv.setThickness(mDoodleView.getPaintThickness());
            mTpv.setColor(mDoodleView.getPaintColor());
            mSlider.setProgress((int) LayoutUtils.pix2dp(DoodleActivity.this, mDoodleView.getPaintThickness()));
            mSlider.setOnSetProgressListener(this);
        }

        public View getView() {
            return mView;
        }

        @Override
        public void onClick(DialogInterface dialog, int which) {
            if (DialogInterface.BUTTON_POSITIVE == which) {
                mDoodleView.setPaintThickness(LayoutUtils.dp2pix(DoodleActivity.this, mSlider.getProgress()));
            }
        }

        @Override
        public void onSetProgress(Slider slider, int newProgress, int oldProgress, boolean byUser, boolean confirm) {
            mTpv.setThickness(LayoutUtils.dp2pix(DoodleActivity.this, newProgress));
        }

        @Override
        public void onFingerDown() {}

        @Override
        public void onFingerUp() {}
    }

    private void showThicknessDialog() {
        ThicknessDialogHelper helper = new ThicknessDialogHelper();
        new AlertDialog.Builder(DoodleActivity.this)
                .setView(helper.getView())
                .setPositiveButton(android.R.string.ok, helper)
                .show();
    }

    private void saveDoodle() {
        if (mExitWaitingDialog == null) {
            mExitWaitingDialog = new ProgressDialogBuilder(this)
                    .setTitle(R.string.please_wait)
                    .setMessage(R.string.saving)
                    .setCancelable(false)
                    .show();
            mDoodleView.save(mOutputFile);
        } else {
            // Wait here, it might be fast click back button
        }
    }

    @Override
    public void onClick(View v) {
        if (mSide == v) {
            hideSide();
        } else if (mPalette == v) {
            showPickColorDialog();
        } else if (mThickness == v) {
            showThicknessDialog();
        } else if (mDrawAction == v) {
            boolean newActivated = !mDrawAction.isActivated();
            mDrawAction.setActivated(newActivated);
            mDoodleView.setEraser(newActivated);
        } else if (mImage == v) {
            if (mDoodleView.hasInsertBitmap()) {
                mDoodleView.insertBitmap(null);
                mImage.setActivated(false);
            } else {
                Intent intent = new Intent();
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intent,
                        getString(R.string.select_picture)), REQUEST_CODE_SELECT_IMAGE);
            }
        } else if (mUndo == v) {
            mDoodleView.undo();
        } else if (mRedo == v) {
            mDoodleView.redo();
        } else if (mClear == v) {
            mDoodleView.clear();
        } else if (mOk == v) {
            if (mOutputFile != null) {
                saveDoodle();
            } else {
                Toast.makeText(this, R.string.cant_create_image_file, Toast.LENGTH_SHORT).show();
            }
        } else if (mMenu == v) {
            if (mShowSide) {
                hideSide();
            } else {
                showSide();
            }
        }
    }

    @Override
    public void onStoreChange(DoodleView view) {
        updateUndoRedo();
    }

    @Override
    public void onSavingFinished(boolean ok) {
        if (mExitWaitingDialog != null) {
            mExitWaitingDialog.dismiss();
            mExitWaitingDialog = null;
        }
        Intent intent = new Intent();
        intent.setData(Uri.fromFile(mOutputFile));
        setResult(RESULT_OK, intent);
        finish();
    }

    /*
    public static class DoodleLayout extends FrameLayout {

        public DoodleLayout(Context context) {
            super(context);
        }

        public DoodleLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

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

        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            final int count = getChildCount();

            final int parentLeft = getPaddingLeft();
            final int parentRight = right - left - getPaddingRight();

            final int parentTop = getPaddingTop();
            final int parentBottom = bottom - top - getPaddingBottom();

            for (int i = 0; i < count; i++) {
                final View child = getChildAt(i);
                if (child.getVisibility() != GONE) {
                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();

                    final int width = child.getMeasuredWidth();
                    final int height = child.getMeasuredHeight();

                    int childLeft;
                    int childTop;

                    int gravity = lp.gravity;
                    if (gravity == -1) {
                        gravity = Gravity.TOP | Gravity.START;
                    }

                    final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
                    switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                        case Gravity.CENTER_HORIZONTAL:
                            childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
                                    lp.leftMargin - lp.rightMargin;
                            break;
                        case Gravity.RIGHT:
                            childLeft = parentRight - width - lp.rightMargin;
                            break;
                        case Gravity.LEFT:
                        default:
                            childLeft = parentLeft + lp.leftMargin;
                    }

                    switch (verticalGravity) {
                        case Gravity.TOP:
                            childTop = parentTop + lp.topMargin;
                            break;
                        case Gravity.CENTER_VERTICAL:
                            childTop = parentTop + (parentBottom - parentTop - height) / 2 +
                                    lp.topMargin - lp.bottomMargin;
                            break;
                        case Gravity.BOTTOM:
                            childTop = parentBottom - height - lp.bottomMargin;
                            break;
                        default:
                            childTop = parentTop + lp.topMargin;
                    }

                    int offsetX = - (int) (lp.percent * width);
                    child.layout(childLeft + offsetX, childTop, childLeft + width, childTop + height);
                }
            }
        }

        @Override
        public LayoutParams generateLayoutParams(AttributeSet attrs) {
            return new LayoutParams(getContext(), attrs);
        }

        @Override
        protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
            return p instanceof LayoutParams;
        }

        @Override
        protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
            return new LayoutParams(p);
        }

        public static class LayoutParams extends FrameLayout.LayoutParams {

            public float percent = 0.0f;

            public LayoutParams(Context c, AttributeSet attrs) {
                super(c, attrs);
            }

            public LayoutParams(ViewGroup.LayoutParams source) {
                super(source);
            }
        }
    }
    */
}