/*
 * Copyright (c) 2009, Takeyuki Nagao
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the
 * following conditions are met:
 * 
 *  * Redistributions of source code must retain the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer.
 *  * Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *    
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

package jp.sourceforge.dvibrowser.dvicore.gui.swing;

import java.awt.FlowLayout;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.logging.Logger;

import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import jp.sourceforge.dvibrowser.dvicore.DviException;
import jp.sourceforge.dvibrowser.dvicore.api.DviContext;
import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
import jp.sourceforge.dvibrowser.dvicore.api.DviPage;


public class TDviDocument
extends JPanel
implements DviContextSupport, ChangeListener
{
  private static final Logger LOGGER = Logger.getLogger(TDviDocument.class.getName());
	private static final long serialVersionUID = -8289664311728401054L;
	
	private JPanel panel = new JPanel();

	private final ArrayList<TDviPage> pageList
	  = new ArrayList<TDviPage>();

	private ViewSpec viewSpec;
		private DviLayoutManager dviLayout = new DefaultDviLayoutManager(this, 1);
	
	public void setViewSpec(ViewSpec vs) throws DviException {
	  if (viewSpec != null) {
	    viewSpec.removeChangeListener(this);
	  }
	  viewSpec = vs;
    if (viewSpec != null) {
      viewSpec.addChangeListener(this);
    }
    reload();
	}
	
	public ViewSpec getViewSpec() {
	  return viewSpec;
	}
	
	private final DviContextSupport dcs;
	public DviContext getDviContext() { return dcs.getDviContext(); }

  public TDviDocument(DviContextSupport dcs) {
    this.dcs = dcs;
    viewSpec = new ViewSpec(this);
    viewSpec.setResolution(viewSpec.getResolution().approximate(10));
    initializeComponents();
  }
  
  private void initializeComponents()
  {
//    setBackground(new Color(0xe0, 0xe0, 0xe0));
    setLayout(new FlowLayout());
    add(panel);
  }
  
  private boolean enableDelayedRendering = false;

	private DviDocument doc;
	public void setDviDocument(DviDocument doc) throws DviException
  {
    LOGGER.fine("Preparaing TDviDocument with viewSpec=" + viewSpec);
    this.doc = doc;
    try {
      panel.removeAll();
      pageList.clear();
      LOGGER.finer("dvi document = " + doc);
      if (doc != null) {
        panel.setLayout(getDviLayout().createLayout(doc, viewSpec.getResolution()));
        for (DviPage page : doc.getPages()) {
          LOGGER.fine("preparing TDviPage viewSpec=" + viewSpec);
          TDviPage dp = new TDviPage(this);
          dp.setViewSpec(viewSpec);
          dp.setDviLayout(dviLayout);
          dp.setPage(page);
          getDviLayout().decoratePage(this, dp);
          dp.setVisible(true);
          dp.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
              TDviDocument.this.fireChangeEvent();
            }
          });
          {
            MouseListener[] mls = getMouseListeners();
            for (int j = 0; j < mls.length; j++) {
              dp.addMouseListener(mls[j]);
            }
          }
          {
            MouseMotionListener[] mmls = getMouseMotionListeners();
            for (int j = 0; j < mmls.length; j++) {
              dp.addMouseMotionListener(mmls[j]);
            }
          }
          panel.add(dp);
          pageList.add(dp);
        }
      } else {
        LOGGER.finer("dvi document is null");
      }
    } catch (Exception ex) {
      LOGGER.severe(ex.toString());
    }
    revalidate();
    repaint();
  }

	
	public void reload()
	throws DviException
	{
	  setDviDocument(doc);
	}
	
	public DviDocument getDviDocument() {
	  return doc;
	}

  public DviLayoutManager getDviLayout()
  {
    return dviLayout;
  }

  public void setDviLayout(DviLayoutManager dviLayout)
  throws DviException
  {
    this.dviLayout = dviLayout;
    reload();
  }

  public void setEnableDelayedRendering(boolean enableDelayedRendering)
  {
    this.enableDelayedRendering = enableDelayedRendering;
  }

  public boolean getEnableDelayedRendering()
  {
    return enableDelayedRendering;
  }

  public void stateChanged(ChangeEvent e)
  {
    LOGGER.fine("stateChanged: EDT=" + SwingUtilities.isEventDispatchThread() + " viewSpec=" + viewSpec);
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        try {
          reload();
        } catch (DviException e) {
          LOGGER.warning(e.toString());
        }
      }
    });
  }
  
  public void addChangeListener(ChangeListener l)
  {
      listenerList.add(ChangeListener.class, l);
  }

  public void removeChangeListener(ChangeListener l)
  {
      listenerList.remove(ChangeListener.class, l);
  }

  protected void fireChangeEvent()
  {
    ChangeEvent event = null;
    
    Object[] listeners = listenerList.getListenerList();
    for (int i = listeners.length-2; i>=0; i-=2) {
      if (listeners[i] == ChangeListener.class) {
        if (event == null)
          event = new ChangeEvent(this);
        ((ChangeListener)listeners[i+1]).stateChanged(event);
      }
    }
  }
  
  public boolean isBusy()
  {
    for (TDviPage dp : pageList) {
      if (dp.isBusy()) return true;
    }
    return false;
  }

}
