package cnp.ew.calendar;

import java.awt.*;
import java.util.*;
import cnp.ew.lightweight.*;
import cnp.ew.util.*;
import cnp.ew.converter.*;
import cnp.ew.displayer.*;
import cnp.ew.button.*;
import cnp.ew.layout.*;

public class CpCalendarLc extends CpAbstractLc
{
    static int margin = 2;
    static String [] dayNames = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" };

    static int leftArrowId;
    static int rightArrowId;

    CpLabeledImageButtonLc leftButton, rightButton;

    static {
        leftArrowId = CpToolkit.registerImageName("larrow7.gif");
        rightArrowId = CpToolkit.registerImageName("rarrow7.gif");
    }

    CpDateModel dateModel;
    CpDate savedDate;
    int previousDayOfTheMonth;
    int todayDay, todayMonth, todayYear;
    CpToStringConverter converter;
    Cp3DBorderDisplayer selectedDayBorderDisplayer;
    Font boldFont = new Font("Helvetica", Font.BOLD, 12);
    Font font = new Font("Helvetica", Font.PLAIN, 12);
    FontMetrics fm, bfm;

    public CpCalendarLc()
    {
        this(new CpDate());
    }

    public CpCalendarLc(Date date)
    {
        this(new CpDate(date));
    }

    public CpCalendarLc(CpDate date)
    {
        this(new CpDateModel(date));
    }

    public CpCalendarLc(CpDateModel model)
    {
        super();
        dateModel = model;
        previousDayOfTheMonth = getCurrentDate().getDate();
        dateModel.addObserver(this);
        converter = new CpDateToStringConverter("mmmm - yyyy");
        selectedDayBorderDisplayer = new Cp3DBorderDisplayer(false, true);
        addButtons();
        setBorderStyle(BORDER_OUTWARD3D);
        setBorderMargin(2);
        Date today = new Date();
        todayDay = today.getDate();
        todayMonth = today.getMonth();
        todayYear = today.getYear();
    }

    public void setDate(Date date)
    {
        setDate(new CpDate(date));
    }

    public void setDate(CpDate date)
    {
        dateModel.setDate(date);
    }

    void addButtons()
    {
        CpAttachmentsLayout layout;
        CpAttachments attachments;

        setLayout(layout = new CpAttachmentsLayout());

        leftButton = new CpLabeledImageButtonLc();
        leftButton.setImage(
            CpToolkit.getImage(leftArrowId),
            CpLabeledImageButtonLc.IMAGE_OUT
        );
        attachments = new CpAttachments();
        attachments.setLeftAttachment(CpAttachments.ATTACH_PARENT, 1);
        attachments.setTopAttachment(CpAttachments.ATTACH_PARENT, 1);
        layout.setAttachments(leftButton, attachments);
        add(leftButton);
        leftButton.addObserver(this);

        rightButton = new CpLabeledImageButtonLc();
        rightButton.setImage(
            CpToolkit.getImage(rightArrowId),
            CpLabeledImageButtonLc.IMAGE_OUT
        );
        attachments = new CpAttachments();
        attachments.setRightAttachment(CpAttachments.ATTACH_PARENT, 1);
        attachments.setTopAttachment(CpAttachments.ATTACH_PARENT, 1);
        layout.setAttachments(rightButton, attachments);
        add(rightButton);
        rightButton.addObserver(this);
    }


    public CpDate getCurrentDate()
    {
        return dateModel.getDate();
    }

    public Object getObject()
    {
        return dateModel.getDate();
    }

    public void setObject(Object aDate)
    {
        setDate((Date)aDate);
    }

    Dimension getCellSize()
    {
        initializeFontMetrics();
        return new Dimension(bfm.stringWidth("We") + 2 * margin, fm.getHeight() + 2 * margin);
    }

    void initializeFontMetrics()
    {
        if (fm == null) {
            fm = getFontMetrics(font);
            bfm = getFontMetrics(boldFont);
        }
    }

    int getHeaderHeight()
    {
        // GETHEIGHT SHOULD MAX AGAINST THE HEIGHT OF THE BUTTONS
        return bfm.getHeight() * 2 + 3 * margin;
    }

