// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.widget;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.graphics.RectF;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.Log;

import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.UrlConstants;
import org.chromium.chrome.browser.util.UrlUtilities;

import java.net.URI;
import java.util.Locale;

import javax.annotation.Nullable;

/**
 * Generator for transparent icons containing a rounded rectangle with a given background color,
 * having a centered character drawn on top of it.
 */
public class RoundedIconGenerator {
    private static final String TAG = RoundedIconGenerator.class.getSimpleName();

    private final int mIconWidthPx;
    private final int mIconHeightPx;
    private final int mCornerRadiusPx;

    private final RectF mBackgroundRect;

    private final Paint mBackgroundPaint;
    private final TextPaint mTextPaint;

    private final float mTextHeight;
    private final float mTextYOffset;

    /**
     * Constructs the generator and initializes the common members based on the display density.
     *
     * @param context The context used for initialization.
     * @param iconWidthDp The width of the generated icon in dp.
     * @param iconHeightDp The height of the generated icon in dp.
     * @param cornerRadiusDp The radius of the corners in the icon in dp.
     * @param backgroundColor Color with which the rounded rectangle should be drawn.
     * @param textSizeDp Size at which the text should be drawn in dp.
     */
    public RoundedIconGenerator(Context context, int iconWidthDp, int iconHeightDp,
            int cornerRadiusDp, int backgroundColor, int textSizeDp) {
        this((int) (context.getResources().getDisplayMetrics().density * iconWidthDp),
                (int) (context.getResources().getDisplayMetrics().density * iconHeightDp),
                (int) (context.getResources().getDisplayMetrics().density * cornerRadiusDp),
                backgroundColor,
                (int) (context.getResources().getDisplayMetrics().density * textSizeDp));
    }

    /**
     * Constructs the generator and initializes the common members ignoring display density.
     *
     * @param iconWidthPx The width of the generated icon in pixels.
     * @param iconHeightPx The height of the generated icon in pixels.
     * @param cornerRadiusPx The radius of the corners in the icon in pixels.
     * @param backgroundColor Color at which the rounded rectangle should be drawn.
     * @param textSizePx Size at which the text should be drawn in pixels.
     */
    public RoundedIconGenerator(int iconWidthPx, int iconHeightPx, int cornerRadiusPx,
            int backgroundColor, float textSizePx) {
        mIconWidthPx = iconWidthPx;
        mIconHeightPx = iconHeightPx;
        mCornerRadiusPx = cornerRadiusPx;

        mBackgroundRect = new RectF(0, 0, mIconWidthPx, mIconHeightPx);

        mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBackgroundPaint.setColor(backgroundColor);

        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setFakeBoldText(true);
        mTextPaint.setTextSize(textSizePx);

        FontMetrics textFontMetrics = mTextPaint.getFontMetrics();
        mTextHeight = (float) Math.ceil(textFontMetrics.bottom - textFontMetrics.top);
        mTextYOffset = -textFontMetrics.top;
    }

    /**
     * Sets the background color to use when generating icons.
     */
    public void setBackgroundColor(int color) {
        mBackgroundPaint.setColor(color);
    }

    /**
     * Generates an icon based on |text|.
     *
     * @param text The text to render the first character of on the icon.
     * @return The generated icon.
     */
    public Bitmap generateIconForText(String text) {
        Bitmap icon = Bitmap.createBitmap(mIconWidthPx, mIconHeightPx, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(icon);

        canvas.drawRoundRect(mBackgroundRect, mCornerRadiusPx, mCornerRadiusPx, mBackgroundPaint);

        String displayText = text.substring(0, 1).toUpperCase(Locale.getDefault());
        float textWidth = mTextPaint.measureText(displayText);

        canvas.drawText(
                displayText,
                (mIconWidthPx - textWidth) / 2f,
                Math.round((Math.max(mIconHeightPx, mTextHeight) - mTextHeight)
                        / 2.0f + mTextYOffset),
                mTextPaint);

        return icon;
    }

    /**
     * Returns a Bitmap representing the icon to be used for |url|.
     *
     * @param url URL for which the icon should be generated.
     * @param includePrivateRegistries Should private registries be considered as TLDs?
     * @return The generated icon, or NULL if |url| is empty or the domain cannot be resolved.
     */
    @Nullable
    public Bitmap generateIconForUrl(String url, boolean includePrivateRegistries) {
        if (TextUtils.isEmpty(url)) return null;

        String text = getIconTextForUrl(url, includePrivateRegistries);
        if (TextUtils.isEmpty(text)) return null;

        return generateIconForText(text);
    }

    /**
     * Returns a Bitmap representing the icon to be used for |url|. Private registries such
     * as "appspot.com" will not be considered as effective TLDs.
     *
     * @TODO(beverloo) Update all call-sites of rounded icons to be explicit about whether
     * private registries should be considered, matching the getDomainAndRegistry requirements.
     * See https://crbug.com/458104.
     *
     * @param url URL for which the icon should be generated.
     * @return The generated icon, or NULL if |url| is empty or the domain cannot be resolved.
     */
    @Nullable
    public Bitmap generateIconForUrl(String url) {
        return generateIconForUrl(url, false);
    }

    /**
     * Returns the text which should be used for generating a rounded icon based on |url|.
     *
     * @param url URL to consider when getting the icon's text.
     * @param includePrivateRegistries Should private registries be considered as TLDs?
     * @return The text to use on the rounded icon, or NULL if |url| is empty or the domain cannot
     *         be resolved.
     */
    @Nullable
    @VisibleForTesting
    public static String getIconTextForUrl(String url, boolean includePrivateRegistries) {
        String domain = UrlUtilities.getDomainAndRegistry(url, includePrivateRegistries);
        if (!TextUtils.isEmpty(domain)) return domain;

        // Special-case chrome:// and chrome-native:// URLs.
        if (url.startsWith(UrlConstants.CHROME_SCHEME)
                || url.startsWith(UrlConstants.CHROME_NATIVE_SCHEME)) {
            return "chrome";
        }

        // Use the host component of |url| when it can be parsed as a URI.
        try {
            URI uri = new URI(url);
            if (!TextUtils.isEmpty(uri.getHost())) {
                return uri.getHost();
            }
        } catch (Exception e) {
            Log.w(TAG, "Unable to parse the URL for generating an icon: " + url);
        }

        return url;
    }
}