// @flow

// Copyright 2016 Attic Labs, Inc. All rights reserved.
// Licensed under the Apache License, version 2.0:
// http://www.apache.org/licenses/LICENSE-2.0

import argv from 'yargs';
import {
  DatasetSpec,
  invariant,
  isSubtype,
  makeListType,
  makeStructType,
  Map,
  newStruct,
  numberType,
  Set,
  stringType,
  Struct,
  walk,
} from '@attic/noms';

const args = argv
  .usage(
    'Finds photos in slurped Facebook metadata\n\n' +
    'Usage: fb/find-photos <in-object> <out-dataset>')
  .demand(2)
  .argv;

main().catch(ex => {
  console.error(ex);
  process.exit(1);
});

const imageType = makeStructType('',
  ['height', 'source', 'width'],
  [
    numberType,
    stringType,
    numberType,
  ]
);

const photoType = makeStructType('',
  ['id', 'images'],
  [
    stringType,
    makeListType(imageType),
  ]
);

const placeType = makeStructType('',
  ['place'],
  [
    makeStructType('',
      ['location'],
      [
        makeStructType('',
          ['latitude', 'longitude'],
          [
            numberType,
            numberType,
          ]
        ),
      ]
    ),
  ]
);

async function main(): Promise<void> {
  const inSpec = DatasetSpec.parse(args._[0]);
  const outSpec = DatasetSpec.parse(args._[1]);
  if (!inSpec) {
    throw 'invalid input object spec';
  }
  if (!outSpec) {
    throw 'inalid output dataset spec';
  }

  const input = await inSpec.value();
  const output = outSpec.dataset();
  let result = Promise.resolve(new Set());

  // TODO: progress
  await walk(input.photos, inSpec.database.database(), async (v: any) => {
    if (v instanceof Struct && isSubtype(photoType, v.type)) {
      const photo: Object = {
        title: v.name || '',
        sizes: await getSizes(v),
        tags: new Set(),  // fb has 'tags', but they are actually people not textual tags
      };
      if (isSubtype(placeType, v.type)) {
        photo.geoposition = getGeo(v);
      }
      result = result.then(r => r.add(newStruct('Photo', photo)));
      return true;
    }
  });

  return output.commit(await result).then();
}

function getGeo(input: Struct): Struct {
  invariant(input.place);
  return newStruct('Geoposition', {
    latitude: input.place.location.latitude,
    longitude: input.place.location.longitude,
  });
}

async function getSizes(input: Struct): Promise<Map<Struct, string>> {
  let result = Promise.resolve(new Map());
  invariant(input.images);
  await input.images.forEach(v => {
    result = result.then(m => m.set(
      newStruct('', {width: v.width, height: v.height}),
      v.source));
  });
  return result;
}
