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 = 80;

    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.ORANGE); // 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_,
            //                                   1,1,1,1,1,1,1,1);

            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);

        }

    }
}
