/*------------------------------------------------------------------------------------
Copyright (c)2000 TogetherSoft Corporation. Patents pending. All rights reserved.
------------------------------------------------------------------------------------*/

package com.togethersoft.modules.xmleditor;

import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.util.*;
import java.io.*;
import java.awt.event.*;

import com.togethersoft.openapi.ide.*;
import com.togethersoft.openapi.ide.message.*;
import com.togethersoft.openapi.ide.window.*;
import com.togethersoft.openapi.ide.command.*;
import com.togethersoft.openapi.ide.project.*;

import com.togethersoft.together.util.message.Informator;
import com.togethersoft.together.util.xml.XMLEntityResolver;

import com.togethersoft.modules.xmleditor.xmltree.*;
import com.togethersoft.modules.xmleditor.dtd.*;
import com.togethersoft.modules.xmleditor.xmlcomponent.XMLEditorComponent;
import com.togethersoft.modules.xmleditor.utils.*;


public class XMLEditorDialog implements ActionListener
{
  private static final String DIALOG_TITLE = "XML Editor";

  private JDialog m_dialog;
  private IdeWindowManager m_windowManager;

  private XMLEditorComponent m_xmlEditorComp;
  private XMLTreeManager  m_xmlTreeManager = null;

  private JPanel  m_buttonPanel;
  private JButton m_createNewButton;
  private JButton m_loadButton;
  private JButton m_saveButton;
  private JButton m_closeButton;

  private String m_xmlFileName;

  //--------------------------------------------------------------------
  public XMLEditorDialog()
  {
    if (m_defaultDir.length() == 0)
      m_defaultDir = IdeAccess.getIdeManager().getHomeDirectory();

    m_windowManager = IdeWindowManagerAccess.getWindowManager();

    m_dialog = m_windowManager.createDialog (null);
    m_dialog.setTitle (DIALOG_TITLE);

    Container contentPane = m_dialog.getContentPane();
    contentPane.setLayout (new BorderLayout (0,4));

    m_xmlEditorComp = new XMLEditorComponent();
    contentPane.add (m_xmlEditorComp, BorderLayout.CENTER);

    contentPane.add (createButtonPanel(), BorderLayout.SOUTH);
    m_dialog.setSize (700, 500);
  }

  //--------------------------------------------------------------------
  private JPanel createButtonPanel()
  {
    m_buttonPanel = new JPanel(new FlowLayout());

    m_createNewButton = new JButton ("New");
    m_createNewButton.setMnemonic ('N');
    m_createNewButton.addActionListener (this);
    m_buttonPanel.add (m_createNewButton);

    m_loadButton = new JButton ("Load");
    m_loadButton.setMnemonic ('L');
    m_loadButton.addActionListener (this);
    m_buttonPanel.add (m_loadButton);

    m_saveButton = new JButton ("Save");
    m_saveButton.setMnemonic ('S');
    m_saveButton.setEnabled (false);
    m_saveButton.addActionListener (this);
    m_buttonPanel.add (m_saveButton);

    m_closeButton = new JButton ("Close");
    m_closeButton.setMnemonic ('C');
    m_closeButton.addActionListener (this);
    m_buttonPanel.add (m_closeButton);

    IdeDialogButtonGroup buttonGroup = m_windowManager.createButtonGroup();
    buttonGroup.add (m_createNewButton);
    buttonGroup.add (m_loadButton);
    buttonGroup.add (m_saveButton);
    buttonGroup.add (m_closeButton);

    return m_buttonPanel;
  }

  //--------------------------------------------------------------------
  public void show()
  {
    m_dialog.show();
  }

