/*
 * Copyright 2020 Red Hat, Inc. and/or its affiliates.
 *
 * 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 org.optaplanner.examples.curriculumcourse.swingui;

import static org.optaplanner.examples.common.swingui.timetable.TimeTablePanel.HeaderColumnKey.HEADER_COLUMN;
import static org.optaplanner.examples.common.swingui.timetable.TimeTablePanel.HeaderColumnKey.HEADER_COLUMN_GROUP1;
import static org.optaplanner.examples.common.swingui.timetable.TimeTablePanel.HeaderRowKey.HEADER_ROW;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingConstants;

import org.optaplanner.examples.common.swingui.CommonIcons;
import org.optaplanner.examples.common.swingui.SolutionPanel;
import org.optaplanner.examples.common.swingui.components.LabeledComboBoxRenderer;
import org.optaplanner.examples.common.swingui.timetable.TimeTablePanel;
import org.optaplanner.examples.curriculumcourse.domain.CourseSchedule;
import org.optaplanner.examples.curriculumcourse.domain.Curriculum;
import org.optaplanner.examples.curriculumcourse.domain.Day;
import org.optaplanner.examples.curriculumcourse.domain.Lecture;
import org.optaplanner.examples.curriculumcourse.domain.Period;
import org.optaplanner.examples.curriculumcourse.domain.Room;
import org.optaplanner.examples.curriculumcourse.domain.Teacher;
import org.optaplanner.swing.impl.SwingUtils;
import org.optaplanner.swing.impl.TangoColorFactory;

public class CurriculumCoursePanel extends SolutionPanel<CourseSchedule> {

    public static final String LOGO_PATH = "/org/optaplanner/examples/curriculumcourse/swingui/curriculumCourseLogo.png";

    private final TimeTablePanel<Room, Period> roomsPanel;
    private final TimeTablePanel<Teacher, Period> teachersPanel;
    private final TimeTablePanel<Curriculum, Period> curriculaPanel;

    public CurriculumCoursePanel() {
        setLayout(new BorderLayout());
        JTabbedPane tabbedPane = new JTabbedPane();
        roomsPanel = new TimeTablePanel<>();
        tabbedPane.add("Rooms", new JScrollPane(roomsPanel));
        teachersPanel = new TimeTablePanel<>();
        tabbedPane.add("Teachers", new JScrollPane(teachersPanel));
        curriculaPanel = new TimeTablePanel<>();
        tabbedPane.add("Curricula", new JScrollPane(curriculaPanel));
        add(tabbedPane, BorderLayout.CENTER);
        setPreferredSize(PREFERRED_SCROLLABLE_VIEWPORT_SIZE);
    }

    @Override
    public boolean isWrapInScrollPane() {
        return false;
    }

    @Override
    public void resetPanel(CourseSchedule courseSchedule) {
        roomsPanel.reset();
        teachersPanel.reset();
        curriculaPanel.reset();
        defineGrid(courseSchedule);
        fillCells(courseSchedule);
        repaint(); // Hack to force a repaint of TimeTableLayout during "refresh screen while solving"
    }

    private void defineGrid(CourseSchedule courseSchedule) {
        JButton footprint = SwingUtils.makeSmallButton(new JButton("LinLetGre1-0"));
        int footprintWidth = footprint.getPreferredSize().width;

        roomsPanel.defineColumnHeaderByKey(HEADER_COLUMN_GROUP1); // Day header
        roomsPanel.defineColumnHeaderByKey(HEADER_COLUMN); // Period header
        for (Room room : courseSchedule.getRoomList()) {
            roomsPanel.defineColumnHeader(room, footprintWidth);
        }
        roomsPanel.defineColumnHeader(null, footprintWidth); // Unassigned

        teachersPanel.defineColumnHeaderByKey(HEADER_COLUMN_GROUP1); // Day header
        teachersPanel.defineColumnHeaderByKey(HEADER_COLUMN); // Period header
        for (Teacher teacher : courseSchedule.getTeacherList()) {
            teachersPanel.defineColumnHeader(teacher, footprintWidth);
        }

        curriculaPanel.defineColumnHeaderByKey(HEADER_COLUMN_GROUP1); // Day header
        curriculaPanel.defineColumnHeaderByKey(HEADER_COLUMN); // Period header
        for (Curriculum curriculum : courseSchedule.getCurriculumList()) {
            curriculaPanel.defineColumnHeader(curriculum, footprintWidth);
        }

        roomsPanel.defineRowHeaderByKey(HEADER_ROW); // Room header
        teachersPanel.defineRowHeaderByKey(HEADER_ROW); // Teacher header
        curriculaPanel.defineRowHeaderByKey(HEADER_ROW); // Curriculum header
        for (Period period : courseSchedule.getPeriodList()) {
            roomsPanel.defineRowHeader(period);
            teachersPanel.defineRowHeader(period);
            curriculaPanel.defineRowHeader(period);
        }
        roomsPanel.defineRowHeader(null); // Unassigned period
        teachersPanel.defineRowHeader(null); // Unassigned period
        curriculaPanel.defineRowHeader(null); // Unassigned period
    }

    private void fillCells(CourseSchedule courseSchedule) {
        roomsPanel.addCornerHeader(HEADER_COLUMN_GROUP1, HEADER_ROW, createTableHeader(new JLabel("Day")));
        roomsPanel.addCornerHeader(HEADER_COLUMN, HEADER_ROW, createTableHeader(new JLabel("Time")));
        fillRoomCells(courseSchedule);
        teachersPanel.addCornerHeader(HEADER_COLUMN_GROUP1, HEADER_ROW, createTableHeader(new JLabel("Day")));
        teachersPanel.addCornerHeader(HEADER_COLUMN, HEADER_ROW, createTableHeader(new JLabel("Time")));
        fillTeacherCells(courseSchedule);
        curriculaPanel.addCornerHeader(HEADER_COLUMN_GROUP1, HEADER_ROW, createTableHeader(new JLabel("Day")));
        curriculaPanel.addCornerHeader(HEADER_COLUMN, HEADER_ROW, createTableHeader(new JLabel("Time")));
        fillCurriculumCells(courseSchedule);
        fillDayCells(courseSchedule);
        fillLectureCells(courseSchedule);
    }

    private void fillRoomCells(CourseSchedule courseSchedule) {
        for (Room room : courseSchedule.getRoomList()) {
            roomsPanel.addColumnHeader(room, HEADER_ROW,
                    createTableHeader(new JLabel(room.getLabel(), SwingConstants.CENTER)));
        }
        roomsPanel.addColumnHeader(null, HEADER_ROW,
                createTableHeader(new JLabel("Unassigned", SwingConstants.CENTER)));
    }

    private void fillTeacherCells(CourseSchedule courseSchedule) {
        for (Teacher teacher : courseSchedule.getTeacherList()) {
            teachersPanel.addColumnHeader(teacher, HEADER_ROW,
                    createTableHeader(new JLabel(teacher.getLabel(), SwingConstants.CENTER)));
        }
    }

    private void fillCurriculumCells(CourseSchedule courseSchedule) {
        for (Curriculum curriculum : courseSchedule.getCurriculumList()) {
            curriculaPanel.addColumnHeader(curriculum, HEADER_ROW,
                    createTableHeader(new JLabel(curriculum.getLabel(), SwingConstants.CENTER)));
        }
    }

    private void fillDayCells(CourseSchedule courseSchedule) {
        for (Day day : courseSchedule.getDayList()) {
            Period dayStartPeriod = day.getPeriodList().get(0);
            Period dayEndPeriod = day.getPeriodList().get(day.getPeriodList().size() - 1);
            roomsPanel.addRowHeader(HEADER_COLUMN_GROUP1, dayStartPeriod, HEADER_COLUMN_GROUP1, dayEndPeriod,
                    createTableHeader(new JLabel(day.getLabel())));
            teachersPanel.addRowHeader(HEADER_COLUMN_GROUP1, dayStartPeriod, HEADER_COLUMN_GROUP1, dayEndPeriod,
                    createTableHeader(new JLabel(day.getLabel())));
            curriculaPanel.addRowHeader(HEADER_COLUMN_GROUP1, dayStartPeriod, HEADER_COLUMN_GROUP1, dayEndPeriod,
                    createTableHeader(new JLabel(day.getLabel())));
            for (Period period : day.getPeriodList()) {
                roomsPanel.addRowHeader(HEADER_COLUMN, period,
                        createTableHeader(new JLabel(period.getTimeslot().getLabel())));
                teachersPanel.addRowHeader(HEADER_COLUMN, period,
                        createTableHeader(new JLabel(period.getTimeslot().getLabel())));
                curriculaPanel.addRowHeader(HEADER_COLUMN, period,
                        createTableHeader(new JLabel(period.getTimeslot().getLabel())));
            }
        }
        roomsPanel.addRowHeader(HEADER_COLUMN_GROUP1, null, HEADER_COLUMN, null,
                createTableHeader(new JLabel("Unassigned")));
        teachersPanel.addRowHeader(HEADER_COLUMN_GROUP1, null, HEADER_COLUMN, null,
                createTableHeader(new JLabel("Unassigned")));
        curriculaPanel.addRowHeader(HEADER_COLUMN_GROUP1, null, HEADER_COLUMN, null,
                createTableHeader(new JLabel("Unassigned")));
    }

    private void fillLectureCells(CourseSchedule courseSchedule) {
        preparePlanningEntityColors(courseSchedule.getLectureList());
        for (Lecture lecture : courseSchedule.getLectureList()) {
            Color color = determinePlanningEntityColor(lecture, lecture.getCourse());
            String toolTip = determinePlanningEntityTooltip(lecture);
            roomsPanel.addCell(lecture.getRoom(), lecture.getPeriod(),
                    createButton(lecture, color, toolTip));
            teachersPanel.addCell(lecture.getTeacher(), lecture.getPeriod(),
                    createButton(lecture, color, toolTip));
            for (Curriculum curriculum : lecture.getCurriculumSet()) {
                curriculaPanel.addCell(curriculum, lecture.getPeriod(),
                        createButton(lecture, color, toolTip));
            }
        }
    }

    private JPanel createTableHeader(JLabel label) {
        JPanel headerPanel = new JPanel(new BorderLayout());
        headerPanel.add(label, BorderLayout.NORTH);
        headerPanel.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(TangoColorFactory.ALUMINIUM_5),
                BorderFactory.createEmptyBorder(2, 2, 2, 2)));
        return headerPanel;
    }

    private JButton createButton(Lecture lecture, Color color, String toolTip) {
        JButton button = SwingUtils.makeSmallButton(new JButton(new LectureAction(lecture)));
        button.setBackground(color);
        if (lecture.isPinned()) {
            button.setIcon(CommonIcons.PINNED_ICON);
        }
        button.setToolTipText(toolTip);
        return button;
    }

    @Override
    public boolean isIndictmentHeatMapEnabled() {
        return true;
    }

    private class LectureAction extends AbstractAction {

        private Lecture lecture;

        public LectureAction(Lecture lecture) {
            super(lecture.getLabel());
            this.lecture = lecture;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            JPanel listFieldsPanel = new JPanel(new GridLayout(3, 2));
            listFieldsPanel.add(new JLabel("Period:"));
            CourseSchedule courseSchedule = getSolution();
            List<Period> periodList = courseSchedule.getPeriodList();
            // Add 1 to array size to add null, which makes the entity unassigned
            JComboBox periodListField = new JComboBox(
                    periodList.toArray(new Object[periodList.size() + 1]));
            LabeledComboBoxRenderer.applyToComboBox(periodListField);
            periodListField.setSelectedItem(lecture.getPeriod());
            listFieldsPanel.add(periodListField);
            listFieldsPanel.add(new JLabel("Room:"));
            List<Room> roomList = courseSchedule.getRoomList();
            // Add 1 to array size to add null, which makes the entity unassigned
            JComboBox roomListField = new JComboBox(
                    roomList.toArray(new Object[roomList.size() + 1]));
            LabeledComboBoxRenderer.applyToComboBox(roomListField);
            roomListField.setSelectedItem(lecture.getRoom());
            listFieldsPanel.add(roomListField);
            listFieldsPanel.add(new JLabel("Pinned:"));
            JCheckBox pinnedField = new JCheckBox("cannot move during solving");
            pinnedField.setSelected(lecture.isPinned());
            listFieldsPanel.add(pinnedField);
            int result = JOptionPane.showConfirmDialog(CurriculumCoursePanel.this.getRootPane(), listFieldsPanel,
                    "Select period and room", JOptionPane.OK_CANCEL_OPTION);
            if (result == JOptionPane.OK_OPTION) {
                Period toPeriod = (Period) periodListField.getSelectedItem();
                if (lecture.getPeriod() != toPeriod) {
                    solutionBusiness.doChangeMove(lecture, "period", toPeriod);
                }
                Room toRoom = (Room) roomListField.getSelectedItem();
                if (lecture.getRoom() != toRoom) {
                    solutionBusiness.doChangeMove(lecture, "room", toRoom);
                }
                boolean toPinned = pinnedField.isSelected();
                if (lecture.isPinned() != toPinned) {
                    if (solutionBusiness.isSolving()) {
                        logger.error("Not doing user change because the solver is solving.");
                        return;
                    }
                    lecture.setPinned(toPinned);
                }
                solverAndPersistenceFrame.resetScreen();
            }
        }

    }

}
