package caltool.view_ui;

import caltool.schedule.*;
import caltool.view.*;
import caltool.caltool_ui.*;
import mvp.*;
import java.util.*;
import java.awt.*;
import javax.swing.*;

/****
 *
 * Class MonthlyAgendaDisplay is the companion view of a MonthlyAgenda model.
 * The fixed layout of the display is a three-part vertical box.  The box
 * contains a date banner, a row of header labels for the days of the week, and
 * the seven-column grid for the days of the month.  The size and layout of the
 * days grid is computed dynamically by the update method, based on the data
 * from the model.
 *
 * @author Gene Fisher (gfisher@calpoly.edu)
 * @version 6feb04
 *
 */
public class MonthlyAgendaDisplay extends CalendarToolWindow {

    /**
     * Construct this by constructing subpanels for the three parts of the
     * display.  Also construct an array of 31 day displays.  This array
     * provides direct access to the individual day displays by date number,
     * which is handy for referencing the companion day models directly.
     *
     * Compute the default size of the days grid to be 5 rows by 7 columns of a
     * default-size day display.  If there are 4 or 6 rows, then the default
     * rows are taller or shorter than they are wide.  This formating is per
     * the requirements.
     *
     * Initialize the displayedOnce flag to false.  The display is only set to
     * the default size the first time it is displayed.  After that, the
     * display retains its size, including any resizing done by the user.
     */
    public MonthlyAgendaDisplay(Screen s, MonthlyAgenda monthlyAgenda,
            CalendarToolUI calToolUI) {
        super(s, monthlyAgenda, calToolUI);
        days = new SmallDayViewDisplay[31];
        dateBanner = new JPanel(new GridLayout(1, 1));
        daysOfWeek = new JPanel(new GridLayout(1, 7));
        dayGrid = new JPanel(new GridLayout(0, 7));
        dayGrid.setBackground(Color.white);
        defaultSize = new Dimension(
            7 * defaultCellWidth, 5 * defaultCellHeight);
        displayedOnce = false;
    }

    /**
     * Compose this as a vertical box, consisting of a date-banner row, a
     * days-of-the-week labels row, and an empty days grid.  The grid will be
     * populated by update.
     */
    public Component compose() {

        /*
         * Make a new window for this.
         */
        window = new mvp.Window();

        /*
         * Make an outer box.
         */
        vbox = new JPanel();
        vbox.setLayout(new BoxLayout(vbox, BoxLayout.Y_AXIS));
        vbox.setBorder(BorderFactory.createLineBorder(Color.black));
        
        /*
         * Compose the top two rows.
         */
        JPanel bannerBox = composeDateBanner();
        JPanel labelBox = composeDaysOfWeek();

        /*
         * Add the date banner and days-of-week labels to the outer vbox.
         */
        vbox.add(bannerBox);
        vbox.add(labelBox);

        /*
         * Add the empty day grid to the vbox.  It will be populated by update.
         */
        vbox.add(dayGrid);

        /*
         * Add the vbox to the window and we're outta here.
         */
        window.add(vbox);
        window.setTitle("Monthly Agenda");
        return window;
    }

    /**
     * Compose the date banner.  For now it's a dummy label.  In the full
     * implementation, it will contain prev,next,today buttons and the
     * current month/year.
     */
    protected JPanel composeDateBanner() {
        JLabel bannerLabel = new JLabel(((MonthlyAgenda)model).
            getFullMonthName());
        JPanel bannerBox = new JPanel();

        bannerBox.setLayout(new BoxLayout(bannerBox, BoxLayout.Y_AXIS));
        bannerLabel.setForeground(Color.black);
        bannerLabel.setFont(bannerLabel.getFont().deriveFont(Font.BOLD));
        bannerLabel.setHorizontalAlignment(SwingConstants.CENTER);
        dateBanner.add(bannerLabel);
//      dateBanner.setOpaque(false);
        dateBanner.setMaximumSize(new Dimension(
            2000, 2 * bannerLabel.getFont().getSize()));

        bannerBox.add(Box.createVerticalStrut(4));
        bannerBox.add(dateBanner);
        bannerBox.add(Box.createVerticalStrut(4));
        bannerBox.setBorder(BorderFactory.createLineBorder(Color.black));
//      bannerBox.setBackground(Color.white);

        return bannerBox;
    }

