/*
 * Copyright ©1998-2020 by Richard A. Wilkes. All rights reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, version 2.0. If a copy of the MPL was not distributed with
 * this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This Source Code Form is "Incompatible With Secondary Licenses", as
 * defined by the Mozilla Public License, version 2.0.
 */

package com.trollworks.gcs.ui.widget.outline;

import com.trollworks.gcs.ui.Colors;
import com.trollworks.gcs.ui.Fonts;
import com.trollworks.gcs.ui.TextDrawing;
import com.trollworks.gcs.ui.scale.Scale;
import com.trollworks.gcs.utility.text.NumericComparator;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.SwingConstants;
import javax.swing.UIManager;

/**
 * A {@link Cell} for displaying both a piece of primary information and a piece of secondary
 * information for a {@link ListRow}.
 */
public class MultiCell implements Cell {
    private static final int     H_MARGIN = 2;
    private              int     mMaxPreferredWidth;
    private              boolean mForEditor;

    /** Creates a new {@link MultiCell} with a maximum preferred width of 250. */
    public MultiCell() {
        this(false);
    }

    /**
     * Creates a new {@link MultiCell} with a maximum preferred width of 250.
     *
     * @param forEditor Whether this is for an editor dialog or for a character sheet.
     */
    public MultiCell(boolean forEditor) {
        this(250, forEditor);
    }

    /**
     * Creates a new {@link MultiCell}.
     *
     * @param maxPreferredWidth The maximum preferred width to use. Pass in -1 for no limit.
     * @param forEditor         Whether this is for an editor dialog or for a character sheet.
     */
    public MultiCell(int maxPreferredWidth, boolean forEditor) {
        mMaxPreferredWidth = maxPreferredWidth;
        mForEditor = forEditor;
    }

    /** @return The primary font. */
    public Font getPrimaryFont() {
        return UIManager.getFont(mForEditor ? "TextField.font" : Fonts.KEY_FIELD_PRIMARY);
    }

    /** @return The secondary font, for notes. */
    public Font getSecondaryFont() {
        if (mForEditor) {
            Font font = getPrimaryFont();
            return font.deriveFont(font.getSize() * 7.0f / 8.0f);
        }
        return UIManager.getFont(Fonts.KEY_FIELD_SECONDARY);
    }

    /**
     * @param row The row to use.
     * @return The primary text to display.
     */
    @SuppressWarnings("static-method")
    protected String getPrimaryText(ListRow row) {
        return row.toString();
    }

    /**
     * @param row The row to use.
     * @return The text to sort.
     */
    protected String getSortText(ListRow row) {
        String text      = getPrimaryText(row);
        String secondary = getSecondaryText(row);
        if (secondary != null && !secondary.isEmpty()) {
            text += '\n';
            text += secondary;
        }
        return text;
    }

    /**
     * @param row The row to use.
     * @return The secondary text to display.
     */
    @SuppressWarnings("static-method")
    protected String getSecondaryText(ListRow row) {
        return row.getSecondaryText();
    }

    @Override
    public void drawCell(Outline outline, Graphics gc, Rectangle bounds, Row row, Column column, boolean selected, boolean active) {
        Scale     scale       = Scale.get(outline);
        ListRow   theRow      = (ListRow) row;
        int       hMargin     = scale.scale(H_MARGIN);
        Rectangle insetBounds = new Rectangle(bounds.x + hMargin, bounds.y, bounds.width - hMargin * 2, bounds.height);
        String    notes       = getSecondaryText(theRow);
        Font      font        = scale.scale(getPrimaryFont());
        int       pos;
        gc.setColor(getColor(selected, active, row, column));
        gc.setFont(font);
        Color strikeThru = row instanceof Switchable && !((Switchable) row).isEnabled() ? Color.RED : null;
        pos = TextDrawing.draw(gc, insetBounds, getPrimaryText(theRow), SwingConstants.LEFT, SwingConstants.TOP, strikeThru, scale.scale(1));
        if (!notes.trim().isEmpty()) {
            insetBounds.height -= pos - insetBounds.y;
            insetBounds.y = pos;
            gc.setFont(scale.scale(getSecondaryFont()));
            TextDrawing.draw(gc, insetBounds, notes, SwingConstants.LEFT, SwingConstants.TOP);
        }
    }

    /**
     * @param selected Whether or not the selected version of the color is needed.
     * @param active   Whether or not the active version of the color is needed.
     * @param row      The row.
     * @param column   The column.
     * @return The foreground color.
     */
    @SuppressWarnings("static-method")
    public Color getColor(boolean selected, boolean active, Row row, Column column) {
        if (((ListRow) row).isSatisfied()) {
            return Colors.getListForeground(selected, active);
        }
        return Color.RED;
    }

    @Override
    public int getPreferredWidth(Outline outline, Row row, Column column) {
        Scale   scale  = Scale.get(outline);
        ListRow theRow = (ListRow) row;
        int     width  = TextDrawing.getWidth(scale.scale(getPrimaryFont()), getPrimaryText(theRow));
        String  notes  = getSecondaryText(theRow);
        if (!notes.trim().isEmpty()) {
            int notesWidth = TextDrawing.getWidth(scale.scale(getSecondaryFont()), notes);

            if (notesWidth > width) {
                width = notesWidth;
            }
        }
        width += scale.scale(H_MARGIN) * 2;
        int scaledMax = scale.scale(mMaxPreferredWidth);
        return mMaxPreferredWidth != -1 && scaledMax < width ? scaledMax : width;
    }

    @Override
    public int getPreferredHeight(Outline outline, Row row, Column column) {
        Scale   scale  = Scale.get(outline);
        ListRow theRow = (ListRow) row;
        Font    font   = scale.scale(getPrimaryFont());
        int     height = TextDrawing.getPreferredSize(font, wrap(scale, theRow, column, getPrimaryText(theRow), font)).height;
        String  notes  = getSecondaryText(theRow);
        if (!notes.trim().isEmpty()) {
            font = scale.scale(getSecondaryFont());
            height += TextDrawing.getPreferredSize(font, wrap(scale, theRow, column, notes, font)).height;
        }
        return height;
    }

    private String wrap(Scale scale, ListRow row, Column column, String text, Font font) {
        int width = column.getWidth();
        if (width == -1) {
            if (mMaxPreferredWidth == -1) {
                return text;
            }
            width = scale.scale(mMaxPreferredWidth);
        }
        OutlineModel owner  = row.getOwner();
        int          indent = owner != null ? scale.scale(owner.getIndentWidth(row, column)) : 0;
        return TextDrawing.wrapToPixelWidth(font, text, width - (indent + scale.scale(H_MARGIN) * 2));
    }

    @Override
    public int compare(Column column, Row one, Row two) {
        return NumericComparator.caselessCompareStrings(getSortText((ListRow) one), getSortText((ListRow) two));
    }

    @Override
    public Cursor getCursor(MouseEvent event, Rectangle bounds, Row row, Column column) {
        return Cursor.getDefaultCursor();
    }

    /** Allow a satisfied row to display a tooltip */
    @Override
    public String getToolTipText(Outline outline, MouseEvent event, Rectangle bounds, Row row, Column column) {
        ListRow theRow = (ListRow) row;
        if (!theRow.isSatisfied()) {
            return theRow.getReasonForUnsatisfied();
        }
        String text = row.getToolTip(column);
        if (text == null || text.isBlank()) {
            return null;
        }
        return text;
    }

    @Override
    public boolean participatesInDynamicRowLayout() {
        return true;
    }

    @Override
    public void mouseClicked(MouseEvent event, Rectangle bounds, Row row, Column column) {
        // Does nothing
    }
}