package util;

import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
import java.io.*;
import java.text.*;
import java.awt.Toolkit;
import java.awt.datatransfer.*;
import DrawTop.*;
import shape.*;
import shapeUtil.*;
import textBox.*;
import geomExtension.*;

public class Edit implements ClipboardOwner {

    Clipboard systemClipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
    boolean lostOwnership = false;
    PasteContainer pasteContainerSave = null;
    Point2D clickedPointSave = null;
    public static final DataFlavor AttributedStringFlavor =
            new DataFlavor(java.io.Serializable.class, "AttributedString");
    public static final DataFlavor DrawShapeFlavor =
            new DataFlavor(java.io.Serializable.class, "ShapeElement");
    public static final int PlainString = 1;
    public static final int AttributedString = 2;
    public static final int Shape = 3;
    public static final int Image = 4;
    public static int debug = 0;

    public void cut() {
        ObjectTable.getExecCommand().clearClickedPoint();
        ContainerManager containerManager = ObjectTable.getContainerManager();
        ShapeContainer shapeContainer = containerManager.getEditableTextBox();
        if (shapeContainer != null) {
            boolean copied = copyString(shapeContainer, false);
            if (copied) {
                return;
            }
        }
        ShapeContainer[] containers = containerManager.getSelectedContainers();
        if (containers != null && containers.length > 0) {
            copyContainers(containers, false);
        }
    }

    public void copy() {
        ObjectTable.getExecCommand().clearClickedPoint();
        ContainerManager containerManager = ObjectTable.getContainerManager();
        ShapeContainer shapeContainer = containerManager.getEditableTextBox();
        if (shapeContainer != null) {
            boolean copied = this.copyString(shapeContainer, true);
            if (copied) {
                return;
            }
        }
        ShapeContainer[] containers = containerManager.getSelectedContainers();
        if (containers != null && containers.length > 0) {
            this.copyContainers(containers, true);
        }
    }

    private boolean copyString(ShapeContainer shapeContainer, boolean copy) {
        boolean selected = false;
        TextBox textBox = shapeContainer.getTextBox();
        if (textBox == null || !textBox.hasSelectedText()) {
            return false;
        }
        AttributedString attribStr = textBox.getSelectedText();
        if (!copy) {
            textBox.deleteSelectedText(TextBox.COMMAND);
        }
        try {
            TransferableAttributedString transferableText =
                    new TransferableAttributedString(attribStr.getIterator());
            this.systemClipboard.setContents(transferableText, this);
            selected = true;
            if (debug > 0) {
                System.out.println("** Edit.cutString string=" + transferableText.toString());
                System.out.println("** SystemClipboard information (Edit.copyString) ; "
                        + this.getClipboardInformation(systemClipboard, this));
            }
        } catch (Exception e) {
            System.err.println("*** Warning Edit.cutString System Clipboard "
                    + "is currently not available, e=" + e);
        }
        return selected;
    }//cutString

    private void copyContainers(ShapeContainer[] containers, boolean copy) {
        TransferableShapeContainer transferable = new TransferableShapeContainer(containers);
        if (!copy) {
            ContainerManager containerManager = ObjectTable.getContainerManager();
            //-----------------------------------//
            containerManager.undoSetupStart();
            //-----------------------------------//
            for (int i = 0; i < containers.length; i++) {
                containerManager.deleteContainer(containers[i]);
            }
            //---------------------------------//
            containerManager.undoSetupEnd();
            //---------------------------------//
        }
        try {
            this.systemClipboard.setContents(transferable, this);
            if (debug > 0) {
                System.out.println("** SystemClipboard information (Edit.copyContainers) ; "
                        + this.getClipboardInformation(systemClipboard, this));
            }
        } catch (Exception e) {
            System.err.println("*** Warning Edit.copyShapeElements System Clipboard "
                    + "is currently not available, e=" + e);
        }
    }//cutShapeElements

