/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
 *
 * 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 com.android.ide.eclipse.adt.internal.editors.layout.gle2;

import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;

import org.eclipse.swt.dnd.ByteArrayTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;

import java.io.UnsupportedEncodingException;

/**
 * A d'n'd {@link Transfer} class that can transfer a <em>simplified</em> XML fragment
 * to transfer elements and their attributes between {@link LayoutCanvas}.
 * <p/>
 * The implementation is based on the {@link ByteArrayTransfer} and what we transfer
 * is text with the following fixed format:
 * <p/>
 * <pre>
 * {element-name element-property ...
 *  attrib_name="attrib_value"
 *  attrib2="..."
 *  {...inner elements...
 *  }
 * }
 * {...next element...
 * }
 *
 * </pre>
 * The format has nothing to do with XML per se, except for the fact that the
 * transfered content represents XML elements and XML attributes.
 *
 * <p/>
 * The detailed syntax is:
 * <pre>
 * - ELEMENT     := {NAME PROPERTY*\nATTRIB_LINE*ELEMENT*}\n
 * - PROPERTY    := $[A-Z]=[^ ]*
 * - NAME        := [^\n=]+
 * - ATTRIB_LINE := @URI:NAME=[^\n]*\n
 * </pre>
 *
 * Elements are represented by {@link SimpleElement}s and their attributes by
 * {@link SimpleAttribute}s, all of which have very specific properties that are
 * specifically limited to our needs for drag'n'drop.
 */
final class SimpleXmlTransfer extends ByteArrayTransfer {

    // Reference: http://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html

    private static final String TYPE_NAME = "android.ADT.simple.xml.transfer.1";    //$NON-NLS-1$
    private static final int TYPE_ID = registerType(TYPE_NAME);
    private static final SimpleXmlTransfer sInstance = new SimpleXmlTransfer();

    /** Private constructor. Use {@link #getInstance()} to retrieve the singleton instance. */
    private SimpleXmlTransfer() {
        // pass
    }

    /** Returns the singleton instance. */
    public static SimpleXmlTransfer getInstance() {
        return sInstance;
    }

    /**
     * Helper method that returns the FQCN transfered for the given {@link ElementDescriptor}.
     * <p/>
     * If the descriptor is a {@link ViewElementDescriptor}, the transfered data is the FQCN
     * of the Android View class represented (e.g. "android.widget.Button").
     * For any other non-null descriptor, the XML name is used.
     * Otherwise it is null.
     *
     * @param desc The {@link ElementDescriptor} to transfer.
     * @return The FQCN, XML name or null.
     */
    public static String getFqcn(ElementDescriptor desc) {
        if (desc instanceof ViewElementDescriptor) {
            return ((ViewElementDescriptor) desc).getFullClassName();
        } else if (desc != null) {
            return desc.getXmlName();
        }

        return null;
    }

    @Override
    protected int[] getTypeIds() {
        return new int[] { TYPE_ID };
    }

    @Override
    protected String[] getTypeNames() {
        return new String[] { TYPE_NAME };
    }

    /** Transforms a array of {@link SimpleElement} into a native data transfer. */
    @Override
    protected void javaToNative(Object object, TransferData transferData) {
        if (object == null || !(object instanceof SimpleElement[])) {
            return;
        }

        if (isSupportedType(transferData)) {
            StringBuilder sb = new StringBuilder();
            for (SimpleElement e : (SimpleElement[]) object) {
                sb.append(e.toString());
            }
            String data = sb.toString();

            try {
                byte[] buf = data.getBytes("UTF-8");  //$NON-NLS-1$
                super.javaToNative(buf, transferData);
            } catch (UnsupportedEncodingException e) {
                // unlikely; ignore
            }
        }
    }

    /**
     * Recreates an array of {@link SimpleElement} from a native data transfer.
     *
     * @return An array of {@link SimpleElement} or null. The array may be empty.
     */
    @Override
    protected Object nativeToJava(TransferData transferData) {
        if (isSupportedType(transferData)) {
            byte[] buf = (byte[]) super.nativeToJava(transferData);
            if (buf != null && buf.length > 0) {
                try {
                    String s = new String(buf, "UTF-8"); //$NON-NLS-1$
                    return SimpleElement.parseString(s);
                } catch (UnsupportedEncodingException e) {
                    // unlikely to happen, but still possible
                }
            }
        }

        return null;
    }
}
