"use strict";

const fs = require("fs"),
  Promise = require("bluebird"),
  config = require("config");

if (process.argv.length <= 2) {
  console.log("usage: node check file|id [file|id] ... ");
  process.exit(-1);
}
const items = process.argv.splice(2);

const picturesPath = config.get("picturesPath").replace(/\/$/, "") + "/";

for (let i = 0; i < items.length; i++) {
  if (parseInt(items[i]) == items[i]) {
    items[i] = {
      type: "id",
      value: items[i]
    };
  } else {
    items[i] = {
      type: "file",
      value: items[i]
    }
  }
}

const exists = function(path) {
  return stat(path).then(function(stats) {
    return stats;
  }).catch(function() {
    return null;
  });
}

const stat = function (_path) {
  if (_path.indexOf(picturesPath.replace(/\/$/, "")) == 0) {
    _path = _path.substring(picturesPath.length);
  }

  let path = picturesPath + _path;

  return new Promise(function (resolve, reject) {
    fs.stat(path, function (error, stats) {
      if (error) {
        return reject(error);
      }
      return resolve(stats);
    });
  });
}

const imageExtensions = [ "jpg", "jpeg", "png", "gif", "nef" ],
  imageRegExp = new RegExp("\.((" + imageExtensions.join(")|(") + "))$", "i"),
  videoExtensions = [ "mov", "avi", "mp4", "webm" ],
  videoRegExp = new RegExp("\.((" + videoExtensions.join(")|(") + "))$", "i");

function getAssetInfoFromDisk(asset, filepath) {
  return exists(filepath).then(function(stats) {
    asset.filepath = filepath;

    if (stats) {
      asset.stats = stats;
    }

    /* Check if entry exists in DB */
    let parts = /^(.*)\/([^/]+)$/.exec(filepath);
    if (parts) {
      asset.path = parts[1] + "/";
      asset.filename = parts[2];
    } else {
      asset.path = "";
      asset.filename = filepath;
    }

    asset.name = asset.filename.replace(/\.[^.]*$/, "");
console.log(asset);
    return photoDB.sequelize.query("SELECT photos.*,(albums.path || photos.filename) AS filepath " +
      "FROM photos LEFT JOIN albums ON albums.id=photos.albumId " +
      "WHERE albums.path=:path AND photos.filename=:filename", {
      replacements: asset,
      type: photoDB.Sequelize.QueryTypes.SELECT,
      raw: true
    }).then(function(photos) {
      if (photos.length == 1) {
        Object.assign(asset, photos[0]);
      }

      return photoDB.sequelize.query("SELECT * FROM albums WHERE path=:path", {
        replacements: asset,
        type: photoDB.Sequelize.QueryTypes.SELECT,
        raw: true
      }).then(function(albums) {
        if (albums.length == 1) {
          asset.album = albums[0];
          asset.albumId = asset.album.id;
        }
      });
    });
  }).then(function() {
    return asset;
  });
}

function getAssetInfoFromDB(asset, id) {
  return photoDB.sequelize.query("SELECT photos.*,(albums.path || photos.filename) AS filepath " +
    "FROM photos LEFT JOIN albums ON albums.id=photos.albumId " +
    "WHERE photos.id=:id", {
    replacements: {
      id: id
    },
    type: photoDB.Sequelize.QueryTypes.SELECT,
    raw: true
  }).then(function(photos) {
    if (photos.length == 1) {
      Object.assign(asset, photos[0]);
    }
  }).then(function() {
    if (!asset.filepath) {
      return;
    }
    
    return exists(asset.filepath).then(function(stats) {
      if (stats) {
        asset.stats = stats;
      }
    });
  }).then(function() {
    return asset;
  });
}

function computeHash(filepath) {
  return new Promise(function(resolve, reject) {
    let input = fs.createReadStream(filepath),
      hash = crypto.createHash("sha256");
    if (!input) {
      return reject()
    }

    input.on("readable", function() {
      const data = input.read();
      if (data) {
        hash.update(data);
      } else {
        input.close();
        resolve(hash.digest("hex"));
        hash = null;
        input = null;
      }
    });
  });
}

