/*
 * Copyright 2015 the original author or authors.
 *
 * 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
 *
 *      https://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 org.springframework.shell.table;

import java.util.ArrayList;
import java.util.List;

import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

/**
 * This represents a directive to set some borders on cells of a table.
 * Multiple specifications can be combined on a single table.
 *
 * @author Eric Bottard
 */
public class BorderSpecification {

	public static final int NONE = 0;

	public static final int TOP = 1;

	public static final int BOTTOM = 2;

	public static final int LEFT = 4;

	public static final int RIGHT = 8;

	public static final int INNER_VERTICAL = 16;

	public static final int INNER_HORIZONTAL = 32;

	public static final int OUTLINE = TOP | BOTTOM | LEFT | RIGHT;

	public static final int FULL = OUTLINE | INNER_HORIZONTAL | INNER_VERTICAL;

	public static final int INNER = INNER_HORIZONTAL | INNER_VERTICAL;

	private final int row1, row2, column1, column2;

	private final int match;

	private final BorderStyle style;

	/**
	 * Specifications are created by {@link Table#addBorder(int, int, int, int, int, BorderStyle)}.
	 */
	/*default*/ BorderSpecification(int row1, int column1, int row2, int column2, int match, BorderStyle style) {
		this.row1 = row1;
		this.row2 = row2;
		this.column1 = column1;
		this.column2 = column2;
		this.match = match;
		this.style = style;
	}

	/**
	 * Does this specification result in the need to paint a vertical bar at row,column?
	 */
	/*default*/ char verticals(int row, int column) {
		boolean result = (match & LEFT) == LEFT && column == column1;
		result |= (match & INNER_VERTICAL) == INNER_VERTICAL && column > column1 && column < column2;
		result |= (match & RIGHT) == RIGHT && column == column2;

		result &= row >= row1;
		result &= row < row2;
		return result ? style.verticalGlyph() : BorderStyle.NONE;
	}

	/**
	 * Does this specification result in the need to paint an horizontal bar at row,column?
	 */
	/*default*/ char horizontals(int row, int column) {
		boolean result = (match & TOP) == TOP && row == row1;
		result |= (match & INNER_HORIZONTAL) == INNER_HORIZONTAL && row > row1 && row < row2;
		result |= (match & BOTTOM) == BOTTOM && row == row2;

		result &= column >= column1;
		result &= column < column2;
		return result ? style.horizontalGlyph() : BorderStyle.NONE;
	}

	@Override
	public String toString() {
		return String.format("%s[(%d, %d)->(%d, %d), %s, %s]", getClass().getSimpleName(), row1, column1, row2, column2, style, matchConstants());
	}

	private String matchConstants() {
		try {
			for (String field : new String[] {"NONE", "INNER", "FULL", "OUTLINE"}) {
				int value = ReflectionUtils.findField(getClass(), field).getInt(null);
				if (match  == value) {
					return field;
				}
			}
			List<String> constants = new ArrayList<String>();
			for (String field : new String[] {"TOP", "BOTTOM", "LEFT", "RIGHT", "INNER_HORIZONTAL", "INNER_VERTICAL"}) {
				int value = ReflectionUtils.findField(getClass(), field).getInt(null);
				if ((match & value) == value) {
					constants.add(field);
				}
			}
			return StringUtils.collectionToDelimitedString(constants, "|");
		}
		catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
}