var EXPORTED_SYMBOLS = ['windowHelper'];

const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");

var jsm = { };
XPCOMUtils.defineLazyGetter(jsm, "utils", function() {
    let obj = { };
    Cu["import"]("resource://livemargins/utils.jsm", obj);
    return obj.utils;
});

XPCOMUtils.defineLazyGetter(jsm, "Services", function() {
    if (jsm.utils.isFFVersionGET4()) {
        let obj = { };
        Cu["import"]("resource://gre/modules/Services.jsm", obj);
        return obj.Services;
    }
    return null;
});

XPCOMUtils.defineLazyGetter(jsm, "ctypes", function() {
    if (jsm.utils.isFFVersionGET4()) {
        let obj = { };
        Cu["import"]("resource://gre/modules/ctypes.jsm", obj);
        return obj.ctypes;
    }
    return null;
});

XPCOMUtils.defineLazyGetter(this, "logger", function() {
    return jsm.utils.logger('LM4.windowJSM');
});

if (jsm.ctypes) {
    const RECT = new jsm.ctypes.StructType('RECT',
        [{left:        jsm.ctypes.int32_t},
         {top:        jsm.ctypes.int32_t},
         {right:    jsm.ctypes.int32_t},
         {bottom:    jsm.ctypes.int32_t}
        ]);

    var ctypesWindowHelper = {
        _user32:        null,
        _findWindow:    null,
        _setWindowPos:    null,
        _getWindowRect:    null,
        _setWindowRgn:    null,
        _const: {
            HWND_TOPMOST:    jsm.ctypes.voidptr_t(-1),
            HWND_NOTOPMOST:    jsm.ctypes.voidptr_t(-2),
            SWP_NOACTIVATE: 0x0010,
            SWP_NOMOVE:        0x0002,
            SWP_NOSIZE:        0x0001,
        },

        _gdi32:                    null,
        _createRoundRectRgn:    null,

        _kernel32:        null,
        _getLastError:    null,

        _init: function() {
            this._user32 = jsm.ctypes.open("user32");

            this._findWindow = this._user32.declare("FindWindowW",
                jsm.ctypes.winapi_abi,
                jsm.ctypes.voidptr_t,    //HWND
                jsm.ctypes.jschar.ptr,    //LPCTSTR
                jsm.ctypes.jschar.ptr);    //LPCTSTR
            this._setWindowPos = this._user32.declare("SetWindowPos",
                jsm.ctypes.winapi_abi,
                jsm.ctypes.bool,
                jsm.ctypes.voidptr_t,    //HWND
                jsm.ctypes.voidptr_t,    //HWND
                jsm.ctypes.int,
                jsm.ctypes.int,
                jsm.ctypes.int,
                jsm.ctypes.int,
                jsm.ctypes.unsigned_int);
            this._getWindowRect = this._user32.declare("GetWindowRect",
                jsm.ctypes.winapi_abi,
                jsm.ctypes.bool,
                jsm.ctypes.voidptr_t,    //HWND
                RECT.ptr);                //LPRECT
            this._setWindowRgn = this._user32.declare("SetWindowRgn",
                jsm.ctypes.winapi_abi,
                jsm.ctypes.int,
                jsm.ctypes.voidptr_t,//HWND
                jsm.ctypes.voidptr_t,//HRGN
                jsm.ctypes.bool);


            this._gdi32 = jsm.ctypes.open("gdi32");

            this._createRoundRectRgn = this._gdi32.declare("CreateRoundRectRgn",
                jsm.ctypes.winapi_abi,
                jsm.ctypes.voidptr_t,//HRGN
                jsm.ctypes.int,
                jsm.ctypes.int,
                jsm.ctypes.int,
                jsm.ctypes.int,
                jsm.ctypes.int,
                jsm.ctypes.int);


            this._kernel32 = jsm.ctypes.open("kernel32");

            this._getLastError = this._kernel32.declare("GetLastError",
                jsm.ctypes.winapi_abi,
                jsm.ctypes.uint32_t);//DWORD
        },
        _close: function() {
            this._user32.close();
            this._gdi32.close();
            this._kernel32.close();
        },
        _error: function(msg) {
            Cu.reportError([msg, this._getLastError()].join(': '));
            this._close();
            return false;
        },

        setTopMost: function(winName, winOnTop) {
            this._init();
            var handle = this._findWindow("MozillaWindowClass", winName);
            if (!handle) {
                return this._error('FindWindow');
            }
            if (!this._setWindowPos(handle,
                    winOnTop ? this._const.HWND_TOPMOST
                                : this._const.HWND_NOTOPMOST,
                    0, 0, 0, 0,
                    this._const.SWP_NOACTIVATE |
                    this._const.SWP_NOMOVE |
                    this._const.SWP_NOSIZE)) {
                return this._error('SetWindowPos');
            }
            this._close();
            return true;
        },
        setRoundWindow: function(winName, winHandle, cornerWidth, cornerHeight) {
            this._init();
            var handle = winHandle ||
                         this._findWindow("MozillaWindowClass", winName);
            if (!handle) {
                return this._error('FindWindow');
            }
            var rect = RECT();
            if (!this._getWindowRect(handle, rect.address())) {
                return this._error('GetWindowRect');
            }
            var width = rect.right - rect.left;
            var height = rect.bottom - rect.top;
            var region = this._createRoundRectRgn(0, 0, width, height,
                                                    cornerWidth, cornerHeight);
            if (!region) {
                return this._error('CreateRoundRectRgn');
            }
            if (!this._setWindowRgn(handle, region, true)) {
                return this._error('SetWindowRgn');
            }
            this._close();
            return handle;
        }
    };
}