let photoDB;

require("../server/db/photos").then(function(db) {
  photoDB = db;

  return Promise.mapSeries(items, function(item) {
    const asset = {};

    console.log("Processing " + item.value);
    return Promise.resolve().then(function() {
      switch (item.type) {
      case "file":
        return getAssetInfoFromDisk(asset, item.value);
        break;

      case "id":
        return getAssetInfoFromDB(asset, item.value);
        break;
      }
    }).then(function() {
      if (!asset.stats && !asset.id) {
        throw "...exists neither on disk nor in the DB.";
      }
    }).then(function() {
      if (asset.id) {
        return;
      }

      console.log("...does not exist in the DB.");

      asset.size = asset.stats.size;
      asset.needsHash = true;
      asset.needsUpdate = true;
      return photoDB.sequelize.query("INSERT INTO photos " +
        "(albumId,filename,name,size) VALUES(:albumId,:filename,:name,:size)", {
        replacements: asset
      }).spread(function(results, metadata) {
        asset.id = metadata.lastID;
      });
    }).then(function() {
      if (asset.stats) {
        if (asset.modified < asset.stats.mtime) {
          asset.modified = asset.stats.mtime;
          asset.needsUpdate = true;
          asset.needsHash = true;
        }

        if (asset.deleted) {
          console.log("...setting deleted=0 for " + asset.id);
          return photoDB.sequelize.query("UPDATE photos SET deleted=0 WHERE id=:id", {
            replacements: asset
          });
        }
        return;
      }

      console.log("...does not exist on disk.");
      
      if (!asset.deleted) {
        console.log("...setting deleted=1 for " + asset.id);
        return photoDB.sequelize.query("UPDATE photos SET deleted=1 WHERE id=:id", {
          replacements: asset
        });
      }
    }).then(function() {
      if (!asset.needsHash) {
        return;
      }

      return computeHash(asset.path + asset.filename).then(function(hash) {
        asset.hash = hash;
      }).then(function() {
        return photoDB.sequelize.query("SELECT photos.id,photohashes.*,photos.filename,albums.path FROM photohashes " +
          "LEFT JOIN photos ON (photos.id=photohashes.photoId) " +
          "LEFT JOIN albums ON (albums.id=photos.albumId) " +
          "WHERE hash=:hash OR photoId=:id", {
          replacements: asset,
          type: photoDB.sequelize.QueryTypes.SELECT
        }).then(function(results) {
          let query;
  
          if (results.length == 0) {
            query = "INSERT INTO photohashes (hash,photoId) VALUES(:hash,:id)";
            console.log("...creating photohash.");
          } else if (results[0].hash != asset.hash) {
            query = "UPDATE photohashes SET hash=:hash WHERE photoId=:id";
            console.log("...updating photohash.");
          } else if (results[0].photoId != asset.id) {
            console.log("...duplicate asset: " + 
              "'" + asset.album.path + asset.filename + "' is a copy of " +
              "'" + results[0].path + results[0].filename + "'");
            asset.duplicate = results[0].id;
          }

          if (!query) {
            return asset;
          }
  
          return photoDB.sequelize.query(query, {
            replacements: asset,
          });
        });
      });
    }).then(function() {
      if (asset.deleted) {
        throw "...deleted. Skipping rest of processing.";
      }
      if (asset.duplicate) {
        throw "...duplicate of " + asset.duplicate + ". Skipping rest of processing.";
      }

      return exists(asset.path + "thumbs/" + asset.filename).then(function(stats) {
        if (!stats || stats.mtime < asset.modified) {
          console.log("...needs thumb");
          asset.needsThumb = true;
        }
      });
    }).then(function() {
      return exists(asset.path + "thumbs/scaled/" + asset.filename).then(function(stats) {
        if (!stats || stats.mtime < asset.modified) {
          console.log("...needs scaled");
          asset.needsScaled = true;
        }
      });
    }).then(function() {
      if (asset.needsUpdate) {
        console.log("...DB needs to be updated for " + item.value);
      } else {
        console.log("...does not need to be updated.");
      }
    }).catch(function(error) {
      console.log(error);
    });
  });
});
