/*
 * Created on Jan 30, 2004
 */
package org.lucci.madhoc.gui.aircraft;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.DefaultListCellRenderer;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.lucci.config.ConfigurationException;
import org.lucci.madhoc.env.NetworkEnvironment;
import org.lucci.madhoc.gui.MonitorView;
import org.lucci.madhoc.network.Connection;
import org.lucci.madhoc.network.Network;
import org.lucci.madhoc.network.Station;
import org.lucci.madhoc.network.net.Location;
import org.lucci.madhoc.simulation.Monitor;
import org.lucci.text.TextUtilities;
import org.lucci.up.FilePlotter;
import org.lucci.up.InteractiveSwingPlotterListener;
import org.lucci.up.SwingPlotter;
import org.lucci.up.data.Figure;
import org.lucci.up.data.Point;
import org.lucci.up.data.rendering.DataElementRenderer;
import org.lucci.up.data.rendering.figure.FigureRenderer;
import org.lucci.up.data.rendering.point.PointRenderer;
import org.lucci.up.data.rendering.point.PolygoneRenderer;

public class AircraftView extends MonitorView implements ChangeListener
{
    class  UpRendererRenderer extends DefaultListCellRenderer
    {
        public Component getListCellRendererComponent(JList arg0, Object renderer, int arg2, boolean arg3, boolean arg4)
        {
            JLabel l = (JLabel) super.getListCellRendererComponent(arg0, renderer, arg2, arg3, arg4);
            String s = ((DataElementRenderer) renderer).getPublicName();
            s = TextUtilities.capitalize(s);
            l.setText(s);
            return l;
        }
    }
    
    private Collection<PointRenderer> availableStationRenderers = new Vector<PointRenderer>();
    private Collection<PointRenderer> availableCoverageRenderers = new Vector<PointRenderer>();
    private Collection<FigureRenderer> availableConnectionRenderers = new Vector<FigureRenderer>();

    private Collection<PointRenderer> activeStationRenderers = new Vector<PointRenderer>();
    private Collection<PointRenderer> activeCoverageRenderers = new Vector<PointRenderer>();
    private Collection<FigureRenderer> activeConnectionRenderers = new Vector<FigureRenderer>();

    private OutOfProjectionComputerRenderer outOfProjectionComputerRenderer = new OutOfProjectionComputerRenderer ();
    private OutOfProjectionConnectionRenderer outOfProjectionConnectionRenderer = new OutOfProjectionConnectionRenderer();


    private File imageDirectory;
    
    private AirCraftViewPlotter plotter;

    private JTabbedPane tabbedPane = new JTabbedPane();

    private JCheckBox drawGridCheckbox = new JCheckBox("Draw grid", true);
    private JCheckBox drawEnvCheckbox = new JCheckBox("Draw environment", false);
    private JCheckBox drawOutOfProjectionStations = new JCheckBox("Draw out of projection computers", false);
    private JCheckBox drawOutOfProjectionConnections = new JCheckBox("Draw out of projection connections", false);
    
    private JCheckBox drawConnectionsIfUsedCheckbox = new JCheckBox("Show only connections in use", false);

    private JComboBox computersRendererCombobox = new JComboBox();
    private JComboBox coverageRendereCombobox = new JComboBox();
    private JComboBox connectionRendererCombobox;

    private JLabel descriptionLabel;
    private Collection<StationDescriptionPane> stationDescriptionPanes;
    private String fileExtension;


