/*
 * Copyright 2014-2021 Firestack, all rights reserved.
 */

import { moref, managedObjectProps, managedObjectAlias, managedObjectTypeReverseLookUp } from '../../models/mo.props';

export function ref(obj) {
  if (typeof obj === 'string' && obj.startsWith('$mo:')) {
    const r = parseMoref(obj);
    return { attributes: { type: r.type }, $value: r.id };
  } else if (obj.iid && obj.iid.startsWith('$mo:')) {
    const r = parseMoref(obj);
    return { attributes: { type: r.type }, $value: r.id };
  } else {
    return { attributes: obj.attributes, $value: obj.$value };
  }
}

export function parseMoref(val: moref) {
  const lit = val.split(':');
  let type = lit[1];
  if (managedObjectTypeReverseLookUp[type]) { type = managedObjectTypeReverseLookUp[type]; }
  let id = lit[2];
  return { type, id };
}

export function xsi(type: string, obj: object) {
  const newObj = {
    attributes: { 'xsi:type': type },
    ...obj
  };
  return newObj;
}

export function paramFilter(obj) {
  // console.log(JSON.stringify(obj, null, 4));
  return obj;
}

export const ignoredProps = {
  // declaredAlarmState: true,
};

export function parseValue(obj, propname?: string | number) {
  if (!obj) { return obj; }
  let value = obj;
  let isArray = false;
  let arrayOf = null;
  if (Array.isArray(obj)) {
    if (propname === 'data' || propname === 'standardInquiry') {
      return obj.map(ch => parseInt(ch, 10));
    } else if (propname === 'certificate') {
      return String.fromCharCode.apply(null, obj.map(ch => parseInt(ch, 10)));
    }
    value = [];
    let i = 0;
    for (const entry of obj) { value.push(parseValue(entry, i)); ++i; }
    return value;
  }
  if (obj.attributes) {
    const xsiType = obj.attributes['xsi:type'];
    if (!xsiType) {
      if (obj.attributes.type) {
        const moType = obj.attributes.type;
        let moTypeRoot = managedObjectProps[moType] ? moType : managedObjectAlias[moType];
        if (!moTypeRoot) { moTypeRoot = moType; }
        return `$mo:${moTypeRoot}:${obj.$value}`;
      }
      return obj;
    }
    if (xsiType.startsWith('ArrayOf')) {
      isArray = true;
      arrayOf = xsiType.split('ArrayOf')[1];
      value = [];
      const arr = obj[arrayOf] ? obj[arrayOf] : obj[arrayOf.toLowerCase()] ? obj[arrayOf.toLowerCase()] : null;
      if (arr) {
        let i = 0;
        for (const entry of arr) { value.push(parseValue(entry, i)); ++i; }
      }
    } else {
      if (xsiType.startsWith('xsd:')) { // primitives
        switch (xsiType) {
          case 'xsd:string': value = obj.$value; break;
          case 'xsd:boolean': value = obj.$value.startsWith('t') ? true : false; break;
          case 'xsd:int': value = obj.$value; break;
          case 'xsd:long': value = parseInt(obj.$value, 10); break;
          // tslint:disable-next-line: no-console
          default: console.log(`Unknown property type: ${JSON.stringify(obj.attributes)}`); break;
        }
      } else if (obj.attributes.type) { // mo ref
        const moType = obj.attributes.type;
        let moTypeRoot = managedObjectProps[moType] ? moType : managedObjectAlias[moType];
        if (!moTypeRoot) { moTypeRoot = moType; }
        value = `$mo:${moTypeRoot}:${obj.$value}`;
      } else {
        value = { _type: xsiType };
        if (obj.$value) {
          if (typeof obj.$value === 'string') {
            value = obj.$value;
          } else {
            value = { _type: xsiType, $value: obj.$value };
          }
        } else {
          value = { _type: xsiType };
          for (const innerProp of Object.keys(obj)) {
            if (innerProp === 'attributes' || innerProp === '$value') { continue; }
            value[innerProp] = parseValue(obj[innerProp], innerProp);
          }
        }
      }
    }
  } else if (typeof obj === 'object') {
    if (obj instanceof Date) {
      value = obj.getTime();
    } else {
      value = {};
      for (const innerProp of Object.keys(obj)) {
        value[innerProp] = parseValue(obj[innerProp], innerProp);
      }
    }
  } else if (typeof obj === 'string') {
    if (obj.endsWith('Z')) {
      const lit = obj.split('T');
      if (lit.length === 2 && lit[0].split('-').length === 3 && lit[1].charAt(2) === ':' && lit[1].charAt(5) === ':') {
        // UTC timestamp e.g. 2021-03-05T06:06:55.684633Z
        value = new Date(obj).getTime();
      }
    }
  }
  return value;
}

export async function parseObject(obj) {
  const moType = obj.obj.attributes.type;
  const moTypeRoot = managedObjectProps[moType] ? moType : managedObjectAlias[moType];
  if (!moTypeRoot) {
    // tslint:disable-next-line: no-console
    console.log(`Unknown managed object type ${moType}`);
    return null;
  }
  const newObj = { iid: `$mo:${moTypeRoot}:${obj.obj.$value}` };
  if (obj.propSet) {
    obj.propSet.forEach(prop => {
      if (managedObjectProps[moTypeRoot] && managedObjectProps[moTypeRoot].indexOf(prop.name) === -1) {
        return;
      }
      if (ignoredProps[prop]) { return; }
      newObj[prop.name] = parseValue(prop.val);
    });
  }
  return newObj;
}

export async function parseObjects(objs: any[]) {
  // const t1 = Date.now();
  const list = [];
  for (const obj of objs) {
    const parsed = await parseObject(obj);
    if (parsed) { list.push(parsed); }
  }
  // const t2 = Date.now();
  return list;
}
