package TUGLaby;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.LinkedList;

import javax.swing.JPanel;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;



/*
 * RuleTreePanel.java
 * This Panel represents the current configuration of the rules to control the
 * roboter. The panel is target for drop actions from the RuleBrickPanel
 * which holds the atomic actions and rules.
 *
 * Created on 25. August 2004, 17:25
 */

/**
 * @version 0.1
 * @author auguan@sbox
 */
public class RuleTreePanel extends javax.swing.JPanel implements Scrollable {
    private static final long serialVersionUID = 1L;

    private static final int CONDITION_HEIGHT = 92;
    private static final int CONDITION_WIDTH = 250;
    /** Drop related classes */
    private DropRuleListener drop_rule_listener_;
    private DropTarget drop_target_;
    private int acceptable_actions_ = DnDConstants.ACTION_COPY;

    private int top_level_rules_id_;
    private LinkedList rule_list_;
    
    private HashMap brick_images_;
    private int maxUnitIncrement = 1;
    private static final int MAX_TOP_RULES = 120;
    private Dimension start_dimension_;
    /** Creates new form RuleTreePanel */
    public RuleTreePanel( HashMap brick_images ) {
        
        initComponents();
        this.setBackground(Color.BLUE); // haha
        drop_rule_listener_ = new DropRuleListener();
        brick_images_ = brick_images;
        drop_target_ = new DropTarget(this, this.acceptable_actions_,
                this.drop_rule_listener_, true);
    
        top_level_rules_id_=1;
        rule_list_ = new LinkedList();
        start_dimension_ = this.getSize();
    //    System.out.println(start_dimension_);
    }
    public LinkedList getRuleList()
    {
        return rule_list_;
    }
    /**
     * This methode deletes the rule given by id from the list and repaints the viewport
     * @param id
     */
    public void deleteRule( int id )
    {
        System.out.println("Deleting Rule #"+id);
        for( int index=0; index < rule_list_.size(); index++ )
        {
          if( ((ConditionRulePanel) rule_list_.get( index )).getID() == id )
          {
              rule_list_.remove( index );
          }
        }
        this.drawRules();
    }
    /**
     * This code generates new rule visual
     */
    public void drawRules()
    {
        this.removeAll();
        for( int index=0; index < rule_list_.size(); index++ )
        {
	        ConditionRulePanel temp = (ConditionRulePanel) rule_list_.get( index );  
          this.add( temp );
	        
	        temp.setBounds( 0,(index)*CONDITION_HEIGHT, 
	                              CONDITION_WIDTH, 
	                              CONDITION_HEIGHT );
	        temp.repaint();
	        // muss umgeschrieben werden
	        System.out.println("index: "+index);
	        System.out.println("i*CH="+index*CONDITION_HEIGHT+" getH="+this.getHeight());
	       
	        if( (index+1)*CONDITION_HEIGHT > this.getHeight() ) 
	        {
		         
	            this.setSize( new Dimension(CONDITION_WIDTH,
	                    (index+1)*CONDITION_HEIGHT+20) );
		          this.setPreferredSize( new Dimension(CONDITION_WIDTH,
		                  (index+1)*CONDITION_HEIGHT+20) );
	        }
	        else
	        {
	           // this.setSize( start_dimension_ );
	           // this.setPreferredSize(start_dimension_ );
	        }
        }
        this.repaint();
    }
    public void addNewRule(String rule) {

      JPanel new_rule = null;
        
      if( rule.compareTo("ConditionRule") == 0 )
      {
        new_rule = new ConditionRulePanel( top_level_rules_id_, this, brick_images_ ); 
      } 
      else if( rule.compareTo("ActionRule") == 0 ) 
      {
        System.out.println("It's not allowed to drop Action in the root.");
      }   
        
	    if( new_rule != null )
	    {
	        
	        rule_list_.addLast( new_rule );
	        top_level_rules_id_++;
	        
	        // extern
          this.drawRules();
      }
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    private void initComponents()
    {
        //setLayout(new java.awt.GridLayout(0, 1));
        setLayout( null );
    }
    
//  Start Scrollable interface ***********************************************************
    
    public Dimension getPreferredScrollableViewportSize() {
        return getPreferredSize();
    }
    
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {

        int currentPosition = 0;
        if (orientation == SwingConstants.HORIZONTAL)
            currentPosition = visibleRect.x;
        else
            currentPosition = visibleRect.y;

        if (direction < 0) {
            int newPosition = currentPosition - (currentPosition / maxUnitIncrement) * maxUnitIncrement;
            return (newPosition == 0) ? maxUnitIncrement : newPosition;
        } else {
            return ((currentPosition / maxUnitIncrement) + 1) * maxUnitIncrement - currentPosition;
        }
    }
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
        if (orientation == SwingConstants.HORIZONTAL)
            return visibleRect.width - maxUnitIncrement;
        else
            return visibleRect.height - maxUnitIncrement;
    }
    public boolean getScrollableTracksViewportWidth() {
        return false;
    }
    public boolean getScrollableTracksViewportHeight() {
        return false;
    }
    public void setMaxUnitIncrement(int pixels) {
        maxUnitIncrement = pixels;
    }
    
//  End Scrollable interface ***********************************************************  
      
    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables
    /** ************************************************************************ */
    public class DropRuleListener implements DropTargetListener {

