/*
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * 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 io.syndesis.connector.sheets.model;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.apache.camel.util.ObjectHelper;

public class CellCoordinate {

    private int rowIndex;
    private int columnIndex;

    /**
     * Prevent direct instantiation
     */
    CellCoordinate() {
        super();
    }

    /**
     * Construct grid coordinate from given cell identifier representation in A1 form. For instance convert
     * cell id string "A1" to a coordinate with rowIndex=0, and columnIndex=0.
     */
    public static CellCoordinate fromCellId(String cellId) {
        CellCoordinate coordinate = new CellCoordinate();

        if (cellId != null) {
            coordinate.setRowIndex(getRowIndex(cellId));
            coordinate.setColumnIndex(getColumnIndex(cellId));
        }

        return coordinate;
    }

    /**
     * Evaluate the column index from cellId in A1 notation. Column name letters are translated to numeric column index values.
     * Column "A" will result in column index 0. Method does support columns with combined name letters such as "AA" where this is
     * the first column after "Z" resulting in a column index of 26.
     */
    protected static int getColumnIndex(String cellId) {
        char[] characters = cellId.toCharArray();
        List<Integer> chars = IntStream.range(0, characters.length)
            .mapToObj(i -> characters[i])
            .filter(c -> !Character.isDigit(c))
            .map(Character::toUpperCase)
            .map(Character::getNumericValue)
            .collect(Collectors.toList());

        if (chars.size() > 1) {
            int index = 0;
            for (int i = 0; i < chars.size(); i++) {
                if (i == chars.size() -1) {
                    index += chars.get(i) - Character.digit('A', 26);
                } else {
                    index += ((chars.get(i) - Character.digit('A', 26)) + 1) * 26;
                }
            }
            return index;
        } else if (chars.size() == 1) {
            return chars.get(0) - Character.digit('A', 26);
        } else {
            return 0;
        }
    }

    /**
     * Evaluates the row index from a given cellId in A1 notation. Extracts the row number and translates that to an numeric
     * index value beginning with 0.
     */
    protected static int getRowIndex(String cellId) {
        char[] characters = cellId.toCharArray();
        String index = IntStream.range(0, characters.length)
            .mapToObj(i -> characters[i])
            .filter(Character::isDigit)
            .map(String::valueOf)
            .collect(Collectors.joining());

        if (ObjectHelper.isNotEmpty(index)) {
            return Integer.parseInt(index) - 1;
        }

        return 0;
    }

    /**
     * Evaluates column name in A1 notation based on the column index. Index 0 will be "A" and index 25 will be "Z". Method also supports
     * name overflow where index 26 will be "AA" and index 51 will be "AZ" and so on.
     */
    public static String getColumnName(int columnIndex) {
        String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        StringBuilder columnName = new StringBuilder();

        int index = columnIndex;
        int overflowIndex = -1;
        while (index > 25) {
            overflowIndex++;
            index -= 26;
        }

        if (overflowIndex >= 0) {
            columnName.append(alphabet.toCharArray()[overflowIndex]);
        }

        columnName.append(alphabet.toCharArray()[index]);

        return columnName.toString();
    }

    /**
     * Special getter for column name where user is able to give set of user defined column names. When given column index is resolvable via custom names
     * the custom column name is returned otherwise the evaluated default column name is returned.
     */
    public static String getColumnName(int columnIndex, int columnStartIndex, String ... columnNames) {
        String columnName = getColumnName(columnIndex);

        int index;
        if (columnStartIndex > 0) {
            index = columnIndex % columnStartIndex;
        } else {
            index = columnIndex;
        }

        if (index < columnNames.length) {
            String name = columnNames[index];
            if (columnName.equals(name)) {
                return columnName;
            } else {
                return name;
            }
        }

        return columnName;
    }

    public int getRowIndex() {
        return rowIndex;
    }

    /**
     * Specifies the rowIndex.
     */
    public void setRowIndex(int rowIndex) {
        this.rowIndex = rowIndex;
    }

    public int getColumnIndex() {
        return columnIndex;
    }

    /**
     * Specifies the columnIndex.
     */
    public void setColumnIndex(int columnIndex) {
        this.columnIndex = columnIndex;
    }
}