    public void paste() {
        Transferable contents = this.systemClipboard.getContents(null);
        if (debug > 0) {
            System.out.println("** SystemClipboard information (Edit.paste);"
                    + this.getClipboardInformation(systemClipboard, this));
        }
        if (contents == null) {
            System.err.println("*** Warning  Edit.paste;  nothing pasted");
            return;
        } else {
            if (debug > 0) {
                DataFlavor[] dataFlavors = contents.getTransferDataFlavors();
                for (int i = 0; i < dataFlavors.length; i++) {
                    System.out.println("dataFlavors[" + i + "]=" + dataFlavors[i]
                            + ", presentableName=" + dataFlavors[i].getHumanPresentableName());
                }
            }
        }
        boolean pasted = false;
        if (contents.isDataFlavorSupported(Edit.AttributedStringFlavor)) {
            SerializableAttributedString attribStr = null;
            try {
                Object object = contents.getTransferData(Edit.AttributedStringFlavor);
                if (object != null) {
                    attribStr = (SerializableAttributedString) object;
                    pasted = this.pasteAttributedString(attribStr);
                }
            } catch (Exception e) {
                //System.err.println("*** Warning: Edit.paste AttributedString e="+e);
            }
            if (pasted) {
                if (debug > 0) {
                    System.out.println("AttributedStringFlavor pasted");
                }
                return;
            }
        }

        pasted = false;
        if (contents.isDataFlavorSupported(DataFlavor.stringFlavor)) {
            String str = "";
            try {
                str = (String) (contents.getTransferData(DataFlavor.stringFlavor));
                pasted = this.pasteString(str);
            } catch (Exception e) {
                System.err.println("*** Warning: Edit.paste String e=" + e);
            }
            if (pasted) {
                if (debug > 0) {
                    System.out.println("stringFlavor pasted");
                }
                return;
            }
        }

        pasted = false;
        if (contents.isDataFlavorSupported(Edit.DrawShapeFlavor)) {
            SerializableElement[] sElements = new SerializableElement[0];
            try {
                sElements = (SerializableElement[]) (contents.getTransferData(Edit.DrawShapeFlavor));
                PasteContainer pasteContainer = new PasteContainer(sElements);
                // changed? 
                if (this.pasteContainerSave == null
                        || !pasteContainer.getId().equals(this.pasteContainerSave.getId())) {
                    this.pasteContainerSave = pasteContainer;
                    this.pasteContainerSave.setNewContents(true);
                    this.clickedPointSave = null;
                }
                pasted = this.pasteContainers(this.pasteContainerSave);
                this.pasteContainerSave.setNewContents(false);
                if (debug > 0) {
                    System.out.println("++ pasteContainerSave  " + this.pasteContainerSave.toString());
                }
            } catch (Exception e) {
                //System.err.println("*** Warning: ShapeElement Edit.paste Container e="+e);
                //e.printStackTrace();
            }
            if (pasted) {
                if (debug > 0) {
                    System.out.println("DrawShapeFlavor pasted");
                }
                return;
            }
        }

        if (contents.isDataFlavorSupported(DataFlavor.imageFlavor)) {
            BufferedImage bufferedImage = null;
            try {
                bufferedImage = (BufferedImage) (contents.getTransferData(DataFlavor.imageFlavor));
                PasteContainer pasteContainer = new PasteContainer(bufferedImage);
                // changed?   
                if (this.pasteContainerSave == null
                        || !pasteContainer.getId().equals(this.pasteContainerSave.getId())) {
                    this.pasteContainerSave = pasteContainer;
                    this.pasteContainerSave.setNewContents(true);
                    this.clickedPointSave = null;
                }
                this.pasteImage(this.pasteContainerSave);
                this.pasteContainerSave.setNewContents(false);
                if (debug > 0) {
                    System.out.println("imageFlavor pasted");
                }
            } catch (Exception e) {
                System.err.println("*** Warning: Image Edit.paste image e=" + e);
                e.printStackTrace();
            }
            return;
        }
    } //paste