        /**
         * Called by isDragOk Checks to see if the flavor drag flavor is
         * acceptable
         * 
         * @param e
         *            the DropTargetDragEvent object
         * @return whether the flavor is acceptable
         */
        private boolean isDragFlavorSupported(DropTargetDragEvent e) {
            boolean ok = false;
            if (e.isDataFlavorSupported(DataFlavor.stringFlavor)) {
                ok = true;
            } else if (e.isDataFlavorSupported(DataFlavor.plainTextFlavor)) {
                ok = true;
            }
            return ok;
        }

        /**
         * Called by drop Checks the flavors and operations
         * 
         * @param e
         *            the DropTargetDropEvent object
         * @return the chosen DataFlavor or null if none match
         */
        private DataFlavor chooseDropFlavor(DropTargetDropEvent e) {
            DataFlavor chosen = null;
            if (e.isLocalTransfer() == true )
              if (e.isDataFlavorSupported(DataFlavor.stringFlavor)) {
                chosen = DataFlavor.stringFlavor;
            } else if (e.isDataFlavorSupported(DataFlavor.plainTextFlavor)) {
                chosen = DataFlavor.plainTextFlavor;
            }
            return chosen;
        }

        /**
         * Called by dragEnter and dragOver Checks the flavors and operations
         * 
         * @param e
         *            the event object
         * @return whether the flavor and operation is ok
         */
        private boolean isDragOk(DropTargetDragEvent e) {
            if (isDragFlavorSupported(e) == false) {
                // System.out.println( "isDragOk:no flavors chosen" );
                return false;
            }
            int da = e.getDropAction();
            // System.out.print("dt drop action " + da);
            // System.out.println(" my acceptable actions " +
            // acceptable_actions_);
            if ((da & RuleTreePanel.this.acceptable_actions_) == 0)
                return false;
            return true;
        }

        /**
         * start "drag under" feedback on component invoke acceptDrag or
         * rejectDrag based on isDragOk
         */
        public void dragEnter(DropTargetDragEvent e) {
            // System.out.println( "dtlistener dragEnter");
            if (isDragOk(e) == false) {
                // System.out.println( "enter not ok");
                // CODE HERE if bad
                e.rejectDrag();
                return;
            }
            // CODE HERE if good
            // System.out.println( "dt enter: accepting " + e.getDropAction());
            e.acceptDrag(e.getDropAction());
        }

        /**
         * continue "drag under" feedback on component invoke acceptDrag or
         * rejectDrag based on isDragOk
         */
        public void dragOver(DropTargetDragEvent e) {
            if (isDragOk(e) == false) {
                // System.out.println( "dtlistener dragOver not ok" );
                // CODE IF NESSECARY
                e.rejectDrag();
                return;
            }
            // System.out.println( "dt over: accepting");
            e.acceptDrag(e.getDropAction());
        }

