import * as React from 'react';
import { Dimmer, Loader } from 'semantic-ui-react';
import * as XML from 'xml2js';
import * as _ from 'lodash';
import { LexEditor } from './LexEditor';

function simpleAccessor(lens: string, def: string = '') {
  return {
    get: (li: any) => {
      let val = _.get<any>(li, lens, def);
      return val;
    },
    set: (li: any, value: string) => {
      let lexItem = _.cloneDeep(li);
      let ret = _.set<any>(lexItem, lens, value);
      return ret;
    }
  };
}

const fieldMetaMap = {
  label: {
    type: 'text',
    ...simpleAccessor('label[0]'),
  },
  unl: { type: 'text', ...simpleAccessor('unl[0]'), },
  synset: { type: 'text', ...simpleAccessor('lexprops[0].wnsynset[0]'), },
  guid: { type: 'text', ...simpleAccessor('guid[0]'), },
  pos: { type: 'select', ...simpleAccessor('pos[0]'), },
  image: { type: 'text', ...simpleAccessor('image[0]'), },
  relations: { type: 'text', ...simpleAccessor('relations[0]'), },
  frame: { type: 'select', ...simpleAccessor('syntacticprops[0].property[0]._'), },
  morphclass: {
    type: 'select',
    get: (li: any) => {
      let lens = 'lexprops[0].morphology[0].morph';
      let def = [{ _: '', $: { form: 'morphclass' } }];
      let morphProps = _.get<any>(li, lens, def);
      let prop = _.filter<any>(morphProps, (m) => {
        return m.$.form === 'morphclass';
      });
      let mcls = _.get<any>(prop, '[0]._', '');
      return mcls;
    },
    set: (li: any, value: string) => {
      let lexItem = _.cloneDeep(li);
      let lens = 'lexprops[0].morphology[0].morph';
      let morphProps = _.get<any>(lexItem, lens, []);
      if (morphProps.length > 0) {
        let prop = _.filter<any>(morphProps, (m) => {
          return m.$.form === 'morphclass';
        });
        if (prop.length > 0) {
          _.set(prop[0], '_', value);
        } else {
          let def = { _: value, $: { form: 'morphclass' } };
          morphProps.push(def);
        }
      } else {
        let def = [{ _: value, $: { form: 'morphclass' } }];
        _.set(lexItem, lens, def);
      }
      return lexItem;
    }
  },
  morphexceptions: {
    type: 'list',
    get: (li: any) => {
      let lens = 'lexprops[0].morphology[0].morph';
      let def = [{ _: 'M0', $: { form: 'morphclass' } }];
      let morphProps = _.get<any>(li, lens, def);
      let morphExps = _.filter<any>(morphProps, (m) => {
        return m.$.form !== 'morphclass';
      });
      return morphExps;
    }
  },
  stats: { type: 'text', ...simpleAccessor('stats[0].property[0]._'), },
  lang: { type: 'select', options: ['en', 'es'], ...simpleAccessor('$.id', 'en'), },
};

const xmlToEntries = (xmlData: any) => {
  let allEntries = _.chain(xmlData)
    .get<any>('document.lexicon[0].item')
    .flatMap((o: any) => _.chain(o)
      .get<any>('entry')
      .map((p: any) => _.chain(p)
        .get<any>('lang[0]')
        .set('guid[0]', o.$.guid)
        .value())
      .value()
    )
    .value();
  let langReducer = ((result: any, q: any) => {
    let lang = fieldMetaMap.lang.get(q);
    (result[lang] || (result[lang] = [])).push(q);
    return result;
  });
  let langEntries = _.reduce(allEntries, langReducer, {});
  let langs = _.keys(langEntries);
  let selectFields = _.fromPairs(langs.map((lang) => {
    let langOpts = _.fromPairs(_.keys(fieldMetaMap).filter((s) => {
      return fieldMetaMap[s].type === 'select';
    }).map((s) => {
      let entries = _.get<any>(langEntries, lang, 'en');
      let selectOptions = _.uniq(entries.map((q: any) => {
        return fieldMetaMap[s].get(q);
      }));
      return [s, selectOptions];
    }));
    return [lang, langOpts];
  }));
  return ({
    allEntries, selectFields, fieldMetaMap
  });
};

export class LexSetup extends React.Component<any, any> {
  constructor(props: any) {
    super(props);
    this.state = { xmlLoaded: false };
  }
  public componentDidMount() {
    fetch(this.props.fileName)
      .then((response) => response.text())
      .then((xmlString) => {
        XML.parseString(xmlString, (err, xmlData) => {
          this.props.addXmlData(xmlData);
          this.setState({ xmlLoaded: true });
        });
      })
      .catch((e) => {
        console.log('errored :', e);
      });
  }

  render() {
    let dimmer = (
      <Dimmer active={true} inverted={true}>
        <Loader inverted={true}>Loading</Loader>
      </Dimmer>
    );
    let xmlEntries = xmlToEntries(this.props.xmlData);
    let editor = (
      <LexEditor {...xmlEntries} {...this.props} />
    );
    return this.state.xmlLoaded ? editor : dimmer;
  }
}
