const Constants = require('../../constants');
const fs = require('fs');
const helpers = require('../../helpers');
const os = require('os');
const path = require('path');

let expectedUnmount = {};
let firstMountCheck = true;
let manualMountDetection = {};
let mountedData = {};
let mountedLocations = [];

const clearManualMountDetection = provider => {
  if (manualMountDetection[provider]) {
    clearInterval(manualMountDetection[provider]);
    delete manualMountDetection[provider];
  }
};

const monitorMount = (sender, provider, providerList, version, pid, location) => {
  manualMountDetection[provider] = setInterval(() => {
    helpers
      .detectRepertoryMounts(version, providerList)
      .then(result => {
        if (result[provider].PID !== pid) {
          if (result[provider].PID === -1) {
            clearManualMountDetection(provider);
            sender.send(Constants.IPC_Unmount_Drive_Reply, {
              data: {
                Expected: expectedUnmount[provider],
                Location: location,
                Provider: provider,
                Error: Error(provider + ' Unmounted').toString(),
                Success: false,
              }
            });
          } else {
            pid = result[provider].PID;
          }
        }
      })
      .catch(e => {
        console.log(e);
      });
  },6000);
};

const unmountAllDrives = () => {
  // Reset mount states
  for (const provider of Object.keys(manualMountDetection)) {
    clearManualMountDetection(provider);
    expectedUnmount[provider] = true;
  }

  // Unmount all items
  for (const mountLocation of mountedLocations) {
    const data = mountedData[mountLocation];
    helpers.stopMountProcessSync(data.Version, data.Provider, data.Remote, data.S3);
  }

  mountedLocations = [];
  mountedData = {};
};