        public void dropActionChanged(DropTargetDragEvent e) {
            if (isDragOk(e) == false) {
                // System.out.println( "dtlistener changed not ok" );
                e.rejectDrag();
                return;
            }
            // System.out.println( "dt changed: accepting"+e.getDropAction());
            e.acceptDrag(e.getDropAction());
        }

        public void dragExit(DropTargetEvent e) {
            // System.out.println( "dtlistener dragExit");
        }

        /**
         * perform action from getSourceActions on the transferrable invoke
         * acceptDrop or rejectDrop invoke dropComplete if its a local (same
         * JVM) transfer, use StringTransferable.localStringFlavor find a match
         * for the flavor check the operation get the transferable according to
         * the chosen flavor do the transfer
         */
        public void drop(DropTargetDropEvent e) {
            // System.out.println( "dtlistener drop");
            DataFlavor chosen = chooseDropFlavor(e);
            if (chosen == null) {
                // System.err.println( "No flavor match found" );
                e.rejectDrop();
                return;
            }
            // System.err.println( "Chosen data flavor is " +
            // chosen.getMimeType());
            int da = e.getDropAction();
            int sa = e.getSourceActions();
            // System.out.println( "drop: sourceActions: " + sa);
            // System.out.println( "drop: dropAction: " + da);

            if ((sa & RuleTreePanel.this.acceptable_actions_) == 0) {
                // System.err.println( "No action match found" );
                e.rejectDrop();
                // CODE
                return;
            }

            Object data = null;
            try {
                e.acceptDrop(RuleTreePanel.this.acceptable_actions_);
                data = e.getTransferable().getTransferData(chosen);
                if (data == null)
                    throw new NullPointerException();
            } catch (Throwable t) {
                // System.err.println( "Couldn't get transfer data: " +
                // t.getMessage());
                t.printStackTrace();
                e.dropComplete(false);
                // Bad transdata
                return;
            }
            // System.out.println( "Got data: " + data.getClass().getName() );

            if (data instanceof String) {
                String s = (String) data;
                //DropLabel.this.setText(s);
                
                RuleTreePanel.this.addNewRule(s);
                
            } else if (data instanceof InputStream) {
                InputStream input = (InputStream) data;
                InputStreamReader isr = null;
                //	BufferedReader br = null;
                try {
                    // br = new BufferedReader(isr=new
                    // InputStreamReader(input,"Unicode"));
                    isr = new InputStreamReader(input, "Unicode");
                } catch (UnsupportedEncodingException uee) {
                    isr = new InputStreamReader(input);
                }

                StringBuffer str = new StringBuffer();
                int in = -1;
                try {
                    while ((in = isr.read()) >= 0) {
                        //// System.out.println("read: " + in);
                        if (in != 0)
                            str.append((char) in);
                    }

                    /*
                     * you get garbage chars this way try { String line=null;
                     * while( (line = br.readLine()) != null) {
                     * str.append(line); str.append('\n'); //
                     * System.out.println( "read: " + line); //
                     * System.out.println( "read: " +
                     * (int)line.charAt(line.length()-1)); }
                     * 
                     * br.close();
                     */
                    // CODE HERE DATA FROM STREAM
                    //DropLabel.this.setText(str.toString());
                } catch (IOException ioe) {
                    /*
                     * bug #4094987 sun.io.MalformedInputException: Missing
                     * byte-order mark e.g. if dragging from MS Word 97 still a
                     * bug in 1.2 final
                     */

                    // System.err.println( "cannot read" + ioe);
                    e.dropComplete(false);

                    String message = "Bad drop\n" + ioe.getMessage();
                    // System.err.println(message);
                }

            } else {
                // System.out.println( "drop: rejecting");
                e.dropComplete(false);

                return;
            }

            e.dropComplete(true);

        }

    }
}
