/*
 * TV-Browser
 * Copyright (C) 04-2003 Martin Oberhauser (darras@users.sourceforge.net)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * CVS information:
 *     $Date: 2011-03-26 21:21:11 +0100 (Sat, 26 Mar 2011) $
 *   $Author: bananeweizen $
 * $Revision: 6974 $
 */
package programlistplugin;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import util.program.ProgramUtilities;
import util.settings.PluginPictureSettings;
import util.settings.ProgramPanelSettings;
import util.ui.Localizer;
import util.ui.ProgramList;
import util.ui.SendToPluginDialog;
import util.ui.TVBrowserIcons;
import util.ui.UiUtilities;
import util.ui.WindowClosingIf;
import com.jgoodies.forms.factories.Borders;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import devplugin.ActionMenu;
import devplugin.Channel;
import devplugin.Date;
import devplugin.Plugin;
import devplugin.PluginInfo;
import devplugin.Program;
import devplugin.ProgramFilter;
import devplugin.ProgramReceiveTarget;
import devplugin.Version;
/**
 * Shows all important programs in a time sorted list.
 *
 * @author René Mach
 * @since 2.7
 */
public class ProgramListPlugin extends Plugin {
  /**
   * maximum number of programs to be shown in the list. If the filter has more results, only the first results are shown.
   */
  private static final int MAX_PROGRAMS_SHOWN = 5000;
  protected static final Localizer mLocalizer = Localizer.getLocalizerFor(ProgramListPlugin.class);
  private static Version mVersion = new Version(3, 0);
  private JDialog mDialog;
  private JComboBox mBox;
  private ProgramFilter mFilter;
  private ProgramListSettings mSettings;
  private ArrayList<Program> mPrograms = new ArrayList<Program>();
  private DefaultListModel mModel;
  private ProgramList mList;
  private ProgramPanelSettings mProgramPanelSettings;
  private JCheckBox mShowDescription;
  private JComboBox mFilterBox;
  private ProgramFilter mRecieveFilter;
  private static ProgramListPlugin mInstance;
  private JButton mSendBtn;
  /**
   * Creates an instance of this class.
   */
  public ProgramListPlugin() {
    mInstance = this;
  }
  public static Version getVersion() {
    return mVersion;
  }
  public PluginInfo getInfo() {
    return new PluginInfo(ProgramListPlugin.class, mLocalizer.msg("name", "Program list"), mLocalizer.msg(
        "description", "Shows programs in a list."), "René Mach");
  }
  /**
   * Gets the instance of this plugin.
   *
   * @return The instance of this plugin.
   */
  public static ProgramListPlugin getInstance() {
    if (mInstance == null) {
      new ProgramListPlugin();
    }
    return mInstance;
  }
  public void loadSettings(final Properties properties) {
    mSettings = new ProgramListSettings(properties);
  }
  public Properties storeSettings() {
    return mSettings.storeSettings();
  }
  public ActionMenu getButtonAction() {
    AbstractAction action = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        mRecieveFilter = null;
        showProgramList();
      }
    };
    action.putValue(Action.NAME, mLocalizer.msg("name", "Important programs"));
    action.putValue(Action.SMALL_ICON, createImageIcon("emblems", "emblem-important", 16));
    action.putValue(Plugin.BIG_ICON, createImageIcon("emblems", "emblem-important", 22));
    return new ActionMenu(action);
  }
  private void fillFilterBox() {
    // initialize filter as allFilter because we may no longer find the filter
    // with the stored name
    if (mFilter == null) {
      mFilter = Plugin.getPluginManager().getFilterManager().getAllFilter();
    }
    ArrayList<ProgramFilter> filters = new ArrayList<ProgramFilter>();
    for (ProgramFilter filter : Plugin.getPluginManager().getFilterManager().getAvailableFilters()) {
      filters.add(filter);
    }
    if (mRecieveFilter != null) {
      filters.add(mRecieveFilter);
    }
    for (ProgramFilter filter : filters) {
      boolean found = false;
      for (int i = 0; i < mFilterBox.getItemCount(); i++) {
        if (filter != null && filter.equals(mFilterBox.getItemAt(i))) {
          found = true;
          break;
        }
      }
      if (!found) {
        mFilterBox.addItem(filter);
        if ((mRecieveFilter == null && filter.getName().equals(mSettings.getFilterName()))
            || (mRecieveFilter != null && filter.getName().equals(mRecieveFilter.getName()))) {
          mFilter = filter;
          mFilterBox.setSelectedItem(filter);
        }
      }
    }
    for (int i = mFilterBox.getItemCount() - 1; i >= 0; i--) {
      boolean found = false;
      for (ProgramFilter filter : filters) {
        if (filter.equals(mFilterBox.getItemAt(i))) {
          found = true;
          break;
        }
      }
      if (!found) {
        mFilterBox.removeItemAt(i);
      }
    }
  }
  private void fillProgramList() {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        try {
          mDialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
          mModel.clear();
          mPrograms.clear();
          boolean showExpired = false;
          Channel[] channels = mBox.getSelectedItem() instanceof String ? Plugin.getPluginManager()
              .getSubscribedChannels() : new Channel[] { (Channel) mBox.getSelectedItem() };
          Date date = channels.length > 1
              && mFilter.equals(Plugin.getPluginManager().getFilterManager().getAllFilter()) ? Plugin
              .getPluginManager().getCurrentDate() : Date.getCurrentDate();
          int startTime = Plugin.getPluginManager().getTvBrowserSettings().getProgramTableStartOfDay();
          int endTime = Plugin.getPluginManager().getTvBrowserSettings().getProgramTableEndOfDay();
          int maxDays = channels.length > 1
              && mFilter.equals(Plugin.getPluginManager().getFilterManager().getAllFilter()) ? 2 : 28;
          for (int d = 0; d < maxDays; d++) {
            if (Plugin.getPluginManager().isDataAvailable(date)) {
              for (Channel channel : channels) {
                for (Iterator<Program> it = Plugin.getPluginManager().getChannelDayProgram(date, channel); it.hasNext();) {
                  Program program = it.next();
                  if ((showExpired || !program.isExpired()) && mFilter.accept(program)) {
                    if (mFilter.equals(Plugin.getPluginManager().getFilterManager().getAllFilter())) {
                      if ((d == 0 && program.getStartTime() >= startTime)
                          || (d == 1 && program.getStartTime() <= endTime)) {
                        mPrograms.add(program);
                      }
                    } else {
                      mPrograms.add(program);
                    }
                  }
                }
              }
            }
            date = date.addDays(1);
          }
          if (channels.length > 1) {
            Collections.sort(mPrograms, ProgramUtilities.getProgramComparator());
          }
          int index = -1;
          for (Program program : mPrograms) {
            if (mModel.size() < MAX_PROGRAMS_SHOWN) {
              mModel.addElement(program);
              if (!program.isExpired() && index == -1) {
                index = mModel.getSize() - 1;
              }
            }
          }
          int forceScrollingIndex = mModel.size() - 1;
          if (forceScrollingIndex > 1000) {
            forceScrollingIndex = 1000;
          }
          mList.ensureIndexIsVisible(forceScrollingIndex);
          mList.ensureIndexIsVisible(index);
          mDialog.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    });
  }
  public void onDeactivation() {
    if (mDialog != null) {
      mDialog.setVisible(false);
    }
  }
  private static class ChannelListCellRenderer extends DefaultListCellRenderer {
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
        boolean cellHasFocus) {
      Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
      if (c instanceof JLabel && value instanceof Channel) {
        ((JLabel) c).setIcon(UiUtilities.createChannelIcon(((Channel) value).getIcon()));
      }
      return c;
    }
  }
  @Override
  public boolean canReceiveProgramsWithTarget() {
    return true;
  }
  @Override
  public ProgramReceiveTarget[] getProgramReceiveTargets() {
    return new ProgramReceiveTarget[] { new ProgramReceiveTarget(this, mLocalizer.msg("programTarget", "Program list"),
        "program list") };
  }
  @Override
  public boolean receivePrograms(Program[] programArr, ProgramReceiveTarget receiveTarget) {
    final List<Program> programs = Arrays.asList(programArr);
    mRecieveFilter = new ProgramFilter() {
      @Override
      public boolean accept(Program prog) {
        return programs.contains(prog);
      }
      @Override
      public String getName() {
        return mLocalizer.msg("filterName", "Received programs");
      }
      @Override
      public String toString() {
        return mLocalizer.msg("filterName", "Received programs");
      }
    };
    showProgramList();
    return true;
  }
  private void showProgramList() {
    try {
      if (mDialog == null) {
        mDialog = new JDialog(getParentFrame());
        mDialog.setTitle(mLocalizer.msg("name", "Important programs"));
        mDialog.getContentPane().setLayout(new BorderLayout(0, 10));
        ((JPanel) mDialog.getContentPane()).setBorder(Borders.DIALOG_BORDER);
        UiUtilities.registerForClosing(new WindowClosingIf() {
          public void close() {
            closeDialog();
          }
          public JRootPane getRootPane() {
            return mDialog.getRootPane();
          }
        });
        mModel = new DefaultListModel();
        boolean showDescription = mSettings.getShowDescription();
        mProgramPanelSettings = new ProgramPanelSettings(new PluginPictureSettings(
            PluginPictureSettings.ALL_PLUGINS_SETTINGS_TYPE), !showDescription, ProgramPanelSettings.X_AXIS);
        mList = new ProgramList(mModel, mProgramPanelSettings);
        mList.addMouseListeners(null);
        mBox = new JComboBox(Plugin.getPluginManager().getSubscribedChannels());
        mBox.insertItemAt(mLocalizer.msg("allChannels", "All channels"), 0);
        mBox.setRenderer(new ChannelListCellRenderer());
        mBox.setSelectedIndex(mSettings.getIndex());
        mFilterBox = new JComboBox();
        if (mSettings.getFilterName().isEmpty()) {
          mSettings.setFilterName(Plugin.getPluginManager().getFilterManager().getAllFilter().getName());
        }
        fillFilterBox();
        mFilterBox.addItemListener(new ItemListener() {
          public void itemStateChanged(ItemEvent e) {
            mFilter = (ProgramFilter) mFilterBox.getSelectedItem();
            if (mFilter != mRecieveFilter) {
              mSettings.setFilterName(mFilter.getName());
            }
            mBox.getItemListeners()[0].itemStateChanged(null);
          }
        });
        mBox.addItemListener(new ItemListener() {
          public void itemStateChanged(ItemEvent e) {
            fillProgramList();
            mSettings.setIndex(mBox.getSelectedIndex());
          }
        });
        CellConstraints cc = new CellConstraints();
        JPanel panel = new JPanel(new FormLayout("1dlu,default,3dlu,default:grow", "pref,2dlu,pref,2dlu"));
        panel.add(new JLabel(Localizer.getLocalization(Localizer.I18N_CHANNELS) + ":"), cc.xy(2, 1));
        panel.add(mBox, cc.xy(4, 1));
        panel.add(new JLabel(mLocalizer.msg("filter", "Filter:")), cc.xy(2, 3));
        panel.add(mFilterBox, cc.xy(4, 3));
        mSendBtn = new JButton(TVBrowserIcons.copy(TVBrowserIcons.SIZE_SMALL));
        mSendBtn.setToolTipText(mLocalizer.msg("send", "Send to other Plugins"));
        mSendBtn.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            Program[] programs = mList.getSelectedPrograms();
            if (programs == null || programs.length == 0) {
              programs = mPrograms.toArray(new Program[mPrograms.size()]);
            }
            if (programs != null && programs.length > 0) {
              SendToPluginDialog sendDialog = new SendToPluginDialog(ProgramListPlugin.getInstance(), (Window) mDialog,
                  programs);
              sendDialog.setVisible(true);
            }
          }
        });
        mShowDescription = new JCheckBox(mLocalizer.msg("showProgramDescription", "Show program description"),
            showDescription);
        mShowDescription.addItemListener(new ItemListener() {
          public void itemStateChanged(ItemEvent e) {
            int topRow = mList.getFirstVisibleIndex();
            mProgramPanelSettings.setShowOnlyDateAndTitle(e.getStateChange() == ItemEvent.DESELECTED);
            mSettings.setShowDescription(e.getStateChange() == ItemEvent.SELECTED);
            mList.updateUI();
            if (topRow != -1) {
              mList.ensureIndexIsVisible(topRow);
            }
          }
        });
        JButton close = new JButton(Localizer.getLocalization(Localizer.I18N_CLOSE));
        close.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            closeDialog();
          }
        });
        JPanel southPanel = new JPanel(new FormLayout("default,5dlu,default,0dlu:grow,default", "default"));
        southPanel.add(mSendBtn, cc.xy(1, 1));
        southPanel.add(mShowDescription, cc.xy(3, 1));
        southPanel.add(close, cc.xy(5, 1));
        mDialog.getContentPane().add(panel, BorderLayout.NORTH);
        mDialog.getContentPane().add(new JScrollPane(mList), BorderLayout.CENTER);
        mDialog.getContentPane().add(southPanel, BorderLayout.SOUTH);
        layoutWindow("programListWindow", mDialog, new Dimension(500, 500));
        mDialog.setVisible(true);
        mBox.getItemListeners()[0].itemStateChanged(null);
      } else {
        if (!mDialog.isVisible()) {
          fillFilterBox();
        }
        mDialog.setVisible(!mDialog.isVisible());
        if (mDialog.isVisible()) {
          fillProgramList();
        } else {
          saveMe();
        }
      }
    } catch (Exception ee) {
      ee.printStackTrace();
    }
  }
  private void closeDialog() {
    mDialog.setVisible(false);
    saveMe();
  }
}