    /*
     * Compose the days-of-the-week labeling row.  It's a 1x7 grid of labels,
     * so they'll align properly with the columns of the day grid
     */
    JPanel composeDaysOfWeek() {
        JLabel dayLabel = new JLabel("");
        JPanel labelBox = new JPanel();

        labelBox.setLayout(new BoxLayout(labelBox, BoxLayout.Y_AXIS));
        for (int dayNumber = 0; dayNumber < 7; dayNumber++) {
            dayLabel = new JLabel(
                DayName.values()[dayNumber].toString().substring(0,3));
            dayLabel.setHorizontalAlignment(SwingConstants.CENTER);
            dayLabel.setForeground(Color.black);
            dayLabel.setFont(dayLabel.getFont().deriveFont(Font.BOLD));
            daysOfWeek.add(dayLabel);
        }
//      daysOfWeek.setOpaque(false);
        daysOfWeek.setMaximumSize(new Dimension(
            2000, 2 * dayLabel.getFont().getSize()));

        labelBox.add(Box.createVerticalStrut(4));
        labelBox.add(daysOfWeek);
        labelBox.add(Box.createVerticalStrut(4));
        labelBox.setBorder(BorderFactory.createLineBorder(Color.black));
//      labelBox.setBackground(Color.white);

        return labelBox;
    }

    /**
     * Display the model data in the appropriate daily positions.  The data are
     * produced by firstDay and nextDay iterator methods.  The display is a
     * 7-column grid, with 4, 5, or 6 rows, depending on the configuration of
     * the month.
     *
     * In the current implementation, the display is fully redrawn at each call
     * to update, with no display efficiencies implemented.  Possible display
     * efficiencies that might be implemented include the following. (1) If the
     * model data have not changed at all, no updating is performed.  This is
     * the presumably rare case where the user has executed a 'Goto Date'
     * command for the current month.  (2) If the number of weeks in the new
     * model month is the same as the current model month, the row boxes are
     * not reallocated.
     */
    public void update(Observable o, Object arg) {
        int row = 0;            // Week row number
        int dayPosition;        // Ordinal position 0-41 of the current day
        int i;                  // Loop index
        SmallDayViewDisplay     // Loop var for each day's display
            dayViewDisplay;
        int numberOfWeeks =     // Number of weeks === number or rows
            ((MonthlyAgenda)model).getNumberOfWeeks();
        Dimension curSize =     // Current x/y size of grid
            vbox.getSize();
        Dimension cellDimension // Size of one day cell
            = new Dimension(
                (int) (curSize.getWidth() / 7),
                    (int) (curSize.getHeight() / numberOfWeeks));

        /*
         * Clear everything out and set the number of rows to the number of
         * weeks in the model month.
         */
        Arrays.fill(days, null);
        dayGrid.removeAll();
        GridLayout layout = (GridLayout) dayGrid.getLayout();
        layout.setRows(numberOfWeeks);

        /*
         * Put empty grey boxes up to the first day position.
         */
        SmallDayView dayView = ((MonthlyAgenda)model).getFirstDay();
        dayPosition = dayView.getDay().ordinal();
        for (i = 0; i < dayPosition; i++)
            dayGrid.add(greyDay());

        /*
         * Populate the individual day displays with model data.
         */
        for (; dayView != null;
                dayView = ((MonthlyAgenda)model).getNextDay(), dayPosition++) {
            dayViewDisplay = new SmallDayViewDisplay(
                screen, dayView, (MonthlyAgenda)model, cellDimension);
            days[dayView.getDate()] = dayViewDisplay;
            dayGrid.add(dayViewDisplay.getWidget());
        }

        /*
         * Put empty grey boxes up to the last day position in the last row.
         */
        for (i = dayPosition; i % 7 != 0; i++) {
            dayGrid.add(greyDay());
        }

        /*
         * Set grid to the default size if this is the first time it's being
         * displayed.  Otherwise, leave its size as it was.  In either case,
         * pack the grid in order to "burn in" the layout.
         */
        window.getContentPane().setBackground(Color.blue);
        if (! displayedOnce) {
            dayGrid.setPreferredSize(defaultSize);
            displayedOnce = true;
            window.pack();
        }
    }

    /**
     * Build an empty grey-background, black-border day display.  A fresh one
     * of these needs to be allocated for each use since JFC doesn't play
     * reuses of a components in containers.
     */
    protected JPanel greyDay() {
        JPanel panel = new JPanel();
        panel.setBackground(Color.lightGray);
        panel.setBorder(BorderFactory.createLineBorder(Color.black));

        return panel;
    }

    /** Array of day displays for convenient access by date number.  This array
        contains references to the same day-display objects that are laid out
        in the day grid. */
    protected SmallDayViewDisplay days[];

    /** Outermost box of the laid-out display. */
    protected JPanel vbox;

    /** The date banner at the top of the display. */
    protected JPanel dateBanner;

    /** The days-of-the week labeling row. */
    protected JPanel daysOfWeek;

    /** The day grid. */
    protected JPanel dayGrid;

    /** Number or weeks (hence display rows) in the current display. */
    protected int numberOfWeeks;

    /** Flag that's true after the display has bee shown the first time. */
    boolean displayedOnce;

    /** Initial default size of the day grid. */
    protected Dimension defaultSize;

    /** Default constant for the height of one day display cell. */
    protected final int defaultCellHeight = 75;

    /** Default constant for the width of one day display cell. */
    protected final int defaultCellWidth = 75;

}