const addListeners = (ipcMain, {setTrayImage, standardIPCReply}) => {
  ipcMain.on(Constants.IPC_Check_Mount_Location + '_sync', (event, data) => {
    let response = {
      Success: true,
      Error: ''
    };

    try {
      if (fs.existsSync(data.Location) && fs.statSync(data.Location).isDirectory()) {
        if (fs.readdirSync(data.Location).length !== 0) {
          response.Success = false;
          response.Error = 'Directory not empty: ' + data.Location;
        }
      } else {
        response.Success = false;
        response.Error = 'Directory not found: ' + data.Location;
      }
    } catch (e) {
      response.Success = false;
      response.Error = e.toString();
    }
    event.returnValue = response;
  });

  ipcMain.on(Constants.IPC_Detect_Mount, (event, data) => {
    const provider = data.Provider;

    let driveLetters = {};
    const providerList = [
      ...Constants.PROVIDER_LIST,
      ...data.RemoteMounts,
      ...data.S3Mounts,
    ];
    for (const provider of providerList) {
      driveLetters[provider] = [];
    }

    const grabDriveLetters = (locations) => {
      for (let i = 'c'.charCodeAt(0); i <= 'z'.charCodeAt(0); i++) {
        const drive = (String.fromCharCode(i) + ':').toUpperCase();
        let driveInUse;
        if (Object.keys(locations).length > 0) {
          for (const provider of providerList) {
            driveInUse = locations[provider].startsWith(drive);
            if (driveInUse)
              break;
          }
        }
        if (!driveInUse) {
          try {
            if (!fs.existsSync(drive)) {
              for (const provider of providerList) {
                driveLetters[provider].push(drive);
              }
            }
          } catch (e) {
          }
        }
      }

      if (Object.keys(locations).length > 0) {
        for (const provider of providerList) {
          if (locations[provider].length > 0) {
            if (!driveLetters[provider].find((driveLetter) => {
              return driveLetter === locations[provider];
            })) {
              driveLetters[provider].push(locations[provider]);
            }
          }
        }
      }
    };

    const setImage = locations => {
      let driveInUse;
      if (Object.keys(locations).length > 0) {
        for (const provider of providerList) {
          driveInUse = locations[provider] && locations[provider].length > 0;
          if (driveInUse)
            break;
        }
      }

      setTrayImage(driveInUse)
    };

    helpers
      .detectRepertoryMounts(data.Version, providerList)
      .then((results) => {
        let storageData = {};
        let locations = {};
        for (const provider of providerList) {
          storageData[provider] = results[provider] ? results[provider] : {
            Active: false,
            Location: '',
            PID: -1,
          };
          locations[provider] = storageData[provider].Location;

          if (storageData[provider].PID !== -1) {
            expectedUnmount[provider] = false;
            if (firstMountCheck) {
              monitorMount(event.sender, provider, providerList, data.Version, storageData[provider].PID, storageData[provider].Location);
            }
          }
        }

        if (os.platform() === 'win32') {
          grabDriveLetters(locations);
        }

        setImage(locations);
        if (firstMountCheck) {
          firstMountCheck = false;
        }
        standardIPCReply(event, Constants.IPC_Detect_Mount_Reply, {
          Active: storageData[provider].Active,
          DriveLetters: driveLetters[provider],
          Location: locations[provider],
          PID: storageData[provider].PID,
          Provider: provider,
        });
      })
      .catch(error => {
        if (os.platform() === 'win32') {
          grabDriveLetters({});
        }
        setImage({});
        standardIPCReply(event, Constants.IPC_Detect_Mount_Reply, {
          DriveLetters: driveLetters[provider],
          Provider: provider,
        }, error);
      });
  });

  ipcMain.on(Constants.IPC_Mount_Drive, (event, data) => {
    expectedUnmount[data.Provider] = false;

    if (mountedLocations.indexOf(data.Location) !== -1) {
      console.log(data.Provider + ' already mounted: ' + data.Location);
    } else {
      mountedLocations.push(data.Location);
      mountedData[data.Location] = {
        Provider: data.Provider,
        Remote: data.Remote,
        S3: data.S3,
        Version: data.Version,
      };
      const errorHandler = (pid, error) => {
        if (mountedLocations.indexOf(data.Location) !== -1) {
          mountedLocations.splice(mountedLocations.indexOf(data.Location), 1);
          delete mountedData[data.Location];
        }

        standardIPCReply(event, Constants.IPC_Unmount_Drive_Reply, {
          Expected: expectedUnmount[data.Provider],
          Location: data.Location,
          Provider: data.Provider,
          Remote: data.Remote,
          S3: data.S3,
        }, error || Error(data.Provider + ' Unmounted'));
      };
      helpers
      .executeMount(data.Version, data.Provider, data.Remote, data.S3, data.Location, (error, pid) => {
        errorHandler(pid, error);
      })
      .then(() => {
        standardIPCReply(event, Constants.IPC_Mount_Drive_Reply, {
          Provider: data.Provider,
          Remote: data.Remote,
          S3: data.S3,
        });
      })
      .catch(error => {
        errorHandler(-1, error);
      });
    }
  });

  ipcMain.on(Constants.IPC_Remove_Remote_Mount, (event, data) => {
    data = data.replace(':', '_');
    const dataDirectory = path.resolve(path.join(helpers.getDataDirectory(), '..', 'remote', data));

    try {
      helpers.removeDirectoryRecursively(dataDirectory);
      standardIPCReply(event, Constants.IPC_Remove_Remote_Mount_Reply, {DataDirectory: dataDirectory});
    } catch (e) {
      standardIPCReply(event, Constants.IPC_Remove_Remote_Mount_Reply, {DataDirectory: dataDirectory}, e);
    }
  });

  ipcMain.on(Constants.IPC_Unmount_All_Drives, event => {
    unmountAllDrives();
    standardIPCReply(event, Constants.IPC_Unmount_All_Drives_Reply);
  });

  ipcMain.on(Constants.IPC_Unmount_Drive, (event, data) => {
    clearManualMountDetection(data.Provider);

    expectedUnmount[data.Provider] = true;
    helpers
    .stopMountProcess(data.Version, data.Provider, data.Remote, data.S3)
      .then(result => {
        console.log(result);
      })
      .catch(e => {
        console.log(e);
      });
  });
};

module.exports = {
  addListeners,
  unmountAllDrives
};
