/*
 * Copyright (c) 2015 Adobe Systems.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global require, exports */
"use strict";

var CONSTANTS             = require("./USBMUXConstants"),
    USBMUXBinaryProtocol  = require("./USBMUXBinaryProtocol").USBMUXBinaryProtocol,
    plist                 = require("plist"),
    Logger                = require("./Logging");

var logger = Logger.createLogger({name: "USBMUXPlistProtocol", level: "debug"});

/**
 * Initialize a new instance with the given {@link USBMUXSocket}
 *
 * @param {!USBMUXSocket} usbmuxSocket  use this USBMUXSocket
 * @constructor
 */
function USBMUXPlistProtocol(usbmuxSocket) {
    USBMUXBinaryProtocol.call(this, usbmuxSocket);

    logger.debug("created");
}

// make this based on @see {@link USBMUXBinaryProtocol}
USBMUXPlistProtocol.prototype = Object.create(USBMUXBinaryProtocol);
USBMUXPlistProtocol.prototype.constructor = USBMUXPlistProtocol;

/**
 * Return the protocolVersion
 * @readonly
 */
Object.defineProperty(USBMUXPlistProtocol.prototype, "protocolVersion", {
    get: function() { return CONSTANTS.USBMUX_PROTOCOL_VERSION_PLIST; }
});

// Message and payload types
USBMUXPlistProtocol.prototype.TYPE_RESULT         = "Result";
USBMUXPlistProtocol.prototype.TYPE_LISTEN         = "Listen";
USBMUXPlistProtocol.prototype.TYPE_DEVICE_CONNECT = "Connect";
USBMUXPlistProtocol.prototype.TYPE_DEVICE_ADD     = "Attached";
USBMUXPlistProtocol.prototype.TYPE_DEVICE_REMOVE  = "Detached";
USBMUXPlistProtocol.prototype.TYPE_DEVICE_PAIRED  = "Paired";
USBMUXPlistProtocol.prototype.TYPE_PLIST          = 8;

/**
 * Returns a string representation of this instance.
 * @returns {string}
 */
USBMUXPlistProtocol.prototype.toString = function () {
    return "[USBMUXPlistProtocol]";
};

/**
 * Decode a usbmux packet
 *
 * @param {!Buffer} data    the packet data
 *
 * @returns {{messageType: (getDeviceAddedPacket.MessageType|*|getDeviceRemovePacket.MessageType|getInvalidPacket.MessageType|getDevicePairedPacket.MessageType|responseData.MessageType), packetTag: *, packetData: *}}
 */
USBMUXPlistProtocol.prototype.decodePacket = function (data) {
    var resultData = USBMUXBinaryProtocol.prototype.decodePacket.call(this, data);

    if (resultData.response !== this.TYPE_PLIST) {
        // Buffer toJSON is returns different results on node 0.10.x and 0.12.x
        var res = resultData.packetData.toJSON();

        var debugStr = {
            "response": resultData.response,
            "packetLabel": resultData.packetLabel,
            "packetData": res.hasOwnProperty("data") ? res.data : resultData.packetData
        };

        throw new Error("Plist required, but I got this " + JSON.stringify(debugStr));
    }

    var plistPayload = plist.parse(resultData.packetData.toString());
    return {messageType: plistPayload.MessageType, packetTag: resultData.packetTag, packetData: plistPayload};
};

/**
 * Unwrap the content of the packet. This protocol only returns the plain payload, since it's xml.
 *
 * @param {!string} response    the type of response
 * @param {!string} payload the packet payload. This is usually XML for this protocol
 *
 * @returns {string}
 */
USBMUXPlistProtocol.prototype.unwrapPacket = function (response, payload) {
    logger.debug("unwrapPacket");
    return payload;
};

/**
 * Wrap the payload in a proper packet to send to usbmuxd. This protocol will send the payload (xml) without any
 * conversion.
 *
 * @param {!string} request    the type of request
 * @param {!string} payload the packet payload. This is usually XML for this protocol
 *
 * @returns {string}
 */
USBMUXPlistProtocol.prototype.wrapPacket = function (request, payload) {
    logger.debug("wrapPacket");
    return payload;
};

/**
 * Prepare the packet payload and send it off to the usbmuxd.
 *
 * @param {!string} request    the type of request
 * @param {!number} packetTag   number to tag a packet and to verify later that a response for that packetTag was
 * received
 * @param {Object=} payload payload to send to usbmuxd.
 *
 * @returns {boolean}   true, if data was send, false otherwise
 */
USBMUXPlistProtocol.prototype.sendPacket = function (request, packetTag, payload) {
    logger.debug("sendPacket");

    payload = payload || {};

    payload.ClientVersionString = "node-usbmux";

    if (request instanceof Number) {
        request = [this.TYPE_DEVICE_CONNECT, this.TYPE_LISTEN][request - 2];
    }

    payload.BundleID = "com.adobe.webpa.preview.usbmux";
    payload.MessageType = request;
    payload.ProgName = "node-usbmux";

	return USBMUXBinaryProtocol.prototype.sendPacket.call(this, this.TYPE_PLIST, packetTag, plist.build(payload));
};

// API
exports.USBMUXPlistProtocol = USBMUXPlistProtocol;
