/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */

/* $Id$ */

package org.apache.fop.render.txt.border;

import java.awt.Point;
import java.util.Arrays;

import org.apache.fop.area.CTM;
import org.apache.fop.fo.Constants;
import org.apache.fop.render.txt.TXTState;

/**
 * This class keeps information about abstract border element, i.e. specifies
 * border element for one text position.
 */
public abstract class AbstractBorderElement implements Constants {

    /**
     * Constant for a line segment, directing from a center of symbol up
     * the the symbol boundary.
     */
    public static final int UP = 0;

    /**
     * Constant for a line segment, directing from a center of symbol right
     * the the symbol boundary.
     */
    public static final int RIGHT = 1;

    /**
     * Constant for a line segment, directing from a center of symbol down
     * the the symbol boundary.
     */
    public static final int DOWN = 2;

    /**
     * Constant for a line segment, directing from a center of symbol left
     * the the symbol boundary.
     */
    public static final int LEFT = 3;

    /**
     * I-th element of this array specify, if there line from center of symbol
     * to corresponding side (UP, RIGHT, DOWN, LEFT).
     */
    protected int[] data = {0, 0, 0, 0};

    /**
     * Initializes a newly created <code>AbstractBorderElement</code> object
     * so that it represents an empty border element.
     */
    public AbstractBorderElement() {
    }

    /**
     * Constructs a newly allocated <code>AbstractBorderElement</code> object.
     * Fills array <code>data</code> using binary representation of
     * <code>type</code>.
     *
     * @param type binary representation of type gives <code>data</code>
     */
    public AbstractBorderElement(int type) {
        for (int i = 0; i < 4; i++) {
            data[i] = (type >> i) & 1;
        }
    }

    /**
     * Returns value of side's element of <code>data</code>.
     *
     * @param side integer, representing side
     * @return value of side's element
     */
    public int getData(int side) {
        return data[side];
    }

    /**
     * Sets a value for <code>data[side]</code>.
     *
     * @param side integer, representing side
     * @param value a new value for <code>data[side]</code>
     */
    public void setData(int side, int value) {
        data[side] = value;
    }

    /**
     * Transform border element in according with <code>state</code>.
     * @param state instance of TXTState
     */
    public void transformElement(TXTState state) {
        // here we'll get CTM^-1 without shift
        double[] da = state.getResultCTM().toArray();
        CTM ctm = new CTM(da[0], -da[1], -da[2], da[3], 0, 0);

        Point[] pa = new Point[4];
        pa[0] = new Point(0, data[UP]);
        pa[1] = new Point(data[RIGHT], 0);
        pa[2] = new Point(0, -data[DOWN]);
        pa[3] = new Point(-data[LEFT], 0);

        Arrays.fill(data, 0);
        for (int i = 0; i < 4; i++) {
            Point p = state.transformPoint(pa[i], ctm);

            int length = (int) p.distance(0, 0);
            if (p.x == 0 && p.y > 0) {
                data[UP] = length;
            } else if (p.x == 0 && p.y < 0) {
                data[DOWN] = length;
            } else if (p.x > 0 && p.y == 0) {
                data[RIGHT] = length;
            } else if (p.x < 0 && p.y == 0) {
                data[LEFT] = length;
            }
        }
    }

    /**
     * Merges with border element.
     * @param e instance of AbstractBorderElement
     * @return instance of AbstractBorderElement
     */
    public abstract AbstractBorderElement merge(AbstractBorderElement e);

    /**
     * Convert internal representation of border element to char.
     * @return corresponding char
     */
    public abstract char convert2Char();
}
