/*
 * Copyright (C) 2015 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.ide.common.vectordrawable;

import java.awt.geom.Path2D;
import java.util.Arrays;

/**
 * Used to represent one VectorDrawble's path element.
 */
class VdPath extends VdElement{
    Node[] mNode = null;
    int mStrokeColor = 0;
    int mFillColor = 0;
    float mStrokeWidth = 0;
    float mRotate = 0;
    float mShiftX = 0;
    float mShiftY = 0;
    float mRotateX = 0;
    float mRotateY = 0;
    float trimPathStart = 0;
    float trimPathEnd = 1;
    float trimPathOffset = 0;
    int mStrokeLineCap = -1;
    int mStrokeLineJoin = -1;
    float mStrokeMiterlimit = -1;
    boolean mClip = false;
    float mStrokeOpacity = Float.NaN;
    float mFillOpacity = Float.NaN;
    float mTrimPathStart = 0;
    float mTrimPathEnd = 1;
    float mTrimPathOffset = 0;

    public void toPath(Path2D path) {
        path.reset();
        if (mNode != null) {
            VdNodeRender.creatPath(mNode, path);
        }
    }

    public static class Node {
        char type;
        float[] params;

        public Node(char type, float[] params) {
            this.type = type;
            this.params = params;
        }

        public Node(Node n) {
            this.type = n.type;
            this.params = Arrays.copyOf(n.params, n.params.length);
        }

        public static String NodeListToString(Node[] nodes) {
            String s = "";
            for (int i = 0; i < nodes.length; i++) {
                Node n = nodes[i];
                s += n.type;
                int len = n.params.length;
                for (int j = 0; j < len; j++) {
                    if (j > 0) {
                        s += ((j & 1) == 1) ? "," : " ";
                    }
                    // To avoid trailing zeros like 17.0, use this trick
                    float value = n.params[j];
                    if (value == (long) value) {
                        s += String.valueOf((long) value);
                    } else {
                        s += String.valueOf(value);
                    }

                }
            }
            return s;
        }

        public static void transform(float a,
                float b,
                float c,
                float d,
                float e,
                float f,
                Node[] nodes) {
            float[] pre = new float[2];
            for (int i = 0; i < nodes.length; i++) {
                nodes[i].transform(a, b, c, d, e, f, pre);
            }
        }

        public void transform(float a,
                float b,
                float c,
                float d,
                float e,
                float f,
                float[] pre) {
            int incr = 0;
            float[] tempParams;
            float[] origParams;
            switch (type) {

                case 'z':
                case 'Z':
                    return;
                case 'M':
                case 'L':
                case 'T':
                    incr = 2;
                    pre[0] = params[params.length - 2];
                    pre[1] = params[params.length - 1];
                    for (int i = 0; i < params.length; i += incr) {
                        matrix(a, b, c, d, e, f, i, i + 1);
                    }
                    break;
                case 'm':
                case 'l':
                case 't':
                    incr = 2;
                    pre[0] += params[params.length - 2];
                    pre[1] += params[params.length - 1];
                    for (int i = 0; i < params.length; i += incr) {
                        matrix(a, b, c, d, 0, 0, i, i + 1);
                    }
                    break;
                case 'h':
                    type = 'l';
                    pre[0] += params[params.length - 1];

                    tempParams = new float[params.length * 2];
                    origParams = params;
                    params = tempParams;
                    for (int i = 0; i < params.length; i += 2) {
                        params[i] = origParams[i / 2];
                        params[i + 1] = 0;
                        matrix(a, b, c, d, 0, 0, i, i + 1);
                    }

                    break;
                case 'H':
                    type = 'L';
                    pre[0] = params[params.length - 1];
                    tempParams = new float[params.length * 2];
                    origParams = params;
                    params = tempParams;
                    for (int i = 0; i < params.length; i += 2) {
                        params[i] = origParams[i / 2];
                        params[i + 1] = pre[1];
                        matrix(a, b, c, d, e, f, i, i + 1);
                    }
                    break;
                case 'v':
                    pre[1] += params[params.length - 1];
                    type = 'l';
                    tempParams = new float[params.length * 2];
                    origParams = params;
                    params = tempParams;
                    for (int i = 0; i < params.length; i += 2) {
                        params[i] = 0;
                        params[i + 1] = origParams[i / 2];
                        matrix(a, b, c, d, 0, 0, i, i + 1);
                    }
                    break;
                case 'V':
                    type = 'L';
                    pre[1] = params[params.length - 1];
                    tempParams = new float[params.length * 2];
                    origParams = params;
                    params = tempParams;
                    for (int i = 0; i < params.length; i += 2) {
                        params[i] = pre[0];
                        params[i + 1] = origParams[i / 2];
                        matrix(a, b, c, d, e, f, i, i + 1);
                    }
                    break;
                case 'C':
                case 'S':
                case 'Q':
                    pre[0] = params[params.length - 2];
                    pre[1] = params[params.length - 1];
                    for (int i = 0; i < params.length; i += 2) {
                        matrix(a, b, c, d, e, f, i, i + 1);
                    }
                    break;
                case 's':
                case 'q':
                case 'c':
                    pre[0] += params[params.length - 2];
                    pre[1] += params[params.length - 1];
                    for (int i = 0; i < params.length; i += 2) {
                        matrix(a, b, c, d, 0, 0, i, i + 1);
                    }
                    break;
                case 'a':
                    incr = 7;
                    pre[0] += params[params.length - 2];
                    pre[1] += params[params.length - 1];
                    for (int i = 0; i < params.length; i += incr) {
                        matrix(a, b, c, d, 0, 0, i, i + 1);
                        double ang = Math.toRadians(params[i + 2]);
                        params[i + 2] = (float) Math.toDegrees(ang + Math.atan2(b, d));
                        matrix(a, b, c, d, 0, 0, i + 5, i + 6);
                    }
                    break;
                case 'A':
                    incr = 7;
                    pre[0] = params[params.length - 2];
                    pre[1] = params[params.length - 1];
                    for (int i = 0; i < params.length; i += incr) {
                        matrix(a, b, c, d, e, f, i, i + 1);
                        double ang = Math.toRadians(params[i + 2]);
                        params[i + 2] = (float) Math.toDegrees(ang + Math.atan2(b, d));
                        matrix(a, b, c, d, e, f, i + 5, i + 6);
                    }
                    break;

            }
        }

        void matrix(float a,
                float b,
                float c,
                float d,
                float e,
                float f,
                int offx,
                int offy) {
            float inx = (offx < 0) ? 1 : params[offx];
            float iny = (offy < 0) ? 1 : params[offy];
            float x = inx * a + iny * c + e;
            float y = inx * b + iny * d + f;
            if (offx >= 0) {
                params[offx] = x;
            }
            if (offy >= 0) {
                params[offy] = y;
            }
        }
    }

    public VdPath() {
        mName = this.toString(); // to ensure paths have unique names
    }

    /**
     * TODO: support rotation attribute for stroke width
     */
    public void transform(float a, float b, float c, float d, float e, float f) {
        mStrokeWidth *= Math.hypot(a + b, c + d);
        Node.transform(a, b, c, d, e, f, mNode);
    }
}