  //--------------------------------------------------------------------
  public void actionPerformed (ActionEvent event)
  {
    Object caller = event.getSource();

    if (caller == m_createNewButton)
    {
      createXmlFile();
      return;
    }

    if (caller == m_loadButton)
    {
      loadXmlFile();
      return;
    }

    if (caller == m_saveButton)
    {
      saveXmlFile();
      return;
    }

    if (caller == m_closeButton)
    {
      if (m_xmlTreeManager != null)
      {
        m_xmlEditorComp.stopEditing();

        if (m_xmlTreeManager.isModified())
        {
          String text = "The file has changed,\nwould you like to save changes ?";
          String answer = m_windowManager.showConfirmDialog (m_dialog, "Unsaved file", text, IdeButtonType.YES_NO_CANCEL_BUTTONS);

          if (IdeButtonType.CANCEL.equals (answer))
            return;

          if (IdeButtonType.YES.equals (answer))
          {
            saveXmlFile();
          }
        }
      }

      m_dialog.dispose();
      return;
    }

  }

  //--------------------------------------------------------------------
  private static class MyEntityResolver extends XMLTreeManager.DefaultEntityResolver
  {
    MyEntityResolver (XMLTreeManager xmlTreeManager, Informator informator)
    {
      super (xmlTreeManager, informator);
    }
  }

  //--------------------------------------------------------------------
  private void createXMLTreeManager()
  {
    m_xmlTreeManager = new XMLTreeManager()
    {
      protected XMLEntityResolver getEntityResolver (Informator informator)
      {
        return new MyEntityResolver (m_xmlTreeManager, informator);
      }

      protected DTDDocumentType getAltDocumentType() throws Exception
      {
        String text = "Sorry, it seems file \'" + m_selectedFileName + "\' has no reference to DTD file.\n" +
                      "To start editing the XML document you should provide the appropriate DTD file.\n\n" +
                      "Would you like to choose a different DTD file?";

        String answer = m_windowManager.showConfirmDialog (m_dialog, DIALOG_TITLE, text, IdeButtonType.YES_NO_BUTTONS);

        if (! IdeButtonType.YES.equals (answer))
          return null;

        showChooserDialog (IdeFileChooserConstant.OPEN_DIALOG, "Choose DTD File", "*.dtd", "DTD Files (*.dtd)");
        if (m_selectedFileName != null)
        {
          try
          {
            DTDDocumentType altDocumentType = DTDDocumentType.newInstance();
            altDocumentType.load (m_selectedFileName);

            return altDocumentType;
          }
          catch (Exception e)
          {
            throw e;
          }
          catch (Throwable e)
          {
            DebugTools.showException (m_dialog, e);
            throw new Exception (DIALOG_TITLE + ": fatal error");
          }
        }

        return null;
      }
    };
  }

  //--------------------------------------------------------------------
  private void updateTitle()
  {
    m_dialog.setTitle (DIALOG_TITLE + " - " + m_xmlFileName);
  }

  //--------------------------------------------------------------------
  private void createXmlFile()
  {
    showChooserDialog (IdeFileChooserConstant.OPEN_DIALOG, "Choose DTD File", "*.dtd", "DTD Files (*.dtd)");
    if (m_selectedFileName == null)
      return;

    m_xmlFileName = "???";

    try
    {
      DTDDocumentType documentType = DTDDocumentType.newInstance();
      documentType.load (m_selectedFileName);

      XMLParamDialog paramDialog = new XMLParamDialog();

      paramDialog.xmlVersion = "1.0";
      paramDialog.xmlEncoding = "UTF-8";
      paramDialog.xmlSystemID = m_selectedFileName.replace (File.separatorChar, '/');

      paramDialog.init (m_dialog, documentType);
      paramDialog.show();
      if (! paramDialog.isOk)
        return;

      if (paramDialog.xmlRootElementName.length() == 0)
      {
        m_windowManager.showMessageDialog (DIALOG_TITLE, IdeMessageType.ERROR, "No root element specified");
        return;
      }

      createXMLTreeManager();
      m_xmlTreeManager.initialize (documentType, paramDialog.xmlRootElementName);

      m_xmlTreeManager.setVersion (paramDialog.xmlVersion);
      m_xmlTreeManager.setEncoding (paramDialog.xmlEncoding);
      m_xmlTreeManager.setPublicID (paramDialog.xmlPublicID);
      m_xmlTreeManager.setSystemID (paramDialog.xmlSystemID);

      m_xmlEditorComp.setXMLTreeManager (m_xmlTreeManager);
      m_saveButton.setEnabled (true);
      updateTitle();
    }
    catch (Exception e)
    {
      m_windowManager.showMessageDialog (DIALOG_TITLE, IdeMessageType.ERROR, e.getMessage());
    }
    catch (Throwable e)
    {
      DebugTools.showException (m_dialog, e);
    }
  }