    public void setMonitor(Monitor application)
    {
        super.setMonitor(application);
        setPreferredHeight(2);

        getAvailableStationRenderers().add(new BlackStationRenderer());
        getAvailableStationRenderers().add(new BlackPointStationRenderer());
        getAvailableStationRenderers().add(new GrayStationRenderer());
        getAvailableStationRenderers().add(new ImageStationRenderer());
        getAvailableStationRenderers().add(new NoComputerRendering());
        getAvailableStationRenderers().add(new PolygoneRenderer());

        getAvailableConnectionRenderers().add(new GreyAndBlackIfUsedConnectionRenderer());
        getAvailableConnectionRenderers().add(new BlackConnectionRenderer());
        getAvailableConnectionRenderers().add(new AutomaticConnectionRenderer());
        getAvailableConnectionRenderers().add(new ConnectionTypeRenderer());
        getAvailableConnectionRenderers().add(new ConnectionQualityRenderer());
        getAvailableConnectionRenderers().add(new ConnectionLoadRatioRenderer());
        getAvailableConnectionRenderers().add(new NoConnectionRenderering());
        
        getAvailableCoverageRenderers().add(new NoComputerRendering());
        getAvailableCoverageRenderers().add(new CircleSignalRenderer());
        getAvailableCoverageRenderers().add(new FilledCircleSignalRenderer());

        setLayout(new BorderLayout());

        {
            JPanel p = new JPanel(new GridBagLayout());
            GridBagConstraints c = new GridBagConstraints();
            plotter = new AirCraftViewPlotter(this);
            p.add(plotter, c);
            add(p, BorderLayout.CENTER);
        }
        {
            JPanel p = new JPanel(new GridBagLayout());
            GridBagConstraints c = new GridBagConstraints();

            tabbedPane.addTab("Control", createControlPane());
            descriptionLabel = new JLabel();
            
            try
            {
                this.stationDescriptionPanes = (Collection<StationDescriptionPane>) application.getNetwork().getSimulation().getConfiguration().getInstantiatedClasses("aircraft_view_description_panes");
                
                for (StationDescriptionPane label : this.stationDescriptionPanes)
                {
                    tabbedPane.addTab(label.getPublicName(), new JScrollPane(label.getLabel()));
                }
            }
            catch (ConfigurationException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            p.add(tabbedPane, c);
            add(p, BorderLayout.EAST);
        }

        plotter.getListeners().add(new SwingPlotterHandler());
        connectionRendererCombobox.setSelectedIndex(0);
        computersRendererCombobox.setSelectedIndex(0);
    }

    private JPanel createControlPane()
    {
        JPanel controlPanel = new JPanel(new GridBagLayout());
        controlPanel.setBorder(new TitledBorder("Draw..."));

        {
            drawGridCheckbox.addChangeListener(this);
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 0;
            c.insets = new Insets(30, 0, 0, 0);
            c.anchor = GridBagConstraints.WEST;
            controlPanel.add(drawGridCheckbox, c);
        }
        {
            drawEnvCheckbox.addChangeListener(this);
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 1;
            c.insets = new Insets(0, 0, 0, 0);
            c.anchor = GridBagConstraints.WEST;
            controlPanel.add(drawEnvCheckbox, c);
        }
        {
            drawOutOfProjectionStations.addChangeListener(this);
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 2;
            c.insets = new Insets(0, 0, 0, 0);
            c.anchor = GridBagConstraints.WEST;
            controlPanel.add(drawOutOfProjectionStations, c);
        }
        {
            drawOutOfProjectionConnections.addChangeListener(this);
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 3;
            c.insets = new Insets(0, 0, 15, 0);
            c.anchor = GridBagConstraints.WEST;
            controlPanel.add(drawOutOfProjectionConnections, c);
        }
        {
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 4;
            c.insets = new Insets(15, 0, 0, 0);
            c.anchor = GridBagConstraints.WEST;
            controlPanel.add(new JLabel("Render nodes using:"), c);
        }
        {
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 5;
            c.insets = new Insets(0, 0, 15, 0);
            c.anchor = GridBagConstraints.EAST;
            computersRendererCombobox = new JComboBox();
            computersRendererCombobox.setRenderer(new UpRendererRenderer());

            for (PointRenderer renderer : getAvailableStationRenderers())
            {
                computersRendererCombobox.addItem(renderer);
            }

            computersRendererCombobox.addActionListener(new ActionListener()
            {
                public void actionPerformed(ActionEvent event)
                {
                    updateViewContent();
                }
            });
            controlPanel.add(computersRendererCombobox, c);
        }
        {
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 6;
            c.insets = new Insets(15, 0, 0, 0);
            c.anchor = GridBagConstraints.WEST;
            controlPanel.add(new JLabel("Render connections using:"), c);
        }
        {
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 7;
            c.anchor = GridBagConstraints.EAST;
            connectionRendererCombobox = new JComboBox();
            computersRendererCombobox.setRenderer(new UpRendererRenderer());
            
            for (FigureRenderer renderer : getAvailableConnectionRenderers())
            {
                connectionRendererCombobox.addItem(renderer);
            }

            connectionRendererCombobox.addActionListener(new ActionListener()
            {
                public void actionPerformed(ActionEvent event)
                {
                    updateViewContent();
                }
            });
            controlPanel.add(connectionRendererCombobox, c);
        }
        {
            drawConnectionsIfUsedCheckbox.addChangeListener(this);
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 8;
            c.anchor = GridBagConstraints.WEST;
            c.insets = new Insets(0, 10, 15, 0);
            controlPanel.add(drawConnectionsIfUsedCheckbox, c);
        }
        {
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 9;
            c.insets = new Insets(15, 0, 0, 0);
            c.anchor = GridBagConstraints.WEST;
            controlPanel.add(new JLabel("Render radio signal using:"), c);
        }
        {
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 10;
            c.insets = new Insets(0, 0, 15, 0);
            c.anchor = GridBagConstraints.EAST;
            coverageRendereCombobox = new JComboBox();
            computersRendererCombobox.setRenderer(new UpRendererRenderer());
            Iterator iterator = getAvailableCoverageRenderers().iterator();
            
            while (iterator.hasNext())
            {
                coverageRendereCombobox.addItem(iterator.next());
            }

            coverageRendereCombobox.addActionListener(new ActionListener()
            {
                public void actionPerformed(ActionEvent event)
                {
                    updateViewContent();
                }
            });
            controlPanel.add(coverageRendereCombobox, c);
        }

        return controlPanel;
    }

    public AirCraftViewPlotter getPlotter()
    {
        return plotter;
    }

    public String getName()
    {
        return "Aircraft view";
    }

    public void updateViewContent()
    {
        getActiveStationRenderers().clear();
        getActiveStationRenderers().addAll(new Vector(Arrays.asList(computersRendererCombobox.getSelectedObjects())));
        getActiveConnectionRenderers().clear();
        getActiveConnectionRenderers().addAll(new Vector(Arrays.asList(connectionRendererCombobox.getSelectedObjects())));
        getActiveCoverageRenderers().clear();
        getActiveCoverageRenderers().addAll(new Vector(Arrays.asList(coverageRendereCombobox.getSelectedObjects())));
        
        plotter.setUpdateNeeded(true);
        plotter.repaint(0);
        
        if (fileExtension != null)
        {
            int imageNumber = getMonitor().getNetwork().getSimulation().getIteration();
            String imageNumberString = TextUtilities.flushRight("" + imageNumber, 4, '0');
//            String filename = getMonitor().getName() + "_" + getName() + "_ "+ imageNumberString + "." + fileExtension;
            String filename = this.imageDirectory + File.separator + imageNumberString + "." + fileExtension;
            File file = new File(filename);
            System.out.println("Writing " + filename);
            FilePlotter filePlotter = new FilePlotter();
            filePlotter.setGraphics2DPlotter(plotter.getGraphics2DPlotter());
            Dimension dimension = plotter.getSize();
            
            if (dimension.width > 0 && dimension.height > 0)
            {
                try
                {
                    filePlotter.plotFile(file, dimension.width, dimension.height);
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }

    public Figure createNetworkFigure(Network network)
    {
        Figure figure = new Figure();

        if (drawEnvCheckbox.isSelected())
        {
            figure.addFigure(createEnvFigure(network));
        }

        for (Station computer : network.getStations())
        {
            Location p = computer.getLocation();
            p.removeAllRenderers();
        }

        figure.addFigure(createCoverageFigure(network));
        figure.addFigure(createConnectionFigure(network));
        figure.addFigure(createComputersFigure(network));
        return figure;
    }

    private Figure createEnvFigure(Network network)
    {
        NetworkEnvironment env = network.getNetworkEnvironment();
        return env.createFigure(getPlotter().getGraphics2DPlotter().getSpace());

//      if (env instanceof CityEnvironment)
//      {
//          CityFigureFactory figureFactory = new DefaultCityFigureFactory();
//          return figureFactory.createCityFigure(((CityEnvironment) env).getCity());
//      }
//      else 
//      if (env instanceof MallEnvironment)
//      {
//      }
//      else
//      {
//          return new Figure();
//      }
    }

    private Figure createConnectionFigure(Network network)
    {
        Figure allConnectionsFigure = new Figure();
        Iterator connectionIterator = network.findConnections().iterator();

        while (connectionIterator.hasNext())
        {
            Connection connection = (Connection) connectionIterator.next();

            if (connection.getUsedBandwith() > 0 || !drawConnectionsIfUsedCheckbox.isSelected())
            {
                ConnectionFigure figure = new ConnectionFigure();
                figure.setConnection(connection);
                figure.addPoint(connection.getNetworkInterface1().getNetworkingUnit().getStation().getLocation());
                figure.addPoint(connection.getNetworkInterface2().getNetworkingUnit().getStation().getLocation());

                if (getProjectionComponent().getProjection().acceptConnection(connection))
                {
                    Iterator rendererIterator = getActiveConnectionRenderers().iterator();

                    while (rendererIterator.hasNext())
                    {
                        figure.addRenderer((DataElementRenderer) rendererIterator.next());
                    }
                }
                else
                {
                    if (drawOutOfProjectionConnections.isSelected())
                    {
                        figure.addRenderer(outOfProjectionConnectionRenderer);
                    }
                }

                allConnectionsFigure.addFigure(figure);
            }
        }

        return allConnectionsFigure;
    }

    private Figure createComputersFigure(Network network)
    {
        Figure vehiclesFigure = new Figure();
        Iterator stationIterator = network.getStations().iterator();

        while (stationIterator.hasNext())
        {
            Station computer = (Station) stationIterator.next();
            Location p = computer.getLocation();
            vehiclesFigure.addPoint(p);
            Iterator rendererIterator = getActiveStationRenderers().iterator();

            if (getProjectionComponent().getProjection().acceptComputer(computer))
            {
                while (rendererIterator.hasNext())
                {
                    p.addRenderer((PointRenderer) rendererIterator.next());
                }
            }
            else
            {
                if (drawOutOfProjectionStations.isSelected())
                {
                    p.addRenderer(outOfProjectionComputerRenderer);
                }
            }
        }

        return vehiclesFigure;
    }

    private Figure createCoverageFigure(Network network)
    {
        Figure coverageFigure = new Figure();
        Iterator networkNodeIterator = network.getStations().iterator();

        while (networkNodeIterator.hasNext())
        {
            Station station = (Station) networkNodeIterator.next();
            Location p = station.getLocation();
            coverageFigure.addPoint(p);
            
            if (getProjectionComponent().getProjection().acceptComputer(station))
            {
                Iterator rendererIterator = getActiveCoverageRenderers().iterator();

                while (rendererIterator.hasNext())
                {
                    p.addRenderer((PointRenderer) rendererIterator.next());
                }
            }
        }

        return coverageFigure;
    }


    private class SwingPlotterHandler implements InteractiveSwingPlotterListener
    {
        /*
         * (non-Javadoc)
         * 
         * @see org.lucci.up.InteractiveSwingPlotterListener#pointsSelected(org.lucci.up.SwingPlotter,
         *      java.util.Collection)
         */
        public void pointsSelected(SwingPlotter sp, Collection points)
        {
            if (points.size() == 1)
            {
                Point p = (Point) points.iterator().next();

                if (p instanceof Location)
                {
                    Location point = (Location) p;
                    Station computer = point.getComputer();

                    for (StationDescriptionPane sdp : AircraftView.this.stationDescriptionPanes)
                    {
                        sdp.getLabel().setText(sdp.getDescriptionForStation(computer));
                    }
                }
            }
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.lucci.up.SwingPlotterListener#paintStarting(org.lucci.up.SwingPlotter)
         */
        public void paintStarting(SwingPlotter sp)
        {
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.lucci.up.SwingPlotterListener#paintFinished(org.lucci.up.SwingPlotter)
         */
        public void paintFinished(SwingPlotter sp)
        {
        }
    }

    public void iterationScopedValuesReinitializationRequired()
    {
    }
    public Collection<PointRenderer> getActiveStationRenderers()
    {
        return activeStationRenderers;
    }
    public Collection<FigureRenderer> getAvailableConnectionRenderers()
    {
        return availableConnectionRenderers;
    }


    public Collection<FigureRenderer> getActiveConnectionRenderers()
    {
        return activeConnectionRenderers;
    }
    public Collection<PointRenderer> getActiveCoverageRenderers()
    {
        return activeCoverageRenderers;
    }
    public void setActiveStationRenderers(Collection<PointRenderer> activeStationRenderers)
    {
        this.activeStationRenderers = activeStationRenderers;
    }
    
    public Collection<PointRenderer> getAvailableCoverageRenderers()
    {
        return availableCoverageRenderers;
    }
    public Collection<PointRenderer> getAvailableStationRenderers()
    {
        return availableStationRenderers;
    }

    /* (non-Javadoc)
     * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
     */
    public void stateChanged(ChangeEvent arg0)
    {
        getPlotter().getGraphics2DPlotter().getSpace().setVisible(drawGridCheckbox.isSelected());
        updateViewContent();
    }

    public void configure() throws Throwable
    {
        if (getMonitor().getNetwork().getSimulation().getConfiguration().getBoolean("write_aircraft_views_files"))
        {
            this.fileExtension = getMonitor().getNetwork().getSimulation().getConfiguration().getString("aircraft_views_file_extension");        
            this.imageDirectory = new File(getMonitor().getNetwork().getSimulation().getConfiguration().getString("aircraft_views_image_directory"));
            
            if (this.imageDirectory.isFile())
            {
                throw new ConfigurationException("aircraft_views_image_directory", this.imageDirectory.getAbsolutePath() + " is not a directory, it is a regular file");
            }
            else
            {
                if (!this.imageDirectory.exists())
                {
                    this.imageDirectory.mkdirs();
                }
            }
        }
        else
        {
            this.fileExtension = null;
        }
    }
}