/*
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 */
package com.sun.glass.ui.win;

import com.sun.glass.ui.Application;
import com.sun.glass.ui.Pixels;
import com.sun.glass.ui.SystemClipboard;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.HashMap;

public class WinSystemClipboard extends SystemClipboard {
    static native void initIDs();
    static {
        Application.loadNativeLibrary();
        initIDs();
    }

    protected long ptr = 0L; //native pointer
    protected WinSystemClipboard(String name) {
        super(name);
        create();
    }

    protected native boolean isOwner();

    protected native void create();
    protected native void dispose();

    /*
     * public mime types to system clipboard
     */
    protected native void push(Object[] keys, int supportedActions);

    /*
     * extract clipboard snap-shot
     */
    protected native boolean pop();

    static byte[] terminator = new byte[] { 0, 0 };
    static String defaultCharset = "UTF-16LE";

    protected byte[] fosSerialize(String mime, long index) {
        Object data = getLocalData(mime);
        if (data instanceof ByteBuffer) {
            byte[] b = ((ByteBuffer)data).array();
            if (HTML_TYPE.equals(mime)) {
                b = WinHTMLCodec.encode(b);
            }
            return b;
        } else if (data instanceof String) {
            String st = ((String) data).replace("\n", "\r\n");
            if (HTML_TYPE.equals(mime)) {
                try {
                    // NOTE: Transfer of HTML data on Windows uses UTF-8 encoding!
                    byte[] bytes = st.getBytes(WinHTMLCodec.defaultCharset);
                    ByteBuffer ba = ByteBuffer.allocate(bytes.length + 1);
                    ba.put(bytes);
                    ba.put((byte)0);
                    
                    return WinHTMLCodec.encode(ba.array());
                } catch (UnsupportedEncodingException ex) {
                    // never happen
                    return null;
                }
            } else {
                ByteBuffer ba = ByteBuffer.allocate((st.length() + 1) * 2);
                try {
                    ba.put(st.getBytes(defaultCharset));
                } catch (UnsupportedEncodingException ex) {
                    //never happen
                }
                ba.put(terminator);
                return ba.array();
            }
        } else if (FILE_LIST_TYPE.equals(mime)) {
            String[] ast = ((String[]) data);
            if (ast != null && ast.length > 0) {
                int size = 0;
                for (String st : ast) {
                    size += (st.length() + 1) * 2;
                }
                size += 2;
                try {
                    ByteBuffer ba = ByteBuffer.allocate(size);
                    for (String st : ast) {
                        ba.put(st.getBytes(defaultCharset));
                        ba.put(terminator);
                    }
                    ba.put(terminator);
                    return ba.array();
                } catch (UnsupportedEncodingException ex) {
                    //never happen
                }
            }
        } else if (RAW_IMAGE_TYPE.equals(mime)) {
            Pixels pxls = (Pixels)data;
            if (pxls != null) {
                ByteBuffer ba = ByteBuffer.allocate(
                        pxls.getWidth() * pxls.getHeight() * 4 + 8);
                ba.putInt(pxls.getWidth());
                ba.putInt(pxls.getHeight());
                ba.put(pxls.asByteBuffer());
                return ba.array();
            }
        }
        //TODO: customizes for OS specific cases
        return null;
    }

    protected void pushToSystem(HashMap<String, Object> cacheData, int supportedActions) {
        push(cacheData.keySet().toArray(), supportedActions);
    }

    protected native byte[] popBytes(String mime, long index/*-1 for all*/);
    protected Object popFromSystem(String mime) {
        //we have to syncronize with system ones per
        //a popFromSystem function call, because
        //mime type data could be a collection of
        //sub-mimes likes "ms-staff/XXXX"
        if ( !pop() ) {
            return null;
        }
        byte[] data = popBytes(mime, -1);
        if (data != null) {
            if (TEXT_TYPE.equals(mime) || URI_TYPE.equals(mime)) {
                try {
                    // RT-16199 - internal Windows data null terminated
                    return new String(data, 0, data.length - 2, defaultCharset);
                } catch (UnsupportedEncodingException ex) {
                    //never happen
                }
            } else if (HTML_TYPE.equals(mime) || RTF_TYPE.equals(mime) ) {
                try {
                    if (HTML_TYPE.equals(mime)) {
                        data = WinHTMLCodec.decode(data);
                    }
                    return new String(data, 0, data.length, WinHTMLCodec.defaultCharset);
                } catch (UnsupportedEncodingException ex) {
                    //never happen
                }
            } else if (FILE_LIST_TYPE.equals(mime)) {
                try {
                    String st = new String(data, 0, data.length, defaultCharset);
                    return st.split("\0");
                } catch (UnsupportedEncodingException ex) {
                    //never happen
                }
            } else if (RAW_IMAGE_TYPE.equals(mime)) {
                ByteBuffer size = ByteBuffer.wrap(data, 0, 8);
                return Application.GetApplication().createPixels(size.getInt(), size.getInt(),  ByteBuffer.wrap(data, 8, data.length - 8) );
            } else {
                return ByteBuffer.wrap(data);
            }
        } else {
            //alternative extraction if any
            if (URI_TYPE.equals(mime)) {
                String[] ret = (String[])popFromSystem(FILE_LIST_TYPE);
                if (ret != null) {
                    StringBuilder out = new StringBuilder();
                    //"text/uri-list" spec: http://www.ietf.org/rfc/rfc2483.txt
                    for (int i = 0; i < ret.length; i++) {
                        String fileName = ret[i];
                        fileName = fileName.replace("\\", "/");
                        //fileName = fileName.replace(" ", "%20");
                        if (out.length() > 0) {
                            out.append("\r\n");
                        }
                        out.append("file:/").append(fileName);
                    }
                    return out.toString();
                }
            }
        }
        return null;
    }

    protected native String[] popMimesFromSystem();
    protected String[] mimesFromSystem() {
        //we have to syncronize with system
        //if we heed to do it. DnD clipboard need not.
        if (!pop()) {
            return null;
        }
        return popMimesFromSystem();
    }

    @Override public String toString() {
        return "Windows System Clipboard";
    }

    @Override protected void close() {
        dispose();
        ptr = 0L;
    }

    /**
     * @return the ptr
     */
    public long getPtr() {
        return ptr;
    }

    @Override protected native void pushTargetActionToSystem(int actionDone);

    protected native int popSupportedSourceActions();
    @Override protected int supportedSourceActionsFromSystem() {
        if (!pop()) {
            return ACTION_NONE;
        }
        return popSupportedSourceActions();
   }
}