    public boolean hasClipboad(int type) {
        if (type < 1 || type > Edit.Image) {
            System.out.println("*** Warning: Edit.hasClipboad, Type error");
            return false;
        }
        Transferable contents = this.systemClipboard.getContents(null);
        if (type == Edit.PlainString && contents.isDataFlavorSupported(DataFlavor.stringFlavor)) {
            return true;
        }
        if (type == Edit.AttributedString && contents.isDataFlavorSupported(Edit.AttributedStringFlavor)) {
            return true;
        }
        if (type == Edit.Shape && contents.isDataFlavorSupported(Edit.DrawShapeFlavor)) {
            return true;
        }
        if (type == Edit.Image && contents.isDataFlavorSupported(DataFlavor.imageFlavor)) {
            return true;
        }
        return false;
    }

    public boolean pasteString(String str) {
        boolean pasted = false;
        if (debug > 0) {
            System.out.println("** Edit.pasteString " + str);
        }
        ContainerManager containerManager = ObjectTable.getContainerManager();
        ShapeContainer container = containerManager.getEditableTextBox();
        if (container != null && container.isTextBox()) {
            TextBox textBox = container.getTextBox();
            textBox.deleteSelectedText(TextBox.COMMAND);
            AttributedString attribStr = new AttributedString(str);
            AttributedCharacterIterator iterator=attribStr.getIterator();
            int position = textBox.getTextIndex();
            //System.out.println("** Edit.pasteAttributedString position=" + position);
            if (position < 0) {
                position = 0;
            }
            textBox.insertText(TextBox.COMMAND, position, iterator);
            pasted = true;
            return pasted;
        }
        return pasted;
    }

    private boolean pasteAttributedString(SerializableAttributedString sString) {
        if (debug > 0) {
            System.out.println("** Edit.pasteAttributedString " + sString.toString());
        }
        boolean pasted = false;
        ContainerManager containerManager = ObjectTable.getContainerManager();
        ShapeContainer container = containerManager.getEditableTextBox();
        if (container != null && container.isTextBox()) {
            TextBox textBox = container.getTextBox();
            textBox.deleteSelectedText(TextBox.COMMAND);
            AttributedString attribStr = sString.getAttributedString();
            int position = textBox.getTextIndex();
            if (position < 0) {
                position = 0;
            }
            textBox.insertText(TextBox.COMMAND, position, attribStr.getIterator());
            pasted = true;
            return pasted;
        }
        return pasted;
    }

    private boolean pasteContainers(PasteContainer pasteContainer) {
        boolean pasted = false;
        DrawPanel drawPanel = ObjectTable.getDrawPanel();
        ContainerManager containerManager = ObjectTable.getContainerManager();
        ShapeContainer[] containers = containerManager.getContainers();
        if (containers != null && containers.length > 0) {
            for (int i = 0; i < containers.length; i++) {
                containers[i].setSelected(false);
            }
        }

        SerializableElement[] sElements = pasteContainer.getElementList();
        int size = 0;
        if (sElements != null && sElements.length > 0) {
            pasted = true;
            size = sElements.length;
        }
        //convert SerializableElement to ShapeContainer  
        ShapeContainer[] copiedContainers = new ShapeContainer[size];
        for (int i = 0; i < size; i++) {
            //---------------------------------------------------------------------------------//  
            ShapeContainer container = SerializableElementUtil.invertShapeContainer(sElements[i]);
            //---------------------------------------------------------------------------------//   
            if (debug > 0) {
                System.out.println(" Edit.pasteContainers container=" + container.toString());
            }
            //container.clearShapeIdWithChildren();
            copiedContainers[i] = container;

        }
        // shift shape
        Vector2D transVector = this.getPastePosition(pasteContainer, copiedContainers);
        Rectangle2D oldBox = new Rectangle2D.Double(0, 0, 1, 1);
        Rectangle2D newBox = new Rectangle2D.Double(transVector.getX(), transVector.getY(), 1, 1);
        //---------------------------------//
        containerManager.undoSetupStart();
        //---------------------------------//
        for (int i = 0; i < size; i++) {
            copiedContainers[i].setSelected(true);
            //--------------------------------------------------//
            ConnectionUtil.moveResize(copiedContainers[i],
                    oldBox, newBox, false, false);
            //--------------------------------------------------//  
            copiedContainers[i].setNewShapeId(containerManager, true);
            containerManager.addContainer(copiedContainers[i]);
            if (debug > 0) {
                System.out.println(" Edit.pasteContainers copiedContainers="
                        + copiedContainers[i].toString());
            }
        }
        //------------------------------//
        containerManager.undoSetupEnd();
        //------------------------------//
        drawPanel.repaint("Edit");
        return pasted;
    }

