<commit_before>/* The main background script, containing listeners for communication, aggregating
 * the data and storing it.
 *
 * @author <NAME> (@bgotink)
 * @license MIT
 */

import { Status, ManifestInformation, ExportData } from 'ingress-identity';
import _ from 'lodash';
import * as log from './log';
import Cache from './background/cache';
import { getPrefixedMessages } from './background/i18n';

import DataManager from './data/index';
import TokenBearer from './data/token';

const optionsPage = `chrome-extension://${chrome.runtime.id}/options.html`;
const optionsPageRegexp = new RegExp(`chrome-extension://${chrome.runtime.id}/options.html`);

const tokenBearer = new TokenBearer();
let data = new DataManager(tokenBearer, []);

/*
 * Storage functions
 */

// Use sync storage to sync the stored data between chrome instances for the logged on user
const storage = chrome.storage.sync;
const storageCache = new Cache<any>();

let disableUpdateListener = false;
chrome.storage.onChanged.addListener(function onDataUpdated(changes) {
  if (disableUpdateListener) {
    log.log('Chrome storage update listener was disabled, re-enabling');
    disableUpdateListener = false;
    return;
  }

  let reload = false, update = false;

  log.log('Settings updated:');
  _.forEach(changes, (value, key) => {
    if (storageCache.has(key)) {
      storageCache.remove(key);
    }

    log.log(`- %s`, key);

    if (key === 'manifest_keys') {
      reload = true;
    }

    update = true;
  });

  if (reload) {
    reloadData();
  } else if (update) {
    updateTabs();
  }
});

async function getStoredData<T>(key: string, defaultValue: T): Promise<T> {
  if (storageCache.has(key)) {
    const result = await storageCache.get(key) as Promise<T>;
    log.log('Got data { key: %s, value: %s } from storage cache', key, `${result}`);
    return result;
  }

  const result = new Promise<T>((resolve, reject) => {
    storage.get({ [key]: defaultValue }, (result) => {
      if (chrome.runtime.lastError) {
        return reject(chrome.runtime.lastError);
      }
      log.log('Got data { key: %s, value: %s } from storage', key, `${result[key]}`);
      resolve(result[key] as T);
    });
  });

  // Store this in the cache, but remove it if errored
  storageCache.set(key, result);
  result.catch(() => storageCache.remove(key));

  return result;
}

async function setStoredData<T>(key: string, value: T): Promise<void> {
  log.log('Setting storage key %s to %s', key, value);
  return new Promise<void>((resolve, reject) =>
    storage.set({ [key]: value }, () => {
      if (chrome.runtime.lastError) {
        return reject(chrome.runtime.lastError.message);
      }

      resolve();
    })
  );
}

async function setStoredDatas(data: { [s: string]: any }): Promise<void> {
  log.log('Settings multiple storage values:', data);
  return new Promise<void>((resolve, reject) => {
    storage.set(data, () => {
      if (chrome.runtime.lastError) {
        return reject(chrome.runtime.lastError.message);
      }

      resolve();
    });
  });
}

async function getManifestKeys(): Promise<string[]> {
  log.log('Fetching manifest keys...');
  return new Promise<string[]>((resolve, reject) => {
    storage.get({ manifest_keys: [] }, (result: { manifest_keys: string[] }) => {
      if (chrome.runtime.lastError) {
        return reject(chrome.runtime.lastError.message);
      }

      resolve(result.manifest_keys);
    });
  });
}

async function setManifestKeys(keys: string[]): Promise<void> {
  log.log('Setting manifest keys...');
  return new Promise<void>((resolve, reject) => {
    storage.set({ manifest_keys: keys }, () => {
      if (chrome.runtime.lastError) {
        return reject(chrome.runtime.lastError.message);
      }

      resolve();
    })
  });
}

async function addManifestKey(key: string, name: string): Promise<boolean> {
  const keys = await getManifestKeys();
  
  if (keys.includes(key)) {
    log.log('manifest key %s already loaded', key);
    return false;
  }

  log.log('adding manifest key %s', key);
  keys.push(key);

  await setManifestKeys(keys);

  if (_.isEmpty(name)) {
    return true;
  }

  const names = await getStoredData<{ [s: string]: string }>('manifest_names', {});
  names[key] = name;
  await setStoredData('manifest_names', names);

  return true;
}

async function removeManifestKey(key: string): Promise<boolean> {
  const keys = await getManifestKeys();
  const idx = keys.indexOf(key);

  if (idx === -1) {
    return false;
  }

  keys.splice(idx, 1);
  await setManifestKeys(keys);

  return true;
}

