package cnp.ew.button;

import cnp.ew.util.*;
import cnp.ew.displayer.*;
import java.awt.*;
import cnp.ew.lightweight.*;

abstract public class CpButtonLc extends CpAbstractLc
    implements CpTimerUser
{
    CpTimedIntervalPolicy buttonPressIntervalPolicy = new CpSteppedTimedIntervalPolicy(10, -2, 400, 1);

    boolean hasBorder;
    boolean buttonIsDepressed;
    boolean mouseIsDownAndInsideRect;
    boolean shouldNotifyOfClick;
    boolean depressedByKey;
    boolean isDefaultButton;
    boolean isCancelButton;
    boolean usesFocus;

    public CpButtonLc()
    {
        hasBorder = true;
        buttonIsDepressed = false;
        mouseIsDownAndInsideRect = false;
        shouldNotifyOfClick = false;
        depressedByKey = false;
        isDefaultButton = false;
        isCancelButton = false;
        usesFocus = true;
    }

    //  Don't allow the framework to find our children. TBD: THis should probably be in the framwork somehow...
    public CpLightweightComponent locateGlobal(int x, int y)
    {
        return insideGlobal(x, y) ? this : null;
    }

    public void setUsesFocus(boolean usesFocus)
    {
        this.usesFocus = usesFocus;
    }

    public void setHasBorder(boolean newHasBorder)
    {
        hasBorder = newHasBorder;
        if (hasBorder) {
            CpButtonBorderDisplayer borderDisplayer = new CpButtonBorderDisplayer();
            borderDisplayer.setIsDefault(isDefaultButton);
            setBorderDisplayer(borderDisplayer);
        } else {
            setBorderStyle(BORDER_NONE);
        }
        repaint();
    }

    void pressButton()
    {
        if (isDisabled()) { return; }
        redisplayButton();
        notifyButtonPressed();
        notifyButtonPressing();
        startButtonPressTimer();
    }

    void releaseButton()
    {
        stopButtonPressTimer();

        // for our framework; other buttons might
        // want to do something before displaying or notifying.
        // (e.g. changing the state in a state button)
        if (shouldNotifyOfClick) {
            buttonClicked();
        }

        redisplayButton();
        notifyButtonReleased();
    }


    public boolean keyDown(Event e, int key)
    {
        if (key == 32) { // Space
            depressedByKey = true;
            pressButton();
            return true;
        }
        return false;
    }

    public boolean keyUp(Event e, int key)
    {
        if (key == 32) {
            shouldNotifyOfClick = true;
            depressedByKey = false;
            releaseButton();
        }
        return false;
    }

    public boolean mouseDown(Event e, int x, int y)
    {
        if (isDisabled()) { return true; }
        mouseIsDownAndInsideRect = true;
        pressButton();
        return true;
    }

    public boolean mouseUp(Event e, int x, int y)
    {
        shouldNotifyOfClick = mouseIsDownAndInsideRect;
        mouseIsDownAndInsideRect = false;
        releaseButton();
        return true;
    }

    public boolean mouseDrag(Event e, int x, int y)
    {
        if (isDisabled()) { return true; }
        boolean mouseIsInsideRect = mouseInside();
        if (mouseIsDownAndInsideRect != mouseIsInsideRect) {
            mouseIsDownAndInsideRect = mouseIsInsideRect;
            redisplayButton();
        }
        return true;
    }

    public void setButtonPressIntervalPolicy(CpTimedIntervalPolicy newIntervalPolicy)
    {
        buttonPressIntervalPolicy = newIntervalPolicy;
    }

    public CpTimedIntervalPolicy getButtonPressIntervalPolicy()
    {
        return buttonPressIntervalPolicy;
    }

    public void setIsDisabled(boolean newIsDisabled)
    {
        if (newIsDisabled != getIsDisabled()) {
            super.setIsDisabled(newIsDisabled);
            redisplayButton();
        }
    }

    // Unify this
    public boolean getIsDisabled()
    {
        return isDisabled();
    }

    public void notifyButtonPressed()
    {
        notifyObservers(BUTTON_PRESS);
    }

    public void notifyButtonReleased()
    {
        notifyObservers(BUTTON_RELEASE);
        if (shouldNotifyOfClick) {
            notifyObservers(BUTTON_CLICKED);
        }
    }

    public void notifyButtonPressing()
    {
        notifyObservers(BUTTON_PRESSING);
    }


    void startButtonPressTimer()
    {
        buttonPressIntervalPolicy.initialize();
        CpTimer.getDefaultTimer().register(this, buttonPressIntervalPolicy.getNextInterval());
    }

    void stopButtonPressTimer()
    {
        CpTimer.getDefaultTimer().unregister(this);
    }

    public long timerEvent(int eventType)
    {
        if (visiblyPressedIn()) {
            notifyButtonPressing();
        }
        return buttonPressIntervalPolicy.getNextInterval();
    }


    public void redisplayButton()
    {
        enableDamageRepair(false);
        reconfigureBorder();
        reconfigure();
        enableDamageRepair(true);
        repaint();
    }

    void reconfigureBorder()
    {
        if (hasBorder) {
            ((CpButtonBorderDisplayer)borderDisplayer).setRaised(shouldDisplayBorderRaised());
        }
    }

    /**
     * No-op for many buttons.
     */
    void buttonClicked()
    {
    }

    boolean mouseInside()
    {
        if (getComponent() == null) {
            return false;
        }

        Point mouseLoc = mouseLocation();
        Rectangle rect = new Rectangle(0, 0, size().width, size().height);
        return rect.inside(mouseLoc.x, mouseLoc.y);
    }

    boolean visiblyPressedIn()
    {
        return mouseIsDownAndInsideRect || depressedByKey;
    }

    boolean shouldDisplayBorderRaised()
    {
        return !visiblyPressedIn();
    }

    public void setIsDefaultButton(boolean isDefaultButton)
    {
        this.isDefaultButton = isDefaultButton;
        if (hasBorder) {
            ((CpButtonBorderDisplayer)getBorderDisplayer()).setIsDefault(isDefaultButton);
        }
    }

    public void setIsCancelButton(boolean isCancelButton)
    {
        this.isCancelButton = isCancelButton;
    }

    public boolean usesFocus()
    {
        return usesFocus;
    }

    public boolean pushDefaultButton()
    {
        if (isDefaultButton) {
            pressAndReleaseButton();
            return true;
        }
        return false;
    }

    public boolean pushCancelButton()
    {
        if (isCancelButton) {
            pressAndReleaseButton();
            return true;
        }
        return false;
    }

    void pressAndReleaseButton()
    {
        depressedByKey = true;
        pressButton();

        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {}

        shouldNotifyOfClick = true;
        depressedByKey = false;
        releaseButton();
    }

    abstract void reconfigure();

}

