/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * Copyright @ 2015 Atlassian Pty Ltd
 *
 * 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 net.java.sip.communicator.plugin.desktoputil.plaf;

import java.awt.*;
import java.awt.event.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.*;
import javax.swing.plaf.*;
import javax.swing.text.*;

import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.util.skin.*;

import org.apache.commons.lang3.StringUtils;
import org.jitsi.service.resources.*;
import org.jitsi.utils.*;

/**
 * The <tt>SearchTextFieldUI</tt> is the one responsible for the search field
 * look & feel. It draws a search icon inside the field and adjusts the bounds
 * of the editor rectangle according to it.
 *
 * @author Yana Stamcheva
 * @author Adam Netocny
 * @author Marin Dzhigarov
 * @author Damian Minkov
 */
public class SearchFieldUI
    extends SIPCommTextFieldUI
    implements Skinnable
{
    /**
     * Creates a UI for a SearchFieldUI.
     *
     * @param c the text field
     * @return the UI
     */
    public static ComponentUI createUI(JComponent c)
    {
        return new SearchFieldUI();
    }

    /**
     * The default icon of the call button.
     */
    private Image callIcon;

    /**
     * The pressed icon of the call button.
     */
    private Image callPressedIcon;

    /**
     * The roll over icon of the call button.
     */
    private Image callRolloverIcon;

    /**
     * The default icon of the sms button.
     */
    private Image smsIcon;

    /**
     * The pressed icon of the sms button.
     */
    private Image smsPressedIcon;

    /**
     * The roll over icon of the sms button.
     */
    private Image smsRolloverIcon;

    /**
     * The call button tool tip string.
     */
    private final String callString = DesktopUtilActivator.getResources()
        .getI18NString("service.gui.CALL");

    /**
     * The sms button tool tip string.
     */
    private final String smsString = DesktopUtilActivator.getResources()
        .getI18NString("service.gui.SMS");

    /**
     * Indicates if the call button is enabled in this search field.
     */
    private boolean isCallButtonEnabled = true;

    /**
     * Indicates if the sms button is enabled in this search field.
     */
    private boolean isSMSButtonEnabled = false;

    /**
     * Indicates if the call icon is currently visible.
     */
    protected boolean isCallIconVisible = false;

    /**
     * Indicates if the mouse is currently over the call button.
     */
    private boolean isCallMouseOver = false;

    /**
     * Indicates if the mouse is currently over the call button.
     */
    private boolean isCallMousePressed = false;

    /**
     * Indicates if the sms icon is currently visible.
     */
    protected boolean isSMSIconVisible = false;

    /**
     * Indicates if the mouse is currently over the sms button.
     */
    private boolean isSMSMouseOver = false;

    /**
     * Indicates if the mouse is currently over the sms button.
     */
    private boolean isSMSMousePressed = false;

    /**
     * The icon indicating that this is a search field.
     */
    private ImageIcon searchIcon;

    /**
     * The separator icon shown between the call icon and the close.
     */
    private Image separatorIcon;

    /**
     * Creates a <tt>GenericSearchFieldUI</tt>.
     */
    public SearchFieldUI()
    {
        isCallButtonEnabled = false;

        loadSkin();
    }

    /**
     * Paints the background of the associated component.
     * @param g the <tt>Graphics</tt> object used for painting
     */
    @Override
    protected void customPaintBackground(Graphics g)
    {
        Graphics2D g2 = (Graphics2D) g.create();

        try
        {
            AntialiasingManager.activateAntialiasing(g2);
            super.customPaintBackground(g2);

            JTextComponent c = this.getComponent();

            int dy = c.getHeight() / 2
                - searchIcon.getIconHeight()/2;

            g2.drawImage(searchIcon.getImage(), 3, dy, null);
            String str = c.getText();

            if (c.getText() != null
                && c.getText().length() > 0
                && (str != null && StringUtils.isNotEmpty(str) && !StringUtils.isAlpha(str))
                && isSMSButtonEnabled)
            {
                // Paint sms button.
                Rectangle smsRect = getSMSButtonRect();
                int dx = smsRect.x;
                dy = smsRect.y;

                if (isSMSMousePressed)
                    g2.drawImage(smsPressedIcon, dx, dy, null);
                else if (isSMSMouseOver)
                {
                    g2.drawImage(smsRolloverIcon, dx, dy, null);
                }
                else
                    g2.drawImage(smsIcon, dx, dy, null);

                isSMSIconVisible = true;
            }
            else
                isSMSIconVisible = false;

            if (c.getText() != null
                && c.getText().length() > 0
                && isCallButtonEnabled)
            {
                // Paint call button.
                Rectangle callRect = getCallButtonRect();
                int dx = callRect.x;
                dy = callRect.y;

                if (isCallMousePressed)
                    g2.drawImage(callPressedIcon, dx, dy, null);
                else if (isCallMouseOver)
                    g2.drawImage(callRolloverIcon, dx, dy, null);
                else
                    g2.drawImage(callIcon, dx, dy, null);

                g2.drawImage(   separatorIcon,
                                dx + callRect.width + 3,
                                dy + (callRect.height
                                    - separatorIcon.getHeight(null))/2,
                                null);

                isCallIconVisible = true;
            }
            else
                isCallIconVisible = false;
        }
        finally
        {
            g2.dispose();
        }
    }

    /**
     * Calculates the call button rectangle.
     *
     * @return the call button rectangle
     */
    protected Rectangle getCallButtonRect()
    {
        Component c = getComponent();
        Rectangle rect = c.getBounds();

        int dx = getDeleteButtonRect().x - callRolloverIcon.getWidth(null) - 8;
        int dy = (rect.height) / 2 - callRolloverIcon.getHeight(null)/2;

        return new Rectangle(   dx,
                                dy,
                                callRolloverIcon.getWidth(null),
                                callRolloverIcon.getHeight(null));
    }

    /**
     * Calculates the sms button rectangle.
     *
     * @return the sms button rectangle
     */
    protected Rectangle getSMSButtonRect()
    {
        Component c = getComponent();
        Rectangle rect = c.getBounds();

        int dx = getDeleteButtonRect().x - smsRolloverIcon.getWidth(null) - 8;
        if(isCallIconVisible)
            dx -= callRolloverIcon.getWidth(null) + 4;

        int dy = (rect.y + rect.height) / 2 - smsRolloverIcon.getHeight(null)/2;

        return new Rectangle(   dx,
                                dy,
                                smsRolloverIcon.getWidth(null),
                                smsRolloverIcon.getHeight(null));
    }

    /**
     * If we are in the case of disabled delete button, we simply call the
     * parent implementation of this method, otherwise we recalculate the editor
     * rectangle in order to leave place for the delete button.
     * @return the visible editor rectangle
     */
    @Override
    protected Rectangle getVisibleEditorRect()
    {
        Rectangle rect = super.getVisibleEditorRect();

        // Fixes NullPointerException if the rectangle is null for some reason.
        if (rect == null)
            return null;

        if ((rect.width > 0) && (rect.height > 0))
        {
            rect.x += searchIcon.getIconWidth() + 5;
            rect.width -= (searchIcon.getIconWidth() + 5);

            if (isSMSIconVisible)
                rect.width -= (smsRolloverIcon.getWidth(null) + 12);

            if (isCallIconVisible)
                rect.width -= (callRolloverIcon.getWidth(null) + 12);
            else
                rect.width -= 8;

            return rect;
        }
        return null;
    }

    /**
     * Reloads UI icons.
     */
    @Override
    public void loadSkin()
    {
        super.loadSkin();

        ResourceManagementService r = DesktopUtilActivator.getResources();

        searchIcon = r.getImage("service.gui.icons.SEARCH_ICON");
        if (isCallButtonEnabled)
        {
            callIcon
                = r.getImage("service.gui.buttons.SEARCH_CALL_ICON").getImage();
            callRolloverIcon
                = r.getImage("service.gui.buttons.SEARCH_CALL_ROLLOVER_ICON")
                    .getImage();
            callPressedIcon
                = r.getImage("service.gui.buttons.SEARCH_CALL_PRESSED_ICON")
                    .getImage();
        }

        if (isSMSButtonEnabled)
        {
            loadSMSIcons();
        }

        if(isSMSButtonEnabled || isCallButtonEnabled)
            separatorIcon
                = r.getImage("service.gui.icons.SEARCH_SEPARATOR").getImage();
    }

    /**
     * Loads sms icons.
     */
    private void loadSMSIcons()
    {
        ResourceManagementService r = DesktopUtilActivator.getResources();

        smsIcon
            = r.getImage("service.gui.buttons.SEARCH_SMS_ICON").getImage();
        smsRolloverIcon
            = r.getImage("service.gui.buttons.SEARCH_SMS_ROLLOVER_ICON")
                .getImage();
        smsPressedIcon
            = r.getImage("service.gui.buttons.SEARCH_SMS_PRESSED_ICON")
                .getImage();
    }

    /**
     * Updates the call button when the mouse was clicked.
     * @param e the <tt>MouseEvent</tt> that notified us of the click
     */
    @Override
    public void mouseClicked(MouseEvent e)
    {
        super.mouseClicked(e);

        if(isCallButtonEnabled || isSMSButtonEnabled)
            updateIcon(e);
    }

    /**
     * Updates the delete icon when the mouse is dragged over.
     * @param e the <tt>MouseEvent</tt> that notified us
     */
    @Override
    public void mouseDragged(MouseEvent e)
    {
        super.mouseDragged(e);

        if(isCallButtonEnabled || isSMSButtonEnabled)
            updateIcon(e);
    }

    /**
     * Updates the call button when the mouse is enters the component area.
     * @param e the <tt>MouseEvent</tt> that notified us
     */
    @Override
    public void mouseEntered(MouseEvent e)
    {
        super.mouseEntered(e);

        if(isCallButtonEnabled || isSMSButtonEnabled)
            updateIcon(e);
    }

    /**
     * Updates the call button when the mouse exits the component area.
     * @param e the <tt>MouseEvent</tt> that notified us
     */
    @Override
    public void mouseExited(MouseEvent e)
    {
        super.mouseExited(e);

        if(isCallButtonEnabled || isSMSButtonEnabled)
            updateIcon(e);
    }

    /**
     * Updates the delete icon when the mouse is moved over.
     * @param e the <tt>MouseEvent</tt> that notified us
     */
    @Override
    public void mouseMoved(MouseEvent e)
    {
        super.mouseMoved(e);

        if(isCallButtonEnabled || isSMSButtonEnabled)
            updateIcon(e);
    }

    @Override
    public void mousePressed(MouseEvent e)
    {
        super.mousePressed(e);

        if(isCallButtonEnabled || isSMSButtonEnabled)
            updateIcon(e);
    }

    @Override
    public void mouseReleased(MouseEvent e)
    {
        super.mouseReleased(e);

        if(isCallButtonEnabled || isSMSButtonEnabled)
            updateIcon(e);
    }

    /**
     * Implements parent paintSafely method and enables antialiasing.
     * @param g the <tt>Graphics</tt> object that notified us
     */
    @Override
    protected void paintSafely(Graphics g)
    {
        customPaintBackground(g);
        super.paintSafely(g);
    }

    /**
     * Enables/disabled the call button in the search field.
     *
     * @param isEnabled indicates if the call button is enabled
     */
    public void setCallButtonEnabled(boolean isEnabled)
    {
        JTextComponent c = this.getComponent();
        if(isCallButtonEnabled != isEnabled)
        {
            this.isCallButtonEnabled = isEnabled;
            loadSkin();
            if(c != null && c.isVisible())
                c.repaint();
        }
    }

    /**
     * Enables/disabled the sms button in the search field.
     *
     * @param isEnabled indicates if the sms button is enabled
     */
    public void setSMSButtonEnabled(boolean isEnabled)
    {
        loadSMSIcons();

        this.isSMSButtonEnabled = isEnabled;
    }

    /**
     * Updates the delete icon, changes the cursor and deletes the content of
     * the associated text component when the mouse is pressed over the delete
     * icon.
     *
     * @param evt the mouse event that has prompted us to update the delete
     * icon.
     */
    protected void updateIcon(MouseEvent evt)
    {
        int x = evt.getX();
        int y = evt.getY();

        boolean outsideButtons = true;

        if (isCallIconVisible && getCallButtonRect().contains(x, y))
        {
            JTextComponent c = getComponent();
            String searchText = c.getText();

            if (searchText == null)
                return;

            outsideButtons = false;

            // Show a tool tip over the call button.
            getComponent().setToolTipText(callString + " " + searchText);
            ToolTipManager.sharedInstance().mouseEntered(
                new MouseEvent(c, 0, x, y,
                        x, y, // X-Y of the mouse for the tool tip
                        0, false));

            if (evt.getID() == MouseEvent.MOUSE_PRESSED)
            {
                isCallMouseOver = false;
                isCallMousePressed = true;
            }
            else
            {
                isCallMouseOver = true;
                isCallMousePressed = false;
            }

            // Update the default cursor.
            getComponent().setCursor(Cursor.getDefaultCursor());
        }

        if (isSMSIconVisible && getSMSButtonRect().contains(x, y))
        {
            JTextComponent c = getComponent();
            String searchText = c.getText();

            if (searchText == null)
                return;

            outsideButtons = false;

            // Show a tool tip over the call button.
            getComponent().setToolTipText(smsString + " " + searchText);
            ToolTipManager.sharedInstance().mouseEntered(
                new MouseEvent(c, 0, x, y,
                        x, y, // X-Y of the mouse for the tool tip
                        0, false));

            if (evt.getID() == MouseEvent.MOUSE_PRESSED)
            {
                isSMSMouseOver = false;
                isSMSMousePressed = true;
            }
            else
            {
                isSMSMouseOver = true;
                isSMSMousePressed = false;
            }

            // Update the default cursor.
            getComponent().setCursor(Cursor.getDefaultCursor());
        }

        if(outsideButtons)
        {
            // Remove the call button tool tip when the mouse exits the call
            // button area.
            getComponent().setToolTipText("");
            ToolTipManager.sharedInstance().mouseExited(
                new MouseEvent(getComponent(), 0, x, y,
                        x, y, // X-Y of the mouse for the tool tip
                        0, false));

            isCallMouseOver = false;
            isCallMousePressed = false;

            isSMSMouseOver = false;
            isSMSMousePressed = false;
        }

        getComponent().repaint();
    }
}