async function renameManifest(key: string, oldName?: string, newName?: string): Promise<boolean> {
  if (_.isEmpty(oldName) && _.isEmpty(newName)) {
    return true;
  }

  if (`${oldName}`.trim() === `${newName}`.trim()) {
    return true;
  }

  const names = await getStoredData<{ [s: string]: string }>('manifest_names', {});

  // Verify that there's been no change in the mean time
  if (_.isEmpty(oldName)) {
    if (names[key]) {
      return false;
    }
  } else {
    if (names[key] !== oldName) {
      return false;
    }
  }

  if (_.isEmpty(newName)) {
    delete names[key];
  } else {
    names[key] = newName;
  }

  await setStoredData('manifest_names', names);
  storageCache.remove('manifests');

  return true;
}

async function changeManifestOrder(oldOrder: string[], newOrder: string[]): Promise<Status> {
  const currentOrder = await getManifestKeys();
  const { length } = currentOrder;

  if (oldOrder.length !== length || newOrder.length !== length) {
    // What the hell is happening? Abort, abort!
    return 'failed';
  }

  // Check if the old order is the current order (make sure no intermediary changes have been made)
  for (let i = 0; i < length; i++) {
    if (oldOrder[i] !== currentOrder[i]) {
      return 'failed';
    }
  }

  // Verify that the contents of the two orders are the same except for the order of the elements
  for (let i = 0; i < length; i++) {
    if (!oldOrder.includes(newOrder[i]) || !newOrder.includes(oldOrder[i])) {
      return 'failed';
    }
  }

  await setManifestKeys(newOrder);
  return 'success';
}

/*
 * Data functions
 */

async function reloadData(): Promise<Status> {
  const keys = await getManifestKeys();
  const newData = new DataManager(tokenBearer, keys);

  await newData.ready();

  data = newData;

  updateTabs();
  return (await data.hasErrors()) ? 'success' : 'warning';
}

/*
 * Communication functions
 */<commit_msg>[background] Send warning if errors, success if not instead of the other way around<commit_after>/* The main background script, containing listeners for communication, aggregating
 * the data and storing it.
 *
 * @author <NAME> (@bgotink)
 * @license MIT
 */

import { Status, ManifestInformation, ExportData } from 'ingress-identity';
import _ from 'lodash';
import * as log from './log';
import Cache from './background/cache';
import { getPrefixedMessages } from './background/i18n';

import DataManager from './data/index';
import TokenBearer from './data/token';

const optionsPage = `chrome-extension://${chrome.runtime.id}/options.html`;
const optionsPageRegexp = new RegExp(`chrome-extension://${chrome.runtime.id}/options.html`);

const tokenBearer = new TokenBearer();
let data = new DataManager(tokenBearer, []);

/*
 * Storage functions
 */

// Use sync storage to sync the stored data between chrome instances for the logged on user
const storage = chrome.storage.sync;
const storageCache = new Cache<any>();

let disableUpdateListener = false;
chrome.storage.onChanged.addListener(function onDataUpdated(changes) {
  if (disableUpdateListener) {
    log.log('Chrome storage update listener was disabled, re-enabling');
    disableUpdateListener = false;
    return;
  }

  let reload = false, update = false;

  log.log('Settings updated:');
  _.forEach(changes, (value, key) => {
    if (storageCache.has(key)) {
      storageCache.remove(key);
    }

    log.log(`- %s`, key);

    if (key === 'manifest_keys') {
      reload = true;
    }

    update = true;
  });

  if (reload) {
    reloadData();
  } else if (update) {
    updateTabs();
  }
});

async function getStoredData<T>(key: string, defaultValue: T): Promise<T> {
  if (storageCache.has(key)) {
    const result = await storageCache.get(key) as Promise<T>;
    log.log('Got data { key: %s, value: %s } from storage cache', key, `${result}`);
    return result;
  }

  const result = new Promise<T>((resolve, reject) => {
    storage.get({ [key]: defaultValue }, (result) => {
      if (chrome.runtime.lastError) {
        return reject(chrome.runtime.lastError);
      }
      log.log('Got data { key: %s, value: %s } from storage', key, `${result[key]}`);
      resolve(result[key] as T);
    });
  });

  // Store this in the cache, but remove it if errored
  storageCache.set(key, result);
  result.catch(() => storageCache.remove(key));

  return result;
}