    public void paint(Graphics g, Rectangle rect)
    {
        int startX;

        CpDate currentDate = getCurrentDate();
        String text;
        Rectangle clientRect = getClientRect();
        Dimension cellSize = getCellSize();

        initializeFontMetrics();
        g.setFont(boldFont);
        g.setColor(Color.lightGray);
        g.fillRect(0,0, size().width, size().height);



        int startY = clientRect.y + bfm.getMaxAscent();
        if (rect.y < startY) {
            text = converter.convert(currentDate);
            startX = clientRect.x + ((clientRect.width - bfm.stringWidth(text)) / 2);
            g.setColor(Color.black);
            g.drawString(text, startX, startY);
        }

        startY += 2 * margin + bfm.getMaxAscent();

        if (rect.y < startY) {
            for (int i = 0; i < dayNames.length; i++) {
                text = dayNames[i];
                startX = clientRect.x + cellSize.width * i + ((cellSize.width - bfm.stringWidth(text)) / 2);
                g.drawString(text, startX, startY);
            }
        }

        startY = clientRect.y + getHeaderHeight() - margin;
        if (rect.y <= startY) {
            g.setColor(Color.gray);
            g.drawLine(2, startY, clientRect.x + clientRect.width - margin, startY);
        }


        int dayOfWeekIndex = currentDate.dayOfWeekIndexForStartOfMonth();
        startY += margin + fm.getMaxAscent();
        g.setFont(font);

        boolean sameMonthAndYearAsToday = currentDate.getMonth() == todayMonth && currentDate.getYear() == todayYear;
        for (int i = 1; i <= currentDate.daysInCurrentMonth(); i++) {

            startX = clientRect.x + cellSize.width * dayOfWeekIndex;

            if (rect.x <= startX && rect.x + rect.width >= startX && rect.y <= startY && rect.y + rect.height >= startY) {

                text = String.valueOf(i);

                if (i == currentDate.getDate()) {
                    selectedDayBorderDisplayer.paintIn(
                        this,
                        g,
                        startX,
                        startY - fm.getMaxAscent(),
                        cellSize.width,
                        cellSize.height);
                }

                g.setColor(Color.black);

                if (sameMonthAndYearAsToday && todayDay == i) {
                    g.setFont(boldFont);
                    g.setColor(Color.blue);
                }
                startX += (cellSize.width - fm.stringWidth(text)) / 2;

                g.drawString(text, startX, startY + (cellSize.height - fm.getHeight()) / 2);

                if (sameMonthAndYearAsToday && todayDay == i) {
                    g.setFont(font);
                    g.setColor(Color.black);
                }
            }

            dayOfWeekIndex++;
            if (dayOfWeekIndex == 7) {
                dayOfWeekIndex = 0;
                startY += cellSize.height;
            }
        }
    }

    public Dimension preferredSize()
    {
        Dimension cellSize = getCellSize();
        return borderedPreferredSize(new Dimension(cellSize.width * 7, getHeaderHeight() + cellSize.height * 5));
    }

    public boolean mouseDown(Event e, int x, int y)
    {
        savedDate = new CpDate(getCurrentDate());
        moveDateTo(x, y);
        return true;
    }

    public boolean mouseDrag(Event e, int x, int y)
    {
        moveDateTo(x, y);
        return true;
    }

    public boolean mouseUp(Event e, int x, int y)
    {
        CpDate newDate = getCurrentDate();
        if (!savedDate.equals(newDate)) {
            notifyObservers(OBJECT_CHANGED, newDate);
        }
        return false;
    }


    void moveDateTo(int x, int y)
    {
        Rectangle clientRect = getClientRect();
        int baseX = x - clientRect.x;
        int baseY = y - getHeaderHeight() - clientRect.y;
        if (baseY < 0 || baseX < 0 || baseY > size().height || baseX > size().width) {
            return;
        }


        Dimension cellSize = getCellSize();
        int row = baseY / cellSize.height;
        int col = baseX / cellSize.width;
        int day = row * 7 + col + 1 - getCurrentDate().dayOfWeekIndexForStartOfMonth();

        if (day < 1 || day > getCurrentDate().daysInCurrentMonth()) {
            return;
        }
        dateModel.setDay(day);
    }

    Rectangle cellRectForDay(int day)
    {
        int offset = day + getCurrentDate().dayOfWeekIndexForStartOfMonth() - 1;
        int row = offset / 7;
        int col = offset % 7;
        Dimension cellSize = getCellSize();
        Rectangle clientRect = getClientRect();
        return new Rectangle(
            clientRect.x + col * cellSize.width,
            clientRect.y + getHeaderHeight() + row * cellSize.height,
            cellSize.width,
            cellSize.height
        );
    }

    public void update(CpObservable o, int facet, Object arg)
    {
        if (o == dateModel) {
            switch (facet) {
            case CpDateModel.CHANGED_MONTH:
            case CpDateModel.CHANGED_YEAR:
                repaint();
                break;
            case CpDateModel.CHANGED_DAY:
                if (previousDayOfTheMonth >= 0) {
                    damage(cellRectForDay(previousDayOfTheMonth));
                    repairDamage();
                }
                previousDayOfTheMonth = getCurrentDate().getDate();
                damage(cellRectForDay(previousDayOfTheMonth));
                repairDamage();
                break;
            }
            notifyObservers(OBJECT_CHANGING, getObject());
        }

        if (o == leftButton && facet == BUTTON_PRESSING) {
            dateModel.decrementMonth();
        }

        if (o == rightButton && facet == BUTTON_PRESSING) {
            dateModel.incrementMonth();
        }
    }
}