    private boolean pasteImage(PasteContainer pasteContainer) {
        Image image = pasteContainer.getImage();
        if (image == null) {
            return false;
        }
        SerializableElement sElements = this.createSerializableElement(image);
        boolean pasted = false;

        ShapeContainer imageContainer = null;
        //------------------------------------------------------------------------//  
        imageContainer = SerializableElementUtil.invertShapeContainer(sElements);
        //------------------------------------------------------------------------//   
        if (debug > 0) {
            System.out.println(" Edit.pasteContainers container=" + imageContainer.getShapeId());
        }
        //imageContainer.clearShapeIdWithChildren();

        ShapeContainer[] containers = new ShapeContainer[1];
        containers[0] = imageContainer;
        // shift shape
        Vector2D transVector = this.getPastePosition(pasteContainer, containers);

        Rectangle2D oldBox = new Rectangle2D.Double(0, 0, 1, 1);
        Rectangle2D newBox = new Rectangle2D.Double(transVector.getX(), transVector.getY(), 1, 1);
        //---------------------------------//
        ContainerManager containerManager = ObjectTable.getContainerManager();
        containerManager.undoSetupStart();
        //---------------------------------//
        imageContainer.setNewShapeId(containerManager, true);
        containerManager.addContainer(imageContainer);
        imageContainer.setSelected(true);
        //ExecCommand exec=ObjectTable.getExecCommand();
        //--------------------------------------------------//  
        ConnectionUtil.moveResize(imageContainer,
                oldBox, newBox, false, false);
        //--------------------------------------------------//  
        //------------------------------//
        containerManager.undoSetupEnd();
        //------------------------------//
        ObjectTable.getDrawPanel().repaint("Edit");
        return pasted;
    }

    private Vector2D getPastePosition(PasteContainer pasteContainer,
            ShapeContainer[] containers) {
        final Vector2D baseVecor = new Vector2D(20.5d, 10.5d);
        if (pasteContainer.isNewContents()) {
            this.clickedPointSave = null;
        }
        ExecCommand execCommand = ObjectTable.getExecCommand();
        int previousCommandId = execCommand.getPreviousCommandId();
        Point2D clickedPoint = null;
        clickedPoint = execCommand.getClickedPoint();
        if (debug > 0) {
            System.out.println("** Edit.getPastePosition clickedPoint=" + Util.Pt(clickedPoint)
                    + ", previousCommandId=" + Command.getCommandString(previousCommandId));
        }
        if (!pasteContainer.pasteToSameFilePage()) {
            pasteContainer.setShiftCount(0);
        }

        if (clickedPoint != null) {
            if (this.clickedPointSave == null) {
                this.clickedPointSave = clickedPoint;
                pasteContainer.setShiftCount(0);
            } else if (Vector2D.dist(clickedPoint, this.clickedPointSave) > 1.0) {
                this.clickedPointSave = clickedPoint;
                pasteContainer.setShiftCount(0);
            }
        }

        int shiftCount = pasteContainer.getCurrentShiftCount();
        Rectangle2D box = ShapeElementUtil.getBoundingBox(containers);
        Vector2D boxCenter = new Vector2D(box.getX() + 0.5 * box.getWidth(),
                box.getY() + 0.5 * box.getHeight());
        Vector2D transVector = new Vector2D();
        if (this.clickedPointSave != null) {
            transVector = Vector2D.sub(new Vector2D(this.clickedPointSave), boxCenter);
        }

        transVector = Vector2D.add(transVector, Vector2D.multiply(shiftCount, baseVecor));
        if (debug > 0) {
            System.out.println("** Edit.getPastePosition transVector=" + transVector.toString()
                    + "\n pasteContainer=" + pasteContainer);
        }
        return transVector;
    }