  //--------------------------------------------------------------------
  private void loadXmlFile()
  {
    showChooserDialog (IdeFileChooserConstant.OPEN_DIALOG, "Open XML File", "*.xml", "XML Files (*.xml)");
    if (m_selectedFileName == null)
      return;

    m_xmlFileName = m_selectedFileName;

    (new Thread()
    {
      public void run()
      {
        m_dialog.setCursor (Cursor.getPredefinedCursor (Cursor.WAIT_CURSOR));

        m_xmlEditorComp.setXMLTreeManager (null);
        m_createNewButton.setEnabled (false);
        m_loadButton.setEnabled (false);
        m_saveButton.setEnabled (false);

        try
        {
          createXMLTreeManager();
          m_xmlTreeManager.load (m_xmlFileName);

          m_xmlEditorComp.setXMLTreeManager (m_xmlTreeManager);
          m_saveButton.setEnabled (true);
          updateTitle();

          // m_windowManager.showMessageDialog (DIALOG_TITLE, IdeMessageType.INFORMATION,
          //                                    "File \'" + m_xmlFileName + "\' opened");
        }
        catch (Exception e)
        {
          m_windowManager.showMessageDialog (DIALOG_TITLE, IdeMessageType.ERROR, e.getMessage());
        }
        catch (Throwable e)
        {
          DebugTools.showException (m_dialog, e);
        }

        m_createNewButton.setEnabled (true);
        m_loadButton.setEnabled (true);

        m_dialog.setCursor (Cursor.getPredefinedCursor (Cursor.DEFAULT_CURSOR));
      }
    }).start();
  }

  //--------------------------------------------------------------------
  private void saveXmlFile()
  {
    if (m_xmlTreeManager == null)
      return;

    m_xmlEditorComp.stopEditing();

    showChooserDialog (IdeFileChooserConstant.SAVE_DIALOG, "Save XML File", "*.xml", "XML Files (*.xml)");
    if (m_selectedFileName == null)
      return;

    m_xmlFileName = m_selectedFileName;
    if (m_xmlFileName.indexOf ('.') < 0)
      m_xmlFileName += ".xml";

    m_dialog.setCursor (Cursor.getPredefinedCursor (Cursor.WAIT_CURSOR));
    try
    {
      FileOutputStream fos = new FileOutputStream (m_xmlFileName);
      m_xmlTreeManager.store (new PrintWriter (fos));
      fos.close();
      m_dialog.setCursor (Cursor.getPredefinedCursor (Cursor.DEFAULT_CURSOR));

      updateTitle();
      m_windowManager.showMessageDialog (DIALOG_TITLE, IdeMessageType.INFORMATION,
                           "File \'" + m_xmlFileName + "\' saved");
    }
/*
    catch (Exception e)
    {
      m_windowManager.showMessageDialog (DIALOG_TITLE, IdeMessageType.ERROR, e.getMessage());
    }
*/
    catch (Throwable e)
    {
      DebugTools.showException (m_dialog, e);
    }

    m_dialog.setCursor (Cursor.getPredefinedCursor (Cursor.DEFAULT_CURSOR));
  }

  //--------------------------------------------------------------------
  private static String m_defaultDir = new String();
  private String m_selectedFileName;

