/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.openide.text;

import java.beans.VetoableChangeListener;
import java.util.concurrent.Callable;
import javax.swing.text.*;
import javax.swing.undo.UndoManager;

/**
 * Emulates the behaviour of NetBeans editor's kit with all its special
 * implementations.
 *
 * @author  Jaroslav Tulach
 */
class NbLikeEditorKit extends DefaultEditorKit implements Callable<Void> {
    public javax.swing.text.Document createDefaultDocument() {
        return new Doc ();
    }

    class Doc extends PlainDocument
    implements NbDocument.WriteLockable, StyledDocument {
//    implements NbDocument.PositionBiasable, NbDocument.WriteLockable,
//    NbDocument.Printable, NbDocument.CustomEditor, NbDocument.CustomToolbar, NbDocument.Annotatable {

        public Doc() {
            super (new StringContent ());
            
            // mark yourself of supporting modificationListener
            putProperty ("supportsModificationListener", Boolean.TRUE); 
        }

        public void runAtomic (Runnable r) {
            try {
                runAtomicAsUser (r);
            } catch (BadLocationException ex) {
                // too bad, no modification allowed
            }
        }

        public void runAtomicAsUser (Runnable r) throws BadLocationException {
             insOrRemoveOrRunnable (-1, null, null, -1, false, r);
        }

        public javax.swing.text.Style getLogicalStyle(int p) {
            return null;
        }

        public javax.swing.text.Style getStyle(java.lang.String nm) {
            return null;
        }

        public javax.swing.text.Style addStyle(java.lang.String nm, javax.swing.text.Style parent) {
            return null;
        }

        public void setParagraphAttributes(int offset, int length, javax.swing.text.AttributeSet s, boolean replace) {
        }

        public void setCharacterAttributes(int offset, int length, javax.swing.text.AttributeSet s, boolean replace) {
        }

        public void removeStyle(java.lang.String nm) {
        }

        public java.awt.Font getFont(javax.swing.text.AttributeSet attr) {
            return null;
        }

        public java.awt.Color getBackground(javax.swing.text.AttributeSet attr) {
            return null;
        }

        public javax.swing.text.Element getCharacterElement(int pos) {
            return null;
        }

        public void setLogicalStyle(int pos, javax.swing.text.Style s) {
        }

        public java.awt.Color getForeground(javax.swing.text.AttributeSet attr) {
            return null;
        }

        private int changes;
        private boolean modifiable;
        public void insertString (int offs, String str, AttributeSet a) throws BadLocationException {
            insOrRemoveOrRunnable (offs, str, a, 0, true, null);
        }

        public void remove (int offs, int len) throws BadLocationException {
            insOrRemoveOrRunnable (offs, null, null, len, false, null);
        }
        
        
        private void insOrRemoveOrRunnable (int offset, String str, AttributeSet set, int len, boolean insert, Runnable run) 
        throws BadLocationException {
            // Current implementation does not require the document to fire VetoableChangeListener from runAtomic()
            // and since Doc allows DoucmentFilter attaching (PlainDocument extends AbstractDocument)
            // there is no need to do any extra things.
            if (run != null) {
                run.run();
            } else {
                if (insert) {
                    super.insertString(offset, str, set);
                } else {
                    super.remove(offset, len);
                }
            }
            return;
            
//            boolean alreadyInsideWrite = getCurrentWriter () == Thread.currentThread ();
//            if (alreadyInsideWrite) {
//                if (run != null) {
//                    run.run ();
//                } else {
//                    assertOffset (offset);
//                    if (!modifiable) {
//                        throw new BadLocationException("Document modification vetoed", offset);
//                    }
//                    if (insert) {
//                        super.insertString (offset, str, set);
//                    } else {
//                        super.remove(offset, len);
//                    }
//                }
//                return;
//            }
//            
//            Object o = getProperty ("modificationListener");
//            
//            if (run != null) {
//                boolean canBeModified = notifyModified(o); // Need to notify before acquiring write-lock (that will span the whole runnable)
//                writeLock ();
//                modifiable = canBeModified;
//                int prevChanges = changes;
//                try {
//                    run.run ();
//                } finally {
//                    writeUnlock ();
//                }
//                if (changes == prevChanges) { // No changes => property chane with Boolean.FALSE
//                    if (o instanceof VetoableChangeListener) {
//                        VetoableChangeListener l = (VetoableChangeListener)o;
//                        try {
//                            l.vetoableChange (new java.beans.PropertyChangeEvent (this, "modified", null, Boolean.FALSE));
//                        } catch (java.beans.PropertyVetoException ignore) {
//                        }
//                    }
//                }
//            } else {
//                assertOffset (offset);
//                modifiable = notifyModified (o);
//                try {
//                    if (!modifiable) {
//                        throw new BadLocationException("Document modification vetoed", offset);
//                    }
//                    if (insert) {
//                        super.insertString (offset, str, set);
//                    } else {
//                        super.remove(offset, len);
//                    }
//                } catch (BadLocationException ex) {
//                    if (o instanceof VetoableChangeListener) {
//                        VetoableChangeListener l = (VetoableChangeListener)o;
//                        try {
//                            l.vetoableChange (new java.beans.PropertyChangeEvent (this, "modified", null, Boolean.FALSE));
//                        } catch (java.beans.PropertyVetoException ignore) {
//                        }
//                    }
//                    throw ex;
//                }
//            }
        }
        
        private void assertOffset (int offset) throws BadLocationException {
            if (offset < 0) throw new BadLocationException ("", offset);
        }
        
        private boolean notifyModified (Object o) {
            boolean canBeModified = true;
            if (o instanceof VetoableChangeListener) {
                VetoableChangeListener l = (VetoableChangeListener)o;
                try {
                    l.vetoableChange (new java.beans.PropertyChangeEvent (this, "modified", null, Boolean.TRUE));
                } catch (java.beans.PropertyVetoException ex) {
                    canBeModified = false;
                }
            }
            return canBeModified;
        }

        protected void fireRemoveUpdate (javax.swing.event.DocumentEvent e) {
            super.fireRemoveUpdate(e);
            changes++;
        }

        protected void fireInsertUpdate (javax.swing.event.DocumentEvent e) {
            super.fireInsertUpdate(e);
            changes++;
        }

        protected void fireChangedUpdate (javax.swing.event.DocumentEvent e) {
            super.fireChangedUpdate(e);
            changes++;
        }

    } // end of Doc

    Thread callThread;
    public Void call() throws Exception {
        callThread = Thread.currentThread();
        return null;
    }
}
