/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

let Cu = SpecialPowers.wrap(Components).utils;
let RIL = {};

Cu.import("resource://gre/modules/ril_consts.js", RIL);

// Only bring in what we need from ril_worker/RadioInterfaceLayer here. Reusing
// that code turns out to be a nightmare, so there is some code duplication.
let PDUBuilder = {
  toHexString: function toHexString(n, length) {
    let str = n.toString(16);
    if (str.length < length) {
      for (let i = 0; i < length - str.length; i++) {
        str = "0" + str;
      }
    }
    return str.toUpperCase();
  },

  writeUint16: function writeUint16(value) {
    this.buf += (value & 0xff).toString(16).toUpperCase();
    this.buf += ((value >> 8) & 0xff).toString(16).toUpperCase();
  },

  writeHexOctet: function writeHexOctet(octet) {
    this.buf += this.toHexString(octet, 2);
  },

  writeSwappedNibbleBCD: function writeSwappedNibbleBCD(data) {
    data = data.toString();
    let zeroCharCode = '0'.charCodeAt(0);

    for (let i = 0; i < data.length; i += 2) {
      let low = data.charCodeAt(i) - zeroCharCode;
      let high;
      if (i + 1 < data.length) {
        high = data.charCodeAt(i + 1) - zeroCharCode;
      } else {
        high = 0xF;
      }

      this.writeHexOctet((high << 4) | low);
    }
  },

  writeStringAsSeptets: function writeStringAsSeptets(message,
                                                      paddingBits,
                                                      langIndex,
                                                      langShiftIndex)
  {
    const langTable = RIL.PDU_NL_LOCKING_SHIFT_TABLES[langIndex];
    const langShiftTable = RIL.PDU_NL_SINGLE_SHIFT_TABLES[langShiftIndex];

    let dataBits = paddingBits;
    let data = 0;
    for (let i = 0; i < message.length; i++) {
      let septet = langTable.indexOf(message[i]);
      if (septet == RIL.PDU_NL_EXTENDED_ESCAPE) {
        continue;
      }

      if (septet >= 0) {
        data |= septet << dataBits;
        dataBits += 7;
      } else {
        septet = langShiftTable.indexOf(message[i]);
        if (septet == -1) {
          throw new Error(message[i] + " not in 7 bit alphabet "
                          + langIndex + ":" + langShiftIndex + "!");
        }

        if (septet == RIL.PDU_NL_RESERVED_CONTROL) {
          continue;
        }

        data |= RIL.PDU_NL_EXTENDED_ESCAPE << dataBits;
        dataBits += 7;
        data |= septet << dataBits;
        dataBits += 7;
      }

      for (; dataBits >= 8; dataBits -= 8) {
        this.writeHexOctet(data & 0xFF);
        data >>>= 8;
      }
    }

    if (dataBits != 0) {
      this.writeHexOctet(data & 0xFF);
    }
  },

  buildAddress: function buildAddress(address) {
    let addressFormat = RIL.PDU_TOA_ISDN; // 81
    if (address[0] == '+') {
      addressFormat = RIL.PDU_TOA_INTERNATIONAL | RIL.PDU_TOA_ISDN; // 91
      address = address.substring(1);
    }

    this.buf = "";
    this.writeHexOctet(address.length);
    this.writeHexOctet(addressFormat);
    this.writeSwappedNibbleBCD(address);

    return this.buf;
  },

  // assumes 7 bit encoding
  buildUserData: function buildUserData(options) {
    let headerLength = 0;
    this.buf = "";
    if (options.headers) {
      for each (let header in options.headers) {
        headerLength += 2; // id + length octets
        if (header.octets) {
          headerLength += header.octets.length;
        }
      };
    }

    let encodedBodyLength = options.body.length;
    let headerOctets = (headerLength ? headerLength + 1 : 0);

    let paddingBits;
    let userDataLengthInSeptets;
    let headerSeptets = Math.ceil(headerOctets * 8 / 7);
    userDataLengthInSeptets = headerSeptets + encodedBodyLength;
    paddingBits = headerSeptets * 7 - headerOctets * 8;

    this.writeHexOctet(userDataLengthInSeptets);
    if (options.headers) {
      this.writeHexOctet(headerLength);

      for each (let header in options.headers) {
        this.writeHexOctet(header.id);
        this.writeHexOctet(header.length);

        if (header.octets) {
          for each (let octet in header.octets) {
            this.writeHexOctet(octet);
          }
        }
      }
    }

    this.writeStringAsSeptets(options.body, paddingBits,
                              RIL.PDU_NL_IDENTIFIER_DEFAULT,
                              RIL.PDU_NL_IDENTIFIER_DEFAULT);
    return this.buf;
  }
};
