/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';
/* global marionetteScriptFinished, pair, mozContact, i */
/* global waitFor, aContacts */
/* exported pair, discovery, GaiaDataLayer */
/* jshint -W083 */

var GaiaDataLayer = {

  getBluetoothDefaultAdapter: function() {
    var bluetooth = window.navigator.mozBluetooth;
    if (bluetooth.defaultAdapter) {
      return bluetooth.defaultAdapter;
    } else {
      return bluetooth.getAdapters()[0];
    }
  },

  setBluetooth: function(aState) {
    var adapter = this.getBluetoothDefaultAdapter();

    if (adapter.state == aState) {
      console.log('bluetooth already ' + aState);
      marionetteScriptFinished(true);
      return;
    }

    waitFor(
      function() {
        console.log('bluetooth ' + aState);
        marionetteScriptFinished(true);
      },
      function() {
        console.log('bluetooth enable status: ' + adapter.state);

        // Wait for the adapter state to become 'disabled' or 'enabled'
        if (adapter.state === 'disabling' || adapter.state === 'enabling') {
          return false;
        }

        if (adapter.state != aState) {
          console.log('trying to make bluetooth ' + aState);
          if (aState === 'enabled') {
            adapter.enable();
          } else {
            adapter.disable();
          }
          return false;
        }

        return adapter.state === aState;
      }
    );
  },

  disableBluetooth: function() {
    this.setBluetooth('disabled');
  },

  enableBluetooth: function() {
    this.setBluetooth('enabled');
  },

  insertContact: function(aContact) {
    // requires the 'contacts-create' permission
    if (aContact.photo) {
      var blob = GaiaDataLayer.base64ToBlob(aContact.photo, 'image/jpg');
      aContact.photo = [blob];
    }

    var contact = new mozContact(aContact);
    var req = window.navigator.mozContacts.save(contact);
    req.onsuccess = function() {
      console.log('success saving contact');
      marionetteScriptFinished(true);
    };
    req.onerror = function() {
      console.error('error saving contact', req.error.name);
      marionetteScriptFinished(false);
    };
  },

  insertSIMContact: function(aType, aContact) {

    // Get 1st SIM
    var iccId = window.navigator.mozIccManager.iccIds[0];
    var icc = window.navigator.mozIccManager.getIccById(iccId);

    var simContact = new window.mozContact(aContact);
    if ('id' in aContact) {
      simContact.id = aContact.id;
    }

    var req = icc.updateContact(aType, simContact);
    req.onsuccess = function() {
      console.log('success saving contact to SIM');
      marionetteScriptFinished(req.result);
    };
    req.onerror = function() {
      console.error('error saving contact to SIM', req.error.name);
      marionetteScriptFinished(false);
    };
  },


  deleteSIMContact: function(aType, aId) {

    // Get 1st SIM
    var iccId = window.navigator.mozIccManager.iccIds[0];
    var icc = window.navigator.mozIccManager.getIccById(iccId);

    var aContact = new mozContact();
    aContact.id = aId;

    var req = icc.updateContact(aType, aContact);
    req.onsuccess = function() {
      console.log('success removing contact from SIM');
      marionetteScriptFinished(true);
    };
    req.onerror = function() {
      console.error('error removing contact from SIM', req.error.name);
      marionetteScriptFinished(false);
    };
  },


  getAllContacts: function(aCallback) {
    // requires 'contacts-read' permission
    var callback = aCallback || marionetteScriptFinished;
    var req = window.navigator.mozContacts.find({});
    req.onsuccess = function() {
      console.log('success finding contacts');
      callback(req.result);
    };
    req.onerror = function() {
      console.error('error finding contacts ' + req.error.name);
      callback([]);
    };
  },

  getSIMContacts: function(aType, aCallback) {
    var type = aType || 'adn';
    var callback = aCallback || marionetteScriptFinished;
    var icc = navigator.mozIccManager;

    // See bug 932134
    // To keep all tests passed while introducing multi-sim APIs, in bug 928325
    // we do the following check. Remove it after the APIs land.
    if (icc && icc.iccIds && icc.iccIds[0]) {
      icc = icc.getIccById(icc.iccIds[0]);
    }
    var req = icc.readContacts(type);
    req.onsuccess = function() {
      console.log('success finding ' + type + ' contacts');
      callback(req.result);
    };
    req.onerror = function() {
      console.error('error finding ' + type + ' contacts ' + req.error.name);
      callback([]);
    };
  },

  removeAllContacts: function() {
    var self = this;
    this.getAllContacts(function(aContacts) {
      if (aContacts.length > 0) {
        var contactsLength = aContacts.length;
        var done = 0;
        for (var i = 0; i < contactsLength; i++) {
          self.removeContact(aContacts[i], function() {
            if (++done === contactsLength) {
              marionetteScriptFinished(true);
            }
          });
        }
      }
      else {
        console.log('no contacts to remove');
          marionetteScriptFinished(true);
      }
    });
  },

  removeContact: function(aContact, aCallback) {
    // requires the 'contacts-write' persmission
    var callback = aCallback || marionetteScriptFinished;
    console.log('removing contact with id \'' + aContact.id + '\'');
    var req = window.navigator.mozContacts.remove(aContact);
    req.onsuccess = function() {
      console.log('success removing contact with id \'' + aContact.id + '\'');
      callback(true);
    };
    req.onerror = function() {
      console.error('error removing contact with id \'' +
                      aContacts[i].id + '\'');
      callback(false);
    };
  },

  getSetting: function(aName, aCallback) {
    // requires the 'settings-read' and 'settings-api-read' permissions
    var callback = aCallback || marionetteScriptFinished;
    var req = window.navigator.mozSettings.createLock().get(aName);
    req.onsuccess = function() {
      console.log('setting retrieved');
      let result = aName === '*' ? req.result : req.result[aName];
      callback(result);
    };
    req.onerror = function() {
      console.log('error getting setting ' + req.error.name);
    };
  },

  setSetting: function(aName, aValue, aReturnOnSuccess) {
    // requires the 'settings-write' and 'settings-api-write' permissions
    var returnOnSuccess = aReturnOnSuccess || aReturnOnSuccess === undefined;
    var setting = {};
    setting[aName] = aValue;
    console.log('setting ' + aName + ' to ' + aValue);
    var lock = window.navigator.mozSettings.createLock();
    var req = lock.set(setting);
    lock.onsettingstransactionsuccess = function() {
      console.log('setting changed');
      if (returnOnSuccess) {
        marionetteScriptFinished(true);
      }
    };
    lock.onsettingstransactionfailure = function() {
      console.log('error changing setting ' + req.error.name);
      marionetteScriptFinished(false);
    };
  },

  connectToWiFi: function(aNetwork, aCallback) {
    var callback = aCallback || marionetteScriptFinished;
    var manager = window.navigator.mozWifiManager;

    if (this.isWiFiConnected(aNetwork)) {
      console.log('already connected to network with ssid \'' +
                  aNetwork.ssid + '\'');
      callback(true);
    }
    else {
      var req;
      if (window.MozWifiNetwork === undefined) {
        req = manager.associate(aNetwork);
      } else {
        req = manager.associate(new window.MozWifiNetwork(aNetwork));
      }

      req.onsuccess = function() {
        console.log('waiting for connection status \'connected\'');
        waitFor(
          function() {
            console.log('success connecting to network with ssid \'' +
                        aNetwork.ssid + '\'');
            callback(true);
          },
          function() {
            console.log('connection status: ' + manager.connection.status);
            return manager.connection.status === 'connected';
          }
        );
      };

      req.onerror = function() {
        console.log('error connecting to network ' + req.error.name);
        callback(false);
      };
    }
  },

  disableWiFi: function() {
    var manager = window.navigator.mozWifiManager;
    if (manager.enabled) {
      waitFor(
        function() { marionetteScriptFinished(true); },
        function() {
          console.log('wifi enabled status: ' + manager.enabled);
          return manager.enabled === false;
      });
      this.setSetting('wifi.enabled', false, false);
    }
    else {
      console.log('wifi already disabled');
      marionetteScriptFinished(true);
    }
  },

  enableWiFi: function() {
    var manager = window.navigator.mozWifiManager;
    if (!manager.enabled) {
      waitFor(
        function() { marionetteScriptFinished(true); },
        function() {
          console.log('wifi enabled status: ' + manager.enabled);
          return manager.enabled === true;
      });
      this.setSetting('wifi.enabled', true, false);
    }
    else {
      console.log('wifi already enabled');
      marionetteScriptFinished(true);
    }
  },

  forgetAllNetworks: function(aCallback) {
    var callback = aCallback || marionetteScriptFinished;
    var self = this;
    this.getKnownNetworks(function(aNetworks) {
      if (aNetworks.length > 0) {
        var networksLength = aNetworks.length;
        var done = 0;
        for (var i = 0; i < networksLength; i++) {
          self.forgetWiFi(aNetworks[i], function() {
            if (++done === networksLength) {
              callback(true);
            }
          }, false);
        }
      }
      else {
        console.log('no known networks to forget');
        callback(true);
      }
    });
  },

  getKnownNetworks: function(aCallback) {
    var callback = aCallback || marionetteScriptFinished;
    var manager = window.navigator.mozWifiManager;
    var req = manager.getKnownNetworks();

    req.onsuccess = function() {
      console.log('success getting known networks');
      callback(req.result);
    };

    req.onerror = function() {
      console.log('error getting known networks ' + req.error.name);
      callback([]);
    };
  },

  forgetWiFi: function(aNetwork, aCallback, aWaitForStatus) {
    var callback = aCallback || marionetteScriptFinished;
    var waitForStatus = aWaitForStatus || 'disconnected';
    var manager = window.navigator.mozWifiManager;
    var req = manager.forget(aNetwork);

    req.onsuccess = function() {
      console.log('success forgetting network with ssid \'' +
                  aNetwork.ssid + '\'');
      if (waitForStatus !== false) {
        console.log('waiting for connection status \'' +
                    waitForStatus + '\'');
        waitFor(
          function() { callback(true); },
          function() {
            console.log('connection status: ' + manager.connection.status);
            return manager.connection.status === waitForStatus;
          }
        );
      }
      else {
        callback(true);
      }
    };

    req.onerror = function() {
      console.log('error forgetting network with ssid \'' +
                    aNetwork.ssid + '\' ' + req.error.name);
      callback(false);
    };
  },

  isWiFiConnected: function(aNetwork) {
    let manager = window.navigator.mozWifiManager;
    let connected = manager.connection.status === 'connected';
    if (connected && aNetwork) {
      return manager.connection.network.ssid === aNetwork.ssid;
    } else {
      return connected;
    }
  },

  getMozTelephonyState: function() {
    return window.navigator.mozTelephony.active.state;
  },

  connectToCellData: function() {
    var manager = window.navigator.mozMobileConnections &&
                  window.navigator.mozMobileConnections[0];

    if (!manager.data.connected) {
      waitFor(
        function() {
          console.log('cell data enabled');
          marionetteScriptFinished(true);
        },
        function() { return manager.data.connected; }
      );
      this.setSetting('ril.data.enabled', true, false);
    }
    else {
      console.log('cell data already connected');
      marionetteScriptFinished(true);
    }
  },

  disableCellData: function() {
    var self = this;
    this.getSetting('ril.data.enabled', function(aCellDataEnabled) {
      var manager = window.navigator.mozMobileConnections &&
                    window.navigator.mozMobileConnections[0];

      if (aCellDataEnabled) {
        waitFor(
          function() {
            console.log('cell data disabled');
            marionetteScriptFinished(true);
          },
          function() { return !manager.data.connected; }
        );
        self.setSetting('ril.data.enabled', false, false);
      }
      else {
        console.log('cell data already disabled');
        marionetteScriptFinished(true);
      }
    });
  },

  getAllPictures: function() {
    this.getFiles('pictures');
  },

  getAllVideos: function() {
    this.getFiles('videos');
  },

  getAllMusic: function() {
    this.getFiles('music');
  },

  getAllSDCardFiles: function() {
    this.getFiles('sdcard');
  },

  getFiles: function(aType, aCallback) {
    var callback = aCallback || marionetteScriptFinished;
    var files = [];
    console.log('getting ' + aType);
    var storage = navigator.getDeviceStorage(aType);
    var req = storage.enumerate();
    req.onsuccess = function() {
      var file = req.result;
      if (file) {
        if (aType === 'music' && file.name.slice(-4) === '.3gp') {
          // 3gp is both music and video; we skip the music definition
          req.continue();
        }
        else {
          // File.name returns a fully qualified path
          files.push({'name': file.name, 'size': file.size});
          req.continue();
        }
      }
      else {
        callback(files);
      }
    };
    req.onerror = function() {
      console.error('failed to enumerate ' + aType + ' ' + req.error.name);
      callback(false);
    };
  },

  sendSMS: function(recipient, content, skipVerification, aCallback) {
    // requires the 'sms' permission and the 'dom.sms.enabled' pref
    var callback = aCallback || marionetteScriptFinished;
    console.log('sending sms message to number: ' + recipient);

    let messageManager = window.navigator.mozMobileMessage;
    let request = messageManager.send(recipient, content);
    if (skipVerification) {
      marionetteScriptFinished(true);
      return;
    }

    request.onsuccess = function(event) {
      var sms = event.target.result;

      waitFor(
        function() { callback(true); },
        function() {
          console.log('sms delivery state: ' + sms.delivery);
          return sms.delivery === 'sent';
        }
      );
    };

    request.onerror = function() {
      console.log('sms message not sent');
      callback(false);
    };
  },

  getAllSms: function(aCallback) {
    // requires the 'sms' permission and the 'dom.sms.enabled' pref
    var callback = aCallback || marionetteScriptFinished;
    console.log('searching for sms messages');

    let sms = window.navigator.mozMobileMessage;

    let msgList = [];
    let cursor = sms.getMessages(null, false);

    cursor.onsuccess = function(event) {
      if(cursor.result) {
        // Add the sms to the list
        msgList.push(cursor.result);
        // Now get the next in the list
        cursor.continue();
      }else{
        // Send back the list
        callback(msgList);
      }
    };

    cursor.onerror = function(event) {
      console.log('sms.getMessages error: ' + event.target.error.name);
      callback(false);
    };
  },

  deleteAllSms: function(aCallback) {
    // requires the 'sms' permission and the 'dom.sms.enabled' pref
    var callback = aCallback || marionetteScriptFinished;
    console.log('searching for sms messages');

    let sms = window.navigator.mozMobileMessage;

    let msgList = [];
    let cursor = sms.getMessages(null, false);

    cursor.onsuccess = function(event) {
      // Check if message was found
      if (cursor.result) {
        msgList.push(cursor.result.id);
        // Now get next message in the list
        cursor.continue();
      } else {
        // No (more) messages found
        if (msgList.length) {
          console.log('found ' + msgList.length + ' sms messages to delete');
          deleteSmsMsgs(msgList);
        } else {
          console.log('zero sms messages found');
          callback(true);
        }
      }
    };

    cursor.onerror = function(event) {
      console.log('sms.getMessages error: ' + event.target.error.name);
      callback(false);
    };

    function deleteSmsMsgs(msgList) {
      let smsId = msgList.shift();
      console.log('deleting sms id: ' + smsId);
      let request = sms.delete(smsId);

      request.onsuccess = function(event) {
        if (event.target.result) {
          // Message deleted, continue until none are left
          if (msgList.length) {
            deleteSmsMsgs(msgList);
          } else {
            // All messages deleted
            console.log('finished deleting all sms messages');
            callback(true);
          }
        } else {
          console.log('sms delete failed');
          callback(false);
        }
      };

      request.onerror = function(event) {
        console.log('sms.delete request returned unexpected error: ' +
                    event.target.error.name);
        callback(false);
      };
    }
  },

  deleteAllAlarms: function() {
    window.wrappedJSObject.AlarmManager.getAlarmList(function(aList) {
      aList.forEach(function(aAlarm) {
         console.log('Deleting alarm with id  \'' + aAlarm.id + '\'');
         window.wrappedJSObject.AlarmManager.delete(aAlarm);
      });
    });
  },

  base64ToBlob: function(base64, mimeType) {
      var binary = atob(base64);
      var len = binary.length;
      var buffer = new ArrayBuffer(len);
      var view = new Uint8Array(buffer);
      for (var i = 0; i < len; i++) {
        view[i] = binary.charCodeAt(i);
      }
      return new Blob([view], {type: mimeType});
  }
};