  private void showChooserDialog (int dialogType, String title, String mask, String maskDescription)
  {
    m_selectedFileName = null;

    IdeFileChooser chooser = IdeAccess.getWindowManager().createFileChooser (m_defaultDir);

    chooser.setDialogType (dialogType);
    chooser.setFileSelectionMode (IdeFileChooserConstant.FILES_ONLY);
    chooser.setDialogTitle (title);
    chooser.setCurrentDirectory (new File (m_defaultDir));

    MyFileFilter filter = new MyFileFilter (mask, maskDescription);
    chooser.addChoosableFileFilter (filter);
    chooser.setFileFilter (filter);

    if (chooser.showDialog (m_dialog) != IdeFileChooserConstant.APPROVE_OPTION)
      return;

    File selFile = chooser.getSelectedFile();
    m_defaultDir = chooser.getCurrentDirectory().getPath();

    if (selFile == null) return;

    String path = selFile.getPath();
    if (path != null && path.length() > 0)
      m_selectedFileName = path;
  }

  //--------------------------------------------------------------------
  /**
   * This function was written for primary debugging
   */
  private DTDDocumentType createDocumentType()
  {
    DTDAttributeDef   attrDef;
    DTDElementItemDef itemDef;
    DTDGroupItemDef   groupDef;

    DTDDocumentType documentType = DTDDocumentType.newInstance();

    //-- root element
    DTDElementDef rootElementDef = documentType.createElementDef ("root_element", DTDElementDef.ET_COMPOUND);

    // "element_1"
    DTDElementDef element1Def = documentType.createElementDef ("element_1", DTDElementDef.ET_COMPOUND);

    // attributes
    attrDef = documentType.createAttributeDef ("attribute_1");
    attrDef.setFullName ("Nice Attribute");
    attrDef.setValueTypeName ("StringField");
    element1Def.addAttributeDef (attrDef);

    attrDef = documentType.createAttributeDef ("attribute_2");
    attrDef.setFullName ("Charming Attribute");
    attrDef.setValueTypeName ("StringField");
    element1Def.addAttributeDef (attrDef);

    // "element_1_1"
    DTDElementDef element11Def = documentType.createElementDef ("element_1_1", DTDElementDef.ET_PCDATA);

    // "element_1_2"
    DTDElementDef element12Def = documentType.createElementDef ("element_1_2", DTDElementDef.ET_PCDATA);

    // add to element_1

    groupDef = documentType.createGroupItemDef (DTDGroupItemDef.OR_GROUP, DTDItemDef.CARDINALITY_ONCE);

    itemDef = documentType.createElementItemDef (element11Def, DTDItemDef.CARDINALITY_ONCE_OR_MORE);
    groupDef.addItemDef (itemDef);

    itemDef = documentType.createElementItemDef (element12Def, DTDItemDef.CARDINALITY_ONCE);
    groupDef.addItemDef (itemDef);

    element1Def.setChildrenItemDef (groupDef);


    //-- "element_2"
    DTDElementDef element2Def = documentType.createElementDef ("element_2", DTDElementDef.ET_EMPTY);

    // attributes
    attrDef = documentType.createAttributeDef ("attribute_21");
    attrDef.setFullName ("Integer Attribute");
    attrDef.setValueTypeName ("Int");
    element2Def.addAttributeDef (attrDef);

    //-- add to root element

    groupDef = documentType.createGroupItemDef (DTDGroupItemDef.AND_GROUP, DTDItemDef.CARDINALITY_ONCE_OR_MORE);

    itemDef = documentType.createElementItemDef (element1Def, DTDItemDef.CARDINALITY_ANY_TIMES);
    groupDef.addItemDef (itemDef);

    itemDef = documentType.createElementItemDef (element2Def, DTDItemDef.CARDINALITY_ONCE);
    groupDef.addItemDef (itemDef);

    rootElementDef.setChildrenItemDef (groupDef);

    return documentType;
  }

  //--------------------------------------------------------------------
  class MyFileFilter extends IdeFileFilter
  {
    private String m_mask;
    private String m_description;

    public MyFileFilter (String mask, String description)
    {
      super();
      m_mask = mask;
      m_description = description;
    }

    public String getMask()
    {
      return m_mask;
    }

    public String getDescription()
    {
      return m_description;
    }

    public boolean accept (File f)
    {
      return true;
    }
  }

  //--------------------------------------------------------------------
}
