text: // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
  // See LICENSE.txt for license information.
  
  // Based on the work done by https://github.com/wcandillon/react-native-expo-image-cache/
  
  import RNFetchBlob from 'rn-fetch-blob';
  
  import {DeviceTypes} from 'app/constants';
  import mattermostBucket from 'app/mattermost_bucket';
  
  import LocalConfig from 'assets/config';
  
  const {IMAGES_PATH} = DeviceTypes;
  
  export default class ImageCacheManager {
      static listeners = {};
  
      static cache = async (filename, uri, listener) => {
          if (!listener) {
              console.warn('Unable to cache image when no listener is provided'); // eslint-disable-line no-console
          }
  
          const {path, exists} = await getCacheFile(filename, uri);
          if (isDownloading(uri)) {
              addListener(uri, listener);
          } else if (exists) {
              listener(path);
          } else {
              addListener(uri, listener);
              if (uri.startsWith('http')) {
                  try {
                      const certificate = await mattermostBucket.getPreference('cert', LocalConfig.AppGroupId);
                      const options = {
                          session: uri,
                          timeout: 10000,
                          indicator: true,
                          overwrite: true,
                          path,
                          certificate,
                      };
  
                      this.downloadTask = await RNFetchBlob.config(options).fetch('GET', uri);
                      if (this.downloadTask.respInfo.respType === 'text') {
                          throw new Error();
                      }
  
                      notifyAll(uri, path);
                  } catch (e) {
                      RNFetchBlob.fs.unlink(path);
                      notifyAll(uri, uri);
                  }
              } else {
                  // In case the uri we are trying to cache is already a local file just notify and return
                  notifyAll(uri, uri);
              }
  
              unsubscribe(uri);
          }
      };
  }
  
  export const getCacheFile = async (name, uri) => {
      const filename = name || uri.substring(uri.lastIndexOf('/'), uri.indexOf('?') === -1 ? uri.length : uri.indexOf('?'));
      const ext = filename.indexOf('.') === -1 ? '.png' : filename.substring(filename.lastIndexOf('.'));
      const path = `${IMAGES_PATH}/${hashCode(uri)}${ext}`;
  
      try {
          const isDir = await RNFetchBlob.fs.isDir(IMAGES_PATH);
          if (!isDir) {
              await RNFetchBlob.fs.mkdir(IMAGES_PATH);
          }
      } catch (error) {
          // do nothing
      }
  
      const exists = await RNFetchBlob.fs.exists(path);
      return {exists, path};
  };
  
  const isDownloading = (uri) => Boolean(ImageCacheManager.listeners[uri]);
  
  const addListener = (uri, listener) => {
      if (!ImageCacheManager.listeners[uri]) {
          ImageCacheManager.listeners[uri] = [];
      }
      ImageCacheManager.listeners[uri].push(listener);
  };
  
  const unsubscribe = (uri) => Reflect.deleteProperty(ImageCacheManager.listeners, uri);
  
  const notifyAll = (uri, path) => {
      ImageCacheManager.listeners[uri].forEach((listener) => {
          if (typeof listener === 'function') {
              listener(path);
          }
      });
  };
  
  // taken from https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery
  const hashCode = (str) => {
      let hash = 0;
      let i;
      let chr;
      if (!str || str.length === 0) {
          return hash;
      }
  
      for (i = 0; i < str.length; i++) {
          chr = str.charCodeAt(i);
          hash = ((hash << 5) - hash) + chr;
          hash |= 0; // Convert to 32bit integer
      }
      return hash;
  };
  