async function setStoredData<T>(key: string, value: T): Promise<void> {
  log.log('Setting storage key %s to %s', key, value);
  return new Promise<void>((resolve, reject) =>
    storage.set({ [key]: value }, () => {
      if (chrome.runtime.lastError) {
        return reject(chrome.runtime.lastError.message);
      }

      resolve();
    })
  );
}

async function setStoredDatas(data: { [s: string]: any }): Promise<void> {
  log.log('Settings multiple storage values:', data);
  return new Promise<void>((resolve, reject) => {
    storage.set(data, () => {
      if (chrome.runtime.lastError) {
        return reject(chrome.runtime.lastError.message);
      }

      resolve();
    });
  });
}

async function getManifestKeys(): Promise<string[]> {
  log.log('Fetching manifest keys...');
  return new Promise<string[]>((resolve, reject) => {
    storage.get({ manifest_keys: [] }, (result: { manifest_keys: string[] }) => {
      if (chrome.runtime.lastError) {
        return reject(chrome.runtime.lastError.message);
      }

      resolve(result.manifest_keys);
    });
  });
}

async function setManifestKeys(keys: string[]): Promise<void> {
  log.log('Setting manifest keys...');
  return new Promise<void>((resolve, reject) => {
    storage.set({ manifest_keys: keys }, () => {
      if (chrome.runtime.lastError) {
        return reject(chrome.runtime.lastError.message);
      }

      resolve();
    })
  });
}

async function addManifestKey(key: string, name: string): Promise<boolean> {
  const keys = await getManifestKeys();
  
  if (keys.includes(key)) {
    log.log('manifest key %s already loaded', key);
    return false;
  }

  log.log('adding manifest key %s', key);
  keys.push(key);

  await setManifestKeys(keys);

  if (_.isEmpty(name)) {
    return true;
  }

  const names = await getStoredData<{ [s: string]: string }>('manifest_names', {});
  names[key] = name;
  await setStoredData('manifest_names', names);

  return true;
}

async function removeManifestKey(key: string): Promise<boolean> {
  const keys = await getManifestKeys();
  const idx = keys.indexOf(key);

  if (idx === -1) {
    return false;
  }

  keys.splice(idx, 1);
  await setManifestKeys(keys);

  return true;
}

async function renameManifest(key: string, oldName?: string, newName?: string): Promise<boolean> {
  if (_.isEmpty(oldName) && _.isEmpty(newName)) {
    return true;
  }

  if (`${oldName}`.trim() === `${newName}`.trim()) {
    return true;
  }

  const names = await getStoredData<{ [s: string]: string }>('manifest_names', {});

  // Verify that there's been no change in the mean time
  if (_.isEmpty(oldName)) {
    if (names[key]) {
      return false;
    }
  } else {
    if (names[key] !== oldName) {
      return false;
    }
  }

  if (_.isEmpty(newName)) {
    delete names[key];
  } else {
    names[key] = newName;
  }

  await setStoredData('manifest_names', names);
  storageCache.remove('manifests');

  return true;
}

async function changeManifestOrder(oldOrder: string[], newOrder: string[]): Promise<Status> {
  const currentOrder = await getManifestKeys();
  const { length } = currentOrder;

  if (oldOrder.length !== length || newOrder.length !== length) {
    // What the hell is happening? Abort, abort!
    return 'failed';
  }

  // Check if the old order is the current order (make sure no intermediary changes have been made)
  for (let i = 0; i < length; i++) {
    if (oldOrder[i] !== currentOrder[i]) {
      return 'failed';
    }
  }

  // Verify that the contents of the two orders are the same except for the order of the elements
  for (let i = 0; i < length; i++) {
    if (!oldOrder.includes(newOrder[i]) || !newOrder.includes(oldOrder[i])) {
      return 'failed';
    }
  }

  await setManifestKeys(newOrder);
  return 'success';
}

/*
 * Data functions
 */

async function reloadData(): Promise<Status> {
  const keys = await getManifestKeys();
  const newData = new DataManager(tokenBearer, keys);

  await newData.ready();

  data = newData;

  updateTabs();
  return data.hasErrors() ? 'warning' : 'success';
}

/*
 * Communication functions
 */
{'fraction_non_alphanumeric': 0.15439484583525082, 'fraction_numerical': 0.0009203865623561896, 'mean_word_length': 3.4915604547020322, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 71, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6096480', 'n_tokens_mistral': 4125, 'n_tokens_neox': 4046, 'n_words': 1108}