var _windowModifier = null;
var _initWMError = false;
function _getWindowModifier() {
    if (_initWMError) {
        return null;
    }
    
    if (null == _windowModifier) {
        try {
        _windowModifier = Cc["@mozillaonline.com/windowmodifier;1"].createInstance().QueryInterface(Ci.IWindowModifier);
        } catch (e) {
            _initWMError = true;
            logger.error(e);
        }
    }
    return _windowModifier;
}

function _getBaseWindow(win) {
    return win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
            .getInterface(Components.interfaces.nsIWebNavigation)
            .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
            .treeOwner
            .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
            .getInterface(Components.interfaces.nsIXULWindow)
            .docShell
            .QueryInterface(Components.interfaces.nsIBaseWindow);
}

var windowHelper = {
    /**
     * Set window border radius
     * @param win
     *     window object
     * @param width
     * @param height
     * @param name
     *     The name of window, is used for finding window handler.
     */
    setRoundRgn: function(win, width, height, name) {
        // if firefox version is higher than 4.0, and os is winnt, then use js-ctype
        if (jsm.utils.isFFVersionGET4() && jsm.Services.appinfo.OS == "WINNT") {
            win.setTimeout(function() {
                var winHandle = ctypesWindowHelper.setRoundWindow(name, false, width, height);
                win.onresize = function() {
                    ctypesWindowHelper.setRoundWindow(null, winHandle, width, height);
                }
            }, 100);
            
        } else {
            // use xpcom
            if (null != _getWindowModifier()) {
                _getWindowModifier().setWindowRoundRgn(_getBaseWindow(win), width, height);
            }
        }
    },
    
    setTopMost: function(win, name, onTop) {
        if (jsm.utils.isFFVersionGET4() && jsm.Services.appinfo.OS == "WINNT") {
            ctypesWindowHelper.setTopMost(name, onTop);
        } else {
            if (null != _getWindowModifier()) {
                if (true == onTop) {
                    _getWindowModifier().setWindowTopMost(_getBaseWindow(win));
                } else {
                    _getWindowModifier().setWindowNoTopMost(_getBaseWindow(win));
                }
            }
        }
    }
}