    private SerializableElement createSerializableElement(Image image) {
        SerializableElement data = new SerializableElement();
        data.shapeId = "Image (No-0)";
        data.elementType = "IMAGE";
        double width = image.getWidth(null);
        double height = image.getHeight(null);
        double scaleX = 128 / width;
        double scaleY = 128 / height;
        double scale = Math.min(scaleX, scaleY);
        Rectangle2DE rect = new Rectangle2DE(0, 0, scale * width, scale * height);
        data.serializableCurve2D = rect.getSerializableCurve2D();
        data.lineWidth = 0.1f;
        data.startArrowType = 0;
        data.endArrowType = 0;
// data for ImageElement only
        data.imageByteData = this.getByteImage(image);
        return data;
    }

    public byte[] getByteImage(Image image) {
        byte[] byteData = null;
        if (image == null) {
            return null;
        }
        try {
            int w = image.getWidth(null);
            int h = image.getHeight(null);
            BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
            Graphics g = bufferedImage.getGraphics();
            g.drawImage(image, 0, 0, null);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ImageIO.write(bufferedImage, "JPG", outputStream);
            byteData = outputStream.toByteArray();
        } catch (IOException e) {
            System.err.println("*** Error ImageElement: Image could not be write: e=" + e);
        }
        return byteData;
    }

    public void lostOwnership(Clipboard clipboard, Transferable contents) {
        this.lostOwnership = true;
        if (debug > 0) {
            System.out.println("** lostOwnership: Clipboard contents replaced");
            System.out.println("-- " + this.getClipboardInformation(clipboard, this));
        }
        DataFlavor[] flavors = contents.getTransferDataFlavors();
        if (debug > 0) {
            System.out.println("-- Transferable contents DataFlavors listing");
        }
        for (int i = 0; i < flavors.length; i++) {
            if (debug > 0) {
                System.out.println("    DataFlavor[" + i + "]  "
                        + "name=" + flavors[i].getHumanPresentableName()
                        + ", mime type=" + flavors[i].getMimeType());
            }
        }
    }

    private String getClipboardInformation(Clipboard clipboard, ClipboardOwner clipboardOwner) {
        String str = "Clipboard name=" + clipboard.getName();
        try {
            Transferable content = clipboard.getContents(clipboardOwner);
            str += "\n-- getContents=" + content + ", content.simpleName="
                    + content.getClass().getSimpleName();
            String name = clipboard.getName();
            str += ", getName=" + name;
            DataFlavor[] flavors = clipboard.getAvailableDataFlavors();
            if (flavors.length == 0) {
                str += ", flavors=null";
            }
            for (int i = 0; i < flavors.length; i++) {
                str += "\n-- AvailableDataFlavor[" + i + "], name="
                        + flavors[i].getHumanPresentableName()
                        + ", MIME type=" + flavors[i].getMimeType();
            }
        } catch (Exception e) {
            str += "\n-- system clipboard is not available now, e=" + e;
        }
        return str;
    }

    public void delete() {
        if (debug > 0) {
            System.out.println("** Edit.delete called");
        }
        ContainerManager containerManager = ObjectTable.getContainerManager();
        DrawPanel drawPanel = ObjectTable.getDrawPanel();
        ShapeContainer shapeContainer = containerManager.getEditableTextBox();
//delete text
        if (shapeContainer != null && shapeContainer.isTextBox()) {
            TextBox textBox = shapeContainer.getTextBox();
            int ret = textBox.deleteTextByDelCommandOrBSkey(TextBox.COMMAND);
            drawPanel.repaint("Edit");
            if (ret > 0 || ret < 0) {
                return;
            }
        }
//delete container
        ShapeContainer[] containers = containerManager.getSelectedContainers();
        //--------------------------------//
        containerManager.undoSetupStart();
        //--------------------------------//
        for (int i = 0; i < containers.length; i++) {
            containerManager.deleteContainer(containers[i]);
        }
        //-------------------------------//
        containerManager.undoSetupEnd();
        //-------------------------------//
        drawPanel.repaint("Edit");
    } //delete
}
