package com.yidianling.common.view.ticker;

import android.graphics.Canvas;
import android.graphics.Paint;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * In ticker, each character in the rendered text is represented by a {@link RxTickerColumn}. The
 * column can be seen as a column of text in which we can animate from one character to the next
 * by scrolling the column vertically. The {@link RxTickerColumnManager} is then a
 * manager/convenience class for handling a list of {@link RxTickerColumn} which then combines into
 * the entire string we are rendering.
 *
 */
class RxTickerColumnManager {
    final ArrayList<RxTickerColumn> mRxTickerColumns = new ArrayList<>();
    private final RxTickerDrawMetrics metrics;

    // The character list that dictates how to transition from one character to another.
    private char[] characterList;
    // A minor optimization so that we can cache the indices of each character.
    private Map<Character, Integer> characterIndicesMap;

    RxTickerColumnManager(RxTickerDrawMetrics metrics) {
        this.metrics = metrics;
    }

    /**
     * @see {@link RxTickerView#setCharacterList(char[])}.
     */
    void setCharacterList(char[] characterList) {
        this.characterList = characterList;
        this.characterIndicesMap = new HashMap<>(characterList.length);

        for (int i = 0; i < characterList.length; i++) {
            characterIndicesMap.put(characterList[i], i);
        }
    }

    /**
     * @return whether or not {@param text} should be debounced because it's the same as the
     *         current target text of this column manager.
     */
    boolean shouldDebounceText(char[] text) {
        final int newTextSize = text.length;
        if (newTextSize == mRxTickerColumns.size()) {
            for (int i = 0; i < newTextSize; i++) {
                if (text[i] != mRxTickerColumns.get(i).getTargetChar()) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    /**
     * Tell the column manager the new target text that it should display.
     */
    void setText(char[] text) {
        if (characterList == null) {
            throw new IllegalStateException("Need to call setCharacterList(char[]) first.");
        }

        // First remove any zero-width columns
        for (int i = 0; i < mRxTickerColumns.size(); ) {
            final RxTickerColumn rxTickerColumn = mRxTickerColumns.get(i);
            if (rxTickerColumn.getCurrentWidth() > 0) {
                i++;
            } else {
                mRxTickerColumns.remove(i);
            }
        }

        // Use Levenshtein distance algorithm to figure out how to manipulate the columns
        final int[] actions = RxLevenshteinUtils.computeColumnActions(getCurrentText(), text);
        int columnIndex = 0;
        int textIndex = 0;
        for (int i = 0; i < actions.length; i++) {
            switch (actions[i]) {
                case RxLevenshteinUtils.ACTION_INSERT:
                    mRxTickerColumns.add(columnIndex,
                            new RxTickerColumn(characterList, characterIndicesMap, metrics));
                case RxLevenshteinUtils.ACTION_SAME:
                    mRxTickerColumns.get(columnIndex).setTargetChar(text[textIndex]);
                    columnIndex++;
                    textIndex++;
                    break;
                case RxLevenshteinUtils.ACTION_DELETE:
                    mRxTickerColumns.get(columnIndex).setTargetChar(RxTickerUtils.EMPTY_CHAR);
                    columnIndex++;
                    break;
                default:
                    throw new IllegalArgumentException("Unknown action: " + actions[i]);
            }
        }
    }

    void onAnimationEnd() {
        for (int i = 0, size = mRxTickerColumns.size(); i < size; i++) {
            final RxTickerColumn column = mRxTickerColumns.get(i);
            column.onAnimationEnd();
        }
    }

    void setAnimationProgress(float animationProgress) {
        for (int i = 0, size = mRxTickerColumns.size(); i < size; i++) {
            final RxTickerColumn column = mRxTickerColumns.get(i);
            column.setAnimationProgress(animationProgress);
        }
    }

    float getMinimumRequiredWidth() {
        float width = 0f;
        for (int i = 0, size = mRxTickerColumns.size(); i < size; i++) {
            width += mRxTickerColumns.get(i).getMinimumRequiredWidth();
        }
        return width;
    }

    float getCurrentWidth() {
        float width = 0f;
        for (int i = 0, size = mRxTickerColumns.size(); i < size; i++) {
            width += mRxTickerColumns.get(i).getCurrentWidth();
        }
        return width;
    }

    char[] getCurrentText() {
        final int size = mRxTickerColumns.size();
        final char[] currentText = new char[size];
        for (int i = 0; i < size; i++) {
            currentText[i] = mRxTickerColumns.get(i).getCurrentChar();
        }
        return currentText;
    }

    /**
     * This method will draw onto the canvas the appropriate UI state of each column dictated
     * by {@param animationProgress}. As a side effect, this method will also translate the canvas
     * accordingly for the draw procedures.
     */
    void draw(Canvas canvas, Paint textPaint) {
        for (int i = 0, size = mRxTickerColumns.size(); i < size; i++) {
            final RxTickerColumn column = mRxTickerColumns.get(i);
            column.draw(canvas, textPaint);
            canvas.translate(column.getCurrentWidth(), 0f);
        }